[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*.cs,*.ps1]\nindent_style = space\nindent_size = 4\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Custom for Visual Studio\n*.cs     diff=csharp text\n*.sln    merge=union text eol=crlf\n*.csproj merge=union text eol=crlf\n*.vbproj merge=union text eol=crlf\n*.fsproj merge=union text eol=crlf\n*.dbproj merge=union text eol=crlf\n\n# Standard to msysgit\n*.doc\t diff=astextplain\n*.DOC\t diff=astextplain\n*.docx diff=astextplain\n*.DOCX diff=astextplain\n*.dot  diff=astextplain\n*.DOT  diff=astextplain\n*.pdf  diff=astextplain\n*.PDF\t diff=astextplain\n*.rtf\t diff=astextplain\n*.RTF\t diff=astextplain\n\n# Source code\n*.cshtml text\n*.css    text\n*.xml    text\n*.bat    text\n*.ps1    text\n*.nuspec text\n*.js     text\n*.json   text\n*.sql    text\n\n# Documentation\n*.md           text\n*.txt          text\n*.manifest     text\nCOPYING        text\nCOPYING.LESSER text\n\n# Configs\n*.config       text\n.editorconfig  text\n.gitattributes text\n.gitignore     text\n*.yml          text\n*.DotSettings  text"
  },
  {
    "path": ".gitignore",
    "content": "#################\n## IDEA Rider\n#################\n.idea\n.idea.*\n\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n*.bak\n*.swp\n*~.nib\nlocal.properties\n.classpath\n.settings/\n.loadpath\n\n# External tool builders\n.externalToolBuilders/\n\n# Locally stored \"Eclipse launch configurations\"\n*.launch\n\n# CDT-specific\n.cproject\n\n# PDT-specific\n.buildpath\n\n\n#################\n## Visual Studio\n#################\n\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\npackages/\n\n# Build results\n\n[Bb]uild/\n[Dd]ebug/\n[Rr]elease/\nx64/\n[Bb]in/\n[Oo]bj/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n\n# dotnet\nartifacts/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\ncoverage.xml\n\n*_i.c\n*_p.c\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.log\n*.scc\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n*.cachefile\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n*.ncrunch*\n.*crunch*.local.xml\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.Publish.xml\n*.pubxml\n\n# NuGet Packages Directory\n## TODO: If you have NuGet Package Restore enabled, uncomment the next line\n#packages/\n\n# Windows Azure Build Output\ncsx\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Others\nsql/\n*.Cache\nClientBin/\n[Ss]tyle[Cc]op.*\n~$*\n*~\n*.dbmdl\n*.[Pp]ublish.xml\n*.pfx\n*.publishsettings\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file to a newer\n# Visual Studio version. Backup files are not needed, because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\nApp_Data/*.mdf\nApp_Data/*.ldf\n\n#############\n## Windows detritus\n#############\n\n# Windows image file caches\nThumbs.db\nehthumbs.db\n\n# Folder config file\nDesktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Mac crap\n.DS_Store\n\n\n#############\n## Python\n#############\n\n*.py[co]\n\n# Packages\n*.egg\n*.egg-info\ndist/\neggs/\nparts/\nvar/\nsdist/\ndevelop-eggs/\n.installed.cfg\n\n# Installer logs\npip-log.txt\n\n# Unit test / coverage reports\n.coverage\n.tox\n\n#Translations\n*.mo\n\n#Mr Developer\n.mr.developer.cfg\n\n#Sphinx Built Docs\ndocs/_build\n\n#Jekyll site builds\n_site\n*.userprefs\n\ncov-int/\n"
  },
  {
    "path": ".nuget/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"Hangfire.Build\" version=\"0.5.0\" />\n  <package id=\"ILRepack\" version=\"2.0.27\" />\n  <package id=\"psake\" version=\"4.4.1\" />\n  <package id=\"RazorGenerator.MsBuild\" version=\"2.5.0\" />\n</packages>"
  },
  {
    "path": ".nuget/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"Any,Version=v0.0\": {\n      \"Hangfire.Build\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[0.5.0, 0.5.0]\",\n        \"resolved\": \"0.5.0\",\n        \"contentHash\": \"4yRCdMaDr6cyFRmCvpFO8kBMV57KPOofugaHOsjkDEDw+G/BCGWOdrpXfkAeTEtZBPUv2jS0PYmVNK5680KxXQ==\"\n      },\n      \"ILRepack\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.27, 2.0.27]\",\n        \"resolved\": \"2.0.27\",\n        \"contentHash\": \"N5nhVwlgU2ipeonLBVuv1HpsIclJI/5QujuLeo3BW7irD2KnSFXkHa5FmXwzdCkB36D0XGOxNMxh99P7kjgU2A==\"\n      },\n      \"psake\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.4.1, 4.4.1]\",\n        \"resolved\": \"4.4.1\",\n        \"contentHash\": \"Hn5kdGPEoapi+wAAjaGjKEZVnuYp7fUrPK3IivLYG6Bn4adhd8l+KXXPMEmte41RmrLvfV7XGZa9KsSTc0gjDA==\"\n      },\n      \"RazorGenerator.MsBuild\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.5.0, 2.5.0]\",\n        \"resolved\": \"2.5.0\",\n        \"contentHash\": \"yOu2KEjDFvE20b+YK/+Ovf4czUHWH2DlBB9lIKDXdBjcMh3PXrPJTxPzi468XrFvE1dVEiWu+pGNkVFE9+VpMA==\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# File an Issue\n\nIf you have a question rather than an issue, please post it to the [Hangfire Stack \nOverflow tag](https://stackoverflow.com/questions/tagged/hangfire). For non-security related bugs please log a new issue:\n\n1. Search the [issue tracker](https://github.com/HangfireIO/Hangfire/issues) for similar issues.\n2. Specify the **version** of `Hangfire.Core` package in which the bug was occurred.\n3. Specify the **storage** package (e.g. `Hangfire.SqlServer`) you are using and its exact version.\n4. Specify the **configuration** logic for Hangfire.\n5. Specify all the custom job **filters** if any, and post their source code.\n6. Describe the problem and your environment in detail (i.e. what happened and what you expected would happen).\n\nProTip!\n\n* Include screenshots from Dashboard UI, to allow us to see the same \n  problem. You can simply use <kbd>Print Screen</kbd>, then <kbd>Ctrl + V</kbd> directly \n into the comment window on GitHub.\n* Include log messages, written by Hangfire when a problem occurred. Don't forget to tell your logger to dump all the exception details.\n* Include stack trace dump, if your background processing is stuck. You can use \n   [`stdump`](https://github.com/odinserj/stdump) utility to get them either from a minidump file,\n   or from a running process without interrupting it: `stdump w3wp > stacktrace.txt`\n\nHints\n\n* Use [syntax highlighting](https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting) for your C#, SQL, etc. code blocks.\n* Use [fenced code blocks](https://help.github.com/articles/creating-and-highlighting-code-blocks/#fenced-code-blocks) for exception details.\n\n# Reporting security issues \n\nIn order to give the community time to respond and upgrade we strongly urge you report all security issues privately. Please email us at [security@hangfire.io](mailto:security@hangfire.io) with details and we will respond ASAP. Security issues always take precedence over bug fixes and feature work. We can and do mark releases as \"urgent\" if they contain serious security fixes. \n"
  },
  {
    "path": "COPYING",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n"
  },
  {
    "path": "COPYING.LESSER",
    "content": "                   GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\n  This version of the GNU Lesser General Public License incorporates\nthe terms and conditions of version 3 of the GNU General Public\nLicense, supplemented by the additional permissions listed below.\n\n  0. Additional Definitions.\n\n  As used herein, \"this License\" refers to version 3 of the GNU Lesser\nGeneral Public License, and the \"GNU GPL\" refers to version 3 of the GNU\nGeneral Public License.\n\n  \"The Library\" refers to a covered work governed by this License,\nother than an Application or a Combined Work as defined below.\n\n  An \"Application\" is any work that makes use of an interface provided\nby the Library, but which is not otherwise based on the Library.\nDefining a subclass of a class defined by the Library is deemed a mode\nof using an interface provided by the Library.\n\n  A \"Combined Work\" is a work produced by combining or linking an\nApplication with the Library.  The particular version of the Library\nwith which the Combined Work was made is also called the \"Linked\nVersion\".\n\n  The \"Minimal Corresponding Source\" for a Combined Work means the\nCorresponding Source for the Combined Work, excluding any source code\nfor portions of the Combined Work that, considered in isolation, are\nbased on the Application, and not on the Linked Version.\n\n  The \"Corresponding Application Code\" for a Combined Work means the\nobject code and/or source code for the Application, including any data\nand utility programs needed for reproducing the Combined Work from the\nApplication, but excluding the System Libraries of the Combined Work.\n\n  1. Exception to Section 3 of the GNU GPL.\n\n  You may convey a covered work under sections 3 and 4 of this License\nwithout being bound by section 3 of the GNU GPL.\n\n  2. Conveying Modified Versions.\n\n  If you modify a copy of the Library, and, in your modifications, a\nfacility refers to a function or data to be supplied by an Application\nthat uses the facility (other than as an argument passed when the\nfacility is invoked), then you may convey a copy of the modified\nversion:\n\n   a) under this License, provided that you make a good faith effort to\n   ensure that, in the event an Application does not supply the\n   function or data, the facility still operates, and performs\n   whatever part of its purpose remains meaningful, or\n\n   b) under the GNU GPL, with none of the additional permissions of\n   this License applicable to that copy.\n\n  3. Object Code Incorporating Material from Library Header Files.\n\n  The object code form of an Application may incorporate material from\na header file that is part of the Library.  You may convey such object\ncode under terms of your choice, provided that, if the incorporated\nmaterial is not limited to numerical parameters, data structure\nlayouts and accessors, or small macros, inline functions and templates\n(ten or fewer lines in length), you do both of the following:\n\n   a) Give prominent notice with each copy of the object code that the\n   Library is used in it and that the Library and its use are\n   covered by this License.\n\n   b) Accompany the object code with a copy of the GNU GPL and this license\n   document.\n\n  4. Combined Works.\n\n  You may convey a Combined Work under terms of your choice that,\ntaken together, effectively do not restrict modification of the\nportions of the Library contained in the Combined Work and reverse\nengineering for debugging such modifications, if you also do each of\nthe following:\n\n   a) Give prominent notice with each copy of the Combined Work that\n   the Library is used in it and that the Library and its use are\n   covered by this License.\n\n   b) Accompany the Combined Work with a copy of the GNU GPL and this license\n   document.\n\n   c) For a Combined Work that displays copyright notices during\n   execution, include the copyright notice for the Library among\n   these notices, as well as a reference directing the user to the\n   copies of the GNU GPL and this license document.\n\n   d) Do one of the following:\n\n       0) Convey the Minimal Corresponding Source under the terms of this\n       License, and the Corresponding Application Code in a form\n       suitable for, and under terms that permit, the user to\n       recombine or relink the Application with a modified version of\n       the Linked Version to produce a modified Combined Work, in the\n       manner specified by section 6 of the GNU GPL for conveying\n       Corresponding Source.\n\n       1) Use a suitable shared library mechanism for linking with the\n       Library.  A suitable mechanism is one that (a) uses at run time\n       a copy of the Library already present on the user's computer\n       system, and (b) will operate properly with a modified version\n       of the Library that is interface-compatible with the Linked\n       Version.\n\n   e) Provide Installation Information, but only if you would otherwise\n   be required to provide such information under section 6 of the\n   GNU GPL, and only to the extent that such information is\n   necessary to install and execute a modified version of the\n   Combined Work produced by recombining or relinking the\n   Application with a modified version of the Linked Version. (If\n   you use option 4d0, the Installation Information must accompany\n   the Minimal Corresponding Source and Corresponding Application\n   Code. If you use option 4d1, you must provide the Installation\n   Information in the manner specified by section 6 of the GNU GPL\n   for conveying Corresponding Source.)\n\n  5. Combined Libraries.\n\n  You may place library facilities that are a work based on the\nLibrary side by side in a single library together with other library\nfacilities that are not Applications and are not covered by this\nLicense, and convey such a combined library under terms of your\nchoice, if you do both of the following:\n\n   a) Accompany the combined library with a copy of the same work based\n   on the Library, uncombined with any other library facilities,\n   conveyed under the terms of this License.\n\n   b) Give prominent notice with the combined library that part of it\n   is a work based on the Library, and explaining where to find the\n   accompanying uncombined form of the same work.\n\n  6. Revised Versions of the GNU Lesser General Public License.\n\n  The Free Software Foundation may publish revised and/or new versions\nof the GNU Lesser General Public License from time to time. Such new\nversions will be similar in spirit to the present version, but may\ndiffer in detail to address new problems or concerns.\n\n  Each version is given a distinguishing version number. If the\nLibrary as you received it specifies that a certain numbered version\nof the GNU Lesser General Public License \"or any later version\"\napplies to it, you have the option of following the terms and\nconditions either of that published version or of any later version\npublished by the Free Software Foundation. If the Library as you\nreceived it does not specify a version number of the GNU Lesser\nGeneral Public License, you may choose any version of the GNU Lesser\nGeneral Public License ever published by the Free Software Foundation.\n\n  If the Library as you received it specifies that a proxy can decide\nwhether future versions of the GNU Lesser General Public License shall\napply, that proxy's public statement of acceptance of any version is\npermanent authorization for you to choose that version for the\nLibrary.\n"
  },
  {
    "path": "Directory.Build.props",
    "content": "<Project>\n    <PropertyGroup>\n        <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>\n        <DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>\n    </PropertyGroup>\n\n    <PropertyGroup Condition=\"'$(APPVEYOR)' == 'True'\">\n        <RestoreLockedMode>true</RestoreLockedMode>\n    </PropertyGroup>\n</Project>\n"
  },
  {
    "path": "Hangfire.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 15\r\nVisualStudioVersion = 15.0.28307.136\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"ConsoleSample\", \"samples\\ConsoleSample\\ConsoleSample.csproj\", \"{C02BB718-2AE4-434C-8668-C894FF663FCE}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Hangfire.Core\", \"src\\Hangfire.Core\\Hangfire.Core.csproj\", \"{C995EA9E-56EE-4951-8260-D94260A7F4C2}\"\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"samples\", \"samples\", \"{119DA7FA-B94C-4B63-AEC9-428EF834E0D8}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Hangfire.SqlServer\", \"src\\Hangfire.SqlServer\\Hangfire.SqlServer.csproj\", \"{A523C0E3-097D-4869-977F-15A717EA3E83}\"\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"tests\", \"tests\", \"{766BE831-F758-46BC-AFD3-BBEEFE0F686F}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\ttests\\Directory.Build.props = tests\\Directory.Build.props\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"build\", \"build\", \"{15E186DF-8918-44F1-9285-9756EDAECA5D}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\tappveyor.yml = appveyor.yml\r\n\t\tpsake-project.ps1 = psake-project.ps1\r\n\t\tsrc\\SharedAssemblyInfo.cs = src\\SharedAssemblyInfo.cs\r\n\t\tsrc\\Directory.Build.props = src\\Directory.Build.props\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"nuspecs\", \"nuspecs\", \"{5E687025-A525-4534-8869-CB685C7B11C4}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\tnuspecs\\Hangfire.AspNetCore.nuspec = nuspecs\\Hangfire.AspNetCore.nuspec\r\n\t\tnuspecs\\Hangfire.Core.nuspec = nuspecs\\Hangfire.Core.nuspec\r\n\t\tnuspecs\\Hangfire.NetCore.nuspec = nuspecs\\Hangfire.NetCore.nuspec\r\n\t\tnuspecs\\Hangfire.nuspec = nuspecs\\Hangfire.nuspec\r\n\t\tnuspecs\\Hangfire.SqlServer.MSMQ.nuspec = nuspecs\\Hangfire.SqlServer.MSMQ.nuspec\r\n\t\tnuspecs\\Hangfire.SqlServer.nuspec = nuspecs\\Hangfire.SqlServer.nuspec\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"content\", \"content\", \"{37E2BF00-C473-48A5-B2A7-5A3DE0910FCF}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\tcontent\\readme.txt = content\\readme.txt\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Hangfire.Core.Tests\", \"tests\\Hangfire.Core.Tests\\Hangfire.Core.Tests.csproj\", \"{E13C3543-39A3-475C-BB43-2E311E634843}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Hangfire.SqlServer.Tests\", \"tests\\Hangfire.SqlServer.Tests\\Hangfire.SqlServer.Tests.csproj\", \"{6DFFA275-C483-4501-823A-741AC9EC0846}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Hangfire.SqlServer.Msmq\", \"src\\Hangfire.SqlServer.Msmq\\Hangfire.SqlServer.Msmq.csproj\", \"{762BE479-0AEC-47E0-8F9C-34FA54641749}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Hangfire.SqlServer.Msmq.Tests\", \"tests\\Hangfire.SqlServer.Msmq.Tests\\Hangfire.SqlServer.Msmq.Tests.csproj\", \"{DBC6BC12-06AD-4597-9E0F-B77BE754FA2C}\"\r\nEndProject\r\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \".nuget\", \".nuget\", \"{F710C9CF-69F9-4373-9B74-ABB185BF985B}\"\r\n\tProjectSection(SolutionItems) = preProject\r\n\t\t.nuget\\packages.config = .nuget\\packages.config\r\n\tEndProjectSection\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Hangfire.AspNetCore\", \"src\\Hangfire.AspNetCore\\Hangfire.AspNetCore.csproj\", \"{73AFEBE0-9B11-44F6-A69C-8A51538CF18E}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"NetCoreSample\", \"samples\\NetCoreSample\\NetCoreSample.csproj\", \"{FA751692-20C8-4986-8164-D21F505B2172}\"\r\nEndProject\r\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Hangfire.NetCore\", \"src\\Hangfire.NetCore\\Hangfire.NetCore.csproj\", \"{AA8E3A67-8731-423A-9B69-EE44EC6B8E1A}\"\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{C02BB718-2AE4-434C-8668-C894FF663FCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{C02BB718-2AE4-434C-8668-C894FF663FCE}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{C02BB718-2AE4-434C-8668-C894FF663FCE}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{C02BB718-2AE4-434C-8668-C894FF663FCE}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{C995EA9E-56EE-4951-8260-D94260A7F4C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{C995EA9E-56EE-4951-8260-D94260A7F4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{C995EA9E-56EE-4951-8260-D94260A7F4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{C995EA9E-56EE-4951-8260-D94260A7F4C2}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{A523C0E3-097D-4869-977F-15A717EA3E83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{A523C0E3-097D-4869-977F-15A717EA3E83}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{A523C0E3-097D-4869-977F-15A717EA3E83}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{A523C0E3-097D-4869-977F-15A717EA3E83}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{E13C3543-39A3-475C-BB43-2E311E634843}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{E13C3543-39A3-475C-BB43-2E311E634843}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{E13C3543-39A3-475C-BB43-2E311E634843}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{E13C3543-39A3-475C-BB43-2E311E634843}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{6DFFA275-C483-4501-823A-741AC9EC0846}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{6DFFA275-C483-4501-823A-741AC9EC0846}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{6DFFA275-C483-4501-823A-741AC9EC0846}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{6DFFA275-C483-4501-823A-741AC9EC0846}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{762BE479-0AEC-47E0-8F9C-34FA54641749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{762BE479-0AEC-47E0-8F9C-34FA54641749}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{762BE479-0AEC-47E0-8F9C-34FA54641749}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{762BE479-0AEC-47E0-8F9C-34FA54641749}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{DBC6BC12-06AD-4597-9E0F-B77BE754FA2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{DBC6BC12-06AD-4597-9E0F-B77BE754FA2C}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{DBC6BC12-06AD-4597-9E0F-B77BE754FA2C}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{DBC6BC12-06AD-4597-9E0F-B77BE754FA2C}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{73AFEBE0-9B11-44F6-A69C-8A51538CF18E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{73AFEBE0-9B11-44F6-A69C-8A51538CF18E}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{73AFEBE0-9B11-44F6-A69C-8A51538CF18E}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{73AFEBE0-9B11-44F6-A69C-8A51538CF18E}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{FA751692-20C8-4986-8164-D21F505B2172}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{FA751692-20C8-4986-8164-D21F505B2172}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{FA751692-20C8-4986-8164-D21F505B2172}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{FA751692-20C8-4986-8164-D21F505B2172}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{AA8E3A67-8731-423A-9B69-EE44EC6B8E1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{AA8E3A67-8731-423A-9B69-EE44EC6B8E1A}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{AA8E3A67-8731-423A-9B69-EE44EC6B8E1A}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{AA8E3A67-8731-423A-9B69-EE44EC6B8E1A}.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{C02BB718-2AE4-434C-8668-C894FF663FCE} = {119DA7FA-B94C-4B63-AEC9-428EF834E0D8}\r\n\t\t{E13C3543-39A3-475C-BB43-2E311E634843} = {766BE831-F758-46BC-AFD3-BBEEFE0F686F}\r\n\t\t{6DFFA275-C483-4501-823A-741AC9EC0846} = {766BE831-F758-46BC-AFD3-BBEEFE0F686F}\r\n\t\t{DBC6BC12-06AD-4597-9E0F-B77BE754FA2C} = {766BE831-F758-46BC-AFD3-BBEEFE0F686F}\r\n\t\t{FA751692-20C8-4986-8164-D21F505B2172} = {119DA7FA-B94C-4B63-AEC9-428EF834E0D8}\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {7597454C-C272-4016-87A2-CF1196347355}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "Hangfire.sln.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Hangfire_002EAnnotations/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Hangfire_002ERedis_002EAnnotations/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Hangfire_002ESqlServer_002EAnnotations/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue\">True</s:Boolean>\n\t<s:String x:Key=\"/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue\">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters&gt;&lt;Filter ModuleMask=\"Hangfire.Core.Tests\" ModuleVersionMask=\"*\" ClassMask=\"*\" FunctionMask=\"*\" IsEnabled=\"True\" /&gt;&lt;Filter ModuleMask=\"Hangfire.SqlServer.Tests\" ModuleVersionMask=\"*\" ClassMask=\"*\" FunctionMask=\"*\" IsEnabled=\"True\" /&gt;&lt;/ExcludeFilters&gt;&lt;/data&gt;</s:String>\n\t<s:String x:Key=\"/Default/FilterSettingsManager/AttributeFilterXml/@EntryValue\">&lt;data /&gt;</s:String>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Hangfire/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Odinokov/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Sergey/@EntryIndexedValue\">True</s:Boolean>\n\t</wpf:ResourceDictionary>\n"
  },
  {
    "path": "LICENSE.md",
    "content": "License\n========\n\nCopyright © 2013-2026 Hangfire OÜ.\n\nHangfire software is an open-source software that is multi-licensed under the terms of the licenses listed in this file. Recipients may choose the terms under which they are want to use or distribute the software, when all the preconditions of a chosen license are satisfied.\n\nLGPL v3 License\n---------------\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.\n\nPlease see COPYING.LESSER and COPYING files for details.\n\nCommercial License\n------------------\n\nSubject to the purchase of a corresponding subscription (please see https://www.hangfire.io/pricing/), you may distribute Hangfire under the terms of commercial license, that allows you to distribute private forks and modifications. Please see LICENSE_STANDARD and LICENSE_ROYALTYFREE files for details.\n"
  },
  {
    "path": "LICENSE_ROYALTYFREE",
    "content": "Royalty-free End-user License Agreement\n=======================================\n\nTHIS LICENSE AGREEMENT DESCRIBES YOUR RIGHTS WITH RESPECT TO THE SOFTWARE AND ITS COMPONENTS.\n\n1. OWNERSHIP, LICENSE GRANT\n\nThis is a license agreement and not an agreement for sale. We reserve ownership of all intellectual property rights inherent in or relating to the Software, which include, but are not limited to, all copyright, patent rights, all rights in relation to registered and unregistered trademarks (including service marks), confidential information (including trade secrets and know-how) and all rights other than those expressly granted by this License Agreement.\n\nSubject to the payment of the fee required and subject to the terms and conditions of this License Agreement, We grant to You a revocable, non- transferable and non-exclusive license (i) for Designated User(s) (as defined below) within Your organization to install and use the Software on any workstations used exclusively by such Designated User and (ii) for You to install and use the Software in connection with unlimited domains and sub-domains on unlimited servers, solely in connection with distribution of the Software in accordance with sections 3 and 4 below. This license is not sublicensable except as explicitly set forth herein. \"Designated User(s)\" shall mean Your employee(s) acting within the scope of their employment or Your consultant(s) or contractor(s) acting within the scope of the services they provide for You or on Your behalf for whom You have purchased a license to use the Software.\n\n2. PERMITTED USES, SOURCE CODE, MODIFICATIONS\n\nWe provide You with source code so that You can create Modifications of the original Software, where Modification means: a) any addition to or deletion from the contents of a file included in the original Software or previous Modifications created by You, or b) any new file that contains any part of the original Software or previous Modifications. While You retain all rights to any original work authored by You as part of the Modifications, We continue to own all copyright and other intellectual property rights in the Software.\n\n3. DISTRIBUTION\n\nYou may distribute the Software in any applications, frameworks, or elements (collectively referred to as an \"Application\" or \"Applications\") that you develop using the Software in accordance with this License Agreement, provided that such distribution does not violate the restrictions set forth in section 4 of this License Agreement. You must not remove, obscure or interfere with any copyright, acknowledgment, attribution, trademark, warning or disclaimer statement affixed to, incorporated in or otherwise applied in connection with the Software.\n\nYou are required to ensure that the Software is not reused by or with any applications other than those with which You distribute it as permitted herein. For example, if You install the Software on a customer's server, that customer is not permitted to use the Software independently of Your application, and must be informed as such.\n\nYou will not owe Us any royalties for Your distribution of the Software in accordance with this License Agreement.\n\n4. PROHIBITED USES\n\nYou may not, without Our prior written consent, redistribute the Software or Modifications other than by including the Software or a portion thereof within Your own product, which must have substantially different functionality than the Software or Modifications and must not allow any third party to use the Software or Modifications, or any portions thereof, for software development or application development purposes. You are explicitly not allowed to redistribute the Software or Modifications as part of any product that can be described as a development toolkit or library, an application builder, a website builder or any product that is intended for use by software, application, or website developers or designers. You may not change or remove the copyright notice from any of the files included in the Software or Modifications.\n\nYou may redistribute the Software as part of Your own products.\n\nUNDER NO CIRCUMSTANCES MAY YOU USE THE SOFTWARE FOR A PRODUCT THAT IS INTENDED FOR SOFTWARE OR APPLICATION DEVELOPMENT PURPOSES.\n\n5. TERMINATION\n\nThis License Agreement and Your right to use the Software and Modifications will terminate immediately without notice if You fail to comply with the terms and conditions of this License Agreement. Upon termination, You agree to immediately cease using and destroy the Software or Modifications, including all accompanying documents. The provisions of sections 4, 5, 6, 7, 8, 9, 10 and 11 will survive any termination of this License Agreement.\n\n6. DISCLAIMER OF WARRANTIES\n\nTO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, WE AND OUR SUPPLIERS DISCLAIM ALL WARRANTIES AND CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, WITH REGARD TO THE SOFTWARE. WE DO NOT GUARANTEE THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, AND YOU ACKNOWLEDGE THAT IT IS NOT TECHNICALLY PRACTICABLE FOR US TO DO SO.\n\n7. LIMITATION OF LIABILITIES\n\nTO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL WE OR OUR SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION OR ANY OTHER PECUNIARY LAW) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY CASE, OUR ENTIRE LIABILITY UNDER ANY PROVISION OF THIS LICENSE AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR THE SOFTWARE.\n\n8. VERIFICATION\n\nWe or a certified auditor acting on Our behalf, may, upon its reasonable request and at its expense, audit You with respect to the use of the Software. Such audit may be conducted by mail, electronic means or through an in-person visit to Your place of business. Any such in-person audit shall be conducted during regular business hours at Your facilities and shall not unreasonably interfere with Your business activities. We shall not remove, copy, or redistribute any electronic material during the course of an audit. If an audit reveals that You are using the Software in a way that is in material violation of the terms of the License Agreement, then You shall pay Our reasonable costs of conducting the audit. In the case of a material violation, You agree to pay Us any amounts owing that are attributable to the unauthorized use. In the alternative, We reserve the right, at Our sole option, to terminate the licenses for the Software.\n\n9. THIRD PARTY SOFTWARE\n\nThe Software may contain third party open source software which requires notices and/or additional terms and conditions. Such required third party software notices and/or additional terms and conditions are located in the NOTICES file accompanying the Software distribution (also available at https://www.hangfire.io/licensing/third-party.html), and are made a part of and incorporated by reference into this Agreement. By accepting this Agreement, you are also accepting the additional terms and conditions, if any, set forth therein.\n\n10. PAYMENT AND TAXES\n\nIf credit has been extended to You by Us, all payments under this License Agreement are due within thirty (30) days of the date We mail an invoice to You. If We have not extended credit to You, You shall be required to make payment concurrent with the delivery of the Software by Us. All amounts payable are gross amounts but exclusive of any value added tax, use tax, sales tax or similar tax. You shall be entitled to withhold from payments any applicable withholding taxes and comply with all applicable tax and employment legislation. Each party shall pay all taxes (including, but not limited to, taxes based upon its income) or levies imposed on it under applicable laws, regulations and tax treaties as a result of this Agreement and any payments made hereunder (including those required to be withheld or deducted from payments). Each party shall furnish evidence of such paid taxes as is sufficient to enable the other party to obtain any credits available to it, including original withholding tax certificates.\n\n11. MISCELLANEOUS\n\nThe license granted herein applies only to the version of the Software available when purchased in connection with the terms of this License Agreement. Any previous or subsequent license granted to You for use of the Software shall be governed by the terms and conditions of the agreement entered in connection with purchase of that version of the Software. You agree that you will comply with all applicable laws and regulations with respect to the Software, including without limitation all export and re-export control laws and regulations.\n\nWhile redistributing the Software or Modifications thereof, You may choose to offer acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License Agreement. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on Our behalf. You agree to indemnify, defend, and hold Us harmless from and against any liability incurred by, or claims asserted against, Us (i) by reason of Your accepting any such support, warranty, indemnity or additional liability; or (ii) arising out of the use, reproduction or distribution of Your Application, except to the extent such claim is solely based on the inclusion of the Software therein.\n\nYou agree to be identified as a customer of ours and You agree that We may refer to You by name, trade name and trademark, if applicable, and may briefly describe Your business in our marketing materials and web site.\n\nYou may not assign this License Agreement without Our prior written consent, which will not be unreasonably withheld. This License Agreement will inure to the benefit of Our successors and assigns.\n\nYou acknowledge that this License Agreement is complete and is the exclusive representation of our agreement. No oral or written information given by Us or on our behalf shall create a warranty or collateral contract, or in any way increase the scope of this License Agreement in any way, and You may not rely on any such oral or written information. No term or condition contained in any purchase order shall apply unless expressly accepted by Us in writing.\n\nThere are no implied licenses or other implied rights granted under this License Agreement, and all rights, save for those expressly granted hereunder, shall remain with Us and our licensors. In addition, no licenses or immunities are granted to the combination of the Software and/or Modifications, as applicable, with any other software or hardware not delivered by Us to You under this License Agreement.\n\nIf any provision in this License Agreement shall be determined to be invalid, such provision shall be deemed omitted; the remainder of this License Agreement shall continue in full force and effect. If any remedy provided is determined to have failed for its essential purpose, all limitations of liability and exclusions of damages set forth in this License Agreement shall remain in effect.\n\nThis License Agreement may be modified only by a written instrument signed by an authorized representative of each party.\n\nThis License Agreement is governed by the law of the State of Oregon, United States (notwithstanding conflicts of laws provisions), and all parties irrevocably submit to the jurisdiction of the courts of the State of Oregon and further agree to commence any litigation which may arise hereunder in the state or federal courts located in the judicial district of Multnomah County, Oregon, US.\n\nIf the Software or any related documentation is licensed to the U.S. government or any agency thereof, it will be deemed to be \"commercial computer software\" or \"commercial computer software documentation\", pursuant to DFAR Section 227.7202 and FAR Section 12.212. Any use of the Software or related documentation by the U.S. government will be governed solely by the terms of this License Agreement.\n"
  },
  {
    "path": "LICENSE_STANDARD",
    "content": "Standard End-user License Agreement\n===================================\n\nTHIS LICENSE AGREEMENT DESCRIBES YOUR RIGHTS WITH RESPECT TO THE SOFTWARE AND ITS COMPONENTS.\n\n1. OWNERSHIP, LICENSE GRANT\n\nThis is a license agreement and not an agreement for sale. We reserve ownership of all intellectual property rights inherent in or relating to the Software, which include, but are not limited to, all copyright, patent rights, all rights in relation to registered and unregistered trademarks (including service marks), confidential information (including trade secrets and know-how) and all rights other than those expressly granted by this License Agreement.\n\nSubject to the payment of the fee required and subject to the terms and conditions of this License Agreement, We grant to You a revocable, non- transferable and non-exclusive license (i) for Designated User(s) (as defined below) within Your organization to install and use the Software on any workstations used exclusively by such Designated User and (ii) for You to install and use the Software in connection with unlimited domains and sub-domains on unlimited servers, solely in connection with distribution of the Software in accordance with sections 3 and 4 below. This license is not sublicensable except as explicitly set forth herein. \"Designated User(s)\" shall mean Your employee(s) acting within the scope of their employment or Your consultant(s) or contractor(s) acting within the scope of the services they provide for You or on Your behalf for whom You have purchased a license to use the Software.\n\n2. PERMITTED USES, SOURCE CODE, MODIFICATIONS\n\nWe provide You with source code so that You can create Modifications of the original Software, where Modification means: a) any addition to or deletion from the contents of a file included in the original Software or previous Modifications created by You, or b) any new file that contains any part of the original Software or previous Modifications. While You retain all rights to any original work authored by You as part of the Modifications, We continue to own all copyright and other intellectual property rights in the Software.\n\n3. DISTRIBUTION\n\nYou may distribute the Software in any applications, frameworks, or elements (collectively referred to as an \"Application\" or \"Applications\") that you develop using the Software in accordance with this License Agreement, provided that such distribution does not violate the restrictions set forth in section 4 of this License Agreement. You must not remove, obscure or interfere with any copyright, acknowledgment, attribution, trademark, warning or disclaimer statement affixed to, incorporated in or otherwise applied in connection with the Software.\n\nYou are required to ensure that the Software is not reused by or with any applications other than those with which You distribute it as permitted herein. For example, if You install the Software on a customer's server, that customer is not permitted to use the Software independently of Your application, and must be informed as such.\n\nYou will not owe Us any royalties for Your distribution of the Software in accordance with this License Agreement.\n\n4. PROHIBITED USES\n\nYou may not, without Our prior written consent, redistribute the Software or Modifications other than by including the Software or a portion thereof within Your own product, which must have substantially different functionality than the Software or Modifications and must not allow any third party to use the Software or Modifications, or any portions thereof, for software development or application development purposes. You are explicitly not allowed to redistribute the Software or Modifications as part of any product that can be described as a development toolkit or library, an application builder, a website builder or any product that is intended for use by software, application, or website developers or designers. You may not change or remove the copyright notice from any of the files included in the Software or Modifications.\n\nYou may not redistribute the Software as part of a product, \"appliance\" or \"virtual server\". You may not redistribute the Software on any server which is not directly under Your control.\n\nUNDER NO CIRCUMSTANCES MAY YOU USE THE SOFTWARE FOR A PRODUCT THAT IS INTENDED FOR SOFTWARE OR APPLICATION DEVELOPMENT PURPOSES.\n\n5. TERMINATION\n\nThis License Agreement and Your right to use the Software and Modifications will terminate immediately without notice if You fail to comply with the terms and conditions of this License Agreement. Upon termination, You agree to immediately cease using and destroy the Software or Modifications, including all accompanying documents. The provisions of sections 4, 5, 6, 7, 8, 9, 10 and 11 will survive any termination of this License Agreement.\n\n6. DISCLAIMER OF WARRANTIES\n\nTO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, WE AND OUR SUPPLIERS DISCLAIM ALL WARRANTIES AND CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT, WITH REGARD TO THE SOFTWARE. WE DO NOT GUARANTEE THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, AND YOU ACKNOWLEDGE THAT IT IS NOT TECHNICALLY PRACTICABLE FOR US TO DO SO.\n\n7. LIMITATION OF LIABILITIES\n\nTO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL WE OR OUR SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION OR ANY OTHER PECUNIARY LAW) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY CASE, OUR ENTIRE LIABILITY UNDER ANY PROVISION OF THIS LICENSE AGREEMENT SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU FOR THE SOFTWARE.\n\n8. VERIFICATION\n\nWe or a certified auditor acting on Our behalf, may, upon its reasonable request and at its expense, audit You with respect to the use of the Software. Such audit may be conducted by mail, electronic means or through an in-person visit to Your place of business. Any such in-person audit shall be conducted during regular business hours at Your facilities and shall not unreasonably interfere with Your business activities. We shall not remove, copy, or redistribute any electronic material during the course of an audit. If an audit reveals that You are using the Software in a way that is in material violation of the terms of the License Agreement, then You shall pay Our reasonable costs of conducting the audit. In the case of a material violation, You agree to pay Us any amounts owing that are attributable to the unauthorized use. In the alternative, We reserve the right, at Our sole option, to terminate the licenses for the Software.\n\n9. THIRD PARTY SOFTWARE\n\nThe Software may contain third party open-source software which requires notices and/or additional terms and conditions. Such required third party software notices and/or additional terms and conditions are located in the NOTICES file accompanying the Software distribution (also available at https://www.hangfire.io/licensing/third-party.html), and are made a part of and incorporated by reference into this Agreement. By accepting this Agreement, you are also accepting the additional terms and conditions, if any, set forth therein.\n\n10. PAYMENT AND TAXES\n\nIf credit has been extended to You by Us, all payments under this License Agreement are due within thirty (30) days of the date We mail an invoice to You. If We have not extended credit to You, You shall be required to make payment concurrent with the delivery of the Software by Us. All amounts payable are gross amounts but exclusive of any value added tax, use tax, sales tax or similar tax. You shall be entitled to withhold from payments any applicable withholding taxes and comply with all applicable tax and employment legislation. Each party shall pay all taxes (including, but not limited to, taxes based upon its income) or levies imposed on it under applicable laws, regulations and tax treaties as a result of this Agreement and any payments made hereunder (including those required to be withheld or deducted from payments). Each party shall furnish evidence of such paid taxes as is sufficient to enable the other party to obtain any credits available to it, including original withholding tax certificates.\n\n11. MISCELLANEOUS\n\nThe license granted herein applies only to the version of the Software available when purchased in connection with the terms of this License Agreement. Any previous or subsequent license granted to You for use of the Software shall be governed by the terms and conditions of the agreement entered in connection with purchase of that version of the Software. You agree that you will comply with all applicable laws and regulations with respect to the Software, including without limitation all export and re-export control laws and regulations.\n\nWhile redistributing the Software or Modifications thereof, You may choose to offer acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License Agreement. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on Our behalf. You agree to indemnify, defend, and hold Us harmless from and against any liability incurred by, or claims asserted against, Us (i) by reason of Your accepting any such support, warranty, indemnity or additional liability; or (ii) arising out of the use, reproduction or distribution of Your Application, except to the extent such claim is solely based on the inclusion of the Software therein.\n\nYou agree to be identified as a customer of ours and You agree that We may refer to You by name, trade name and trademark, if applicable, and may briefly describe Your business in our marketing materials and web site.\n\nYou may not assign this License Agreement without Our prior written consent, which will not be unreasonably withheld. This License Agreement will inure to the benefit of Our successors and assigns.\n\nYou acknowledge that this License Agreement is complete and is the exclusive representation of our agreement. No oral or written information given by Us or on our behalf shall create a warranty or collateral contract, or in any way increase the scope of this License Agreement in any way, and You may not rely on any such oral or written information. No term or condition contained in any purchase order shall apply unless expressly accepted by Us in writing.\n\nThere are no implied licenses or other implied rights granted under this License Agreement, and all rights, save for those expressly granted hereunder, shall remain with Us and our licensors. In addition, no licenses or immunities are granted to the combination of the Software and/or Modifications, as applicable, with any other software or hardware not delivered by Us to You under this License Agreement.\n\nIf any provision in this License Agreement shall be determined to be invalid, such provision shall be deemed omitted; the remainder of this License Agreement shall continue in full force and effect. If any remedy provided is determined to have failed for its essential purpose, all limitations of liability and exclusions of damages set forth in this License Agreement shall remain in effect.\n\nThis License Agreement may be modified only by a written instrument signed by an authorized representative of each party.\n\nThis License Agreement is governed by the law of the State of Oregon, United States (notwithstanding conflicts of laws provisions), and all parties irrevocably submit to the jurisdiction of the courts of the State of Oregon and further agree to commence any litigation which may arise hereunder in the state or federal courts located in the judicial district of Multnomah County, Oregon, US.\n\nIf the Software or any related documentation is licensed to the U.S. government or any agency thereof, it will be deemed to be \"commercial computer software\" or \"commercial computer software documentation\", pursuant to DFAR Section 227.7202 and FAR Section 12.212. Any use of the Software or related documentation by the U.S. government will be governed solely by the terms of this License Agreement.\n"
  },
  {
    "path": "NOTICES",
    "content": "Third Party Software Notices\n============================\n\nHangfire products use software provided by third parties, including open source software. The following copyright statements and licenses apply to various  components that are distributed with various Hangfire products. The Hangfire product that includes this file does not necessarily use all of the third party software components referred to below. \n\nLicensee must fully agree and comply with these license terms or must not use these components. The third party license terms apply only to the respective software to which the license pertains, and the third party license terms do not apply to the Hangfire software. \n\nIn the event that we accidentally failed to list a required notice, please bring it to our attention by sending an email to <support@hangfire.io>.\n\n### [LibLog](https://github.com/damianh/LibLog) (MIT License)\n\nCopyright (C) 2011-2017 Damian Hickey. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n### [NCrontab](https://github.com/atifaziz/NCrontab) (Apache License 2.0)\n\nCopyright (c) 2008 Atif Aziz. All rights reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n### Microsoft.Owin (Apache License 2.0)\n\nPart of Katana Project (http://katanaproject.codeplex.com/)\n\nCopyright (c) Microsoft Open Technologies, Inc. All rights reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n### [Cron Expression Descriptor](https://github.com/bradymholt/cron-expression-descriptor) (MIT License)\n\nCopyright (c) 2013 Brady Holt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software \nand associated documentation files (the \"Software\"), to deal in the Software without restriction, \nincluding without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, \nand/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, \nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial \nportions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT \nNOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. \nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE \nOR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n### [Stack Trace Parser](https://github.com/atifaziz/StackTraceParser) (Apache License 2.0)\n\nCopyright (c) 2011 Atif Aziz. All rights reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n### [Stack Trace Formatter](https://github.com/atifaziz/StackTraceFormatter) (Apache License 2.0)\n\nCopyright (c) 2011 Atif Aziz. All rights reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n### MoreLINQ - Extensions to LINQ to Objects (Apache License 2.0)\n\nCopyright (c) 2008 Jonathan Skeet. All rights reserved.\n \nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n \nhttp://www.apache.org/licenses/LICENSE-2.0\n \nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n### [ExpressionUtil from ASP.NET MVC](https://github.com/aspnet/AspNetWebStack/tree/v3.0/src/Microsoft.Web.Mvc/ExpressionUtil) (Apache License 2.0)\n\nCopyright (c) Microsoft Open Technologies, Inc. All rights reserved. \n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you\nmay not use this file except in compliance with the License. You may\nobtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\nimplied. See the License for the specific language governing permissions\nand limitations under the License.\n\n### [Dapper](https://github.com/StackExchange/Dapper) (Apache License 2.0)\n\nCopyright (c) 2017 Stack Exchange, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n### [Bootstrap](https://github.com/twbs/bootstrap) (MIT License)\n\nCopyright 2011-2019 Twitter, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n### [Moment.js](https://github.com/moment/moment) (MIT License)\n\nCopyright (c) JS Foundation and other contributors\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\n### [jQuery](https://github.com/jquery/jquery) (MIT License)\n\nCopyright OpenJS Foundation and other contributors, https://openjsf.org/\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n### MSMQ API Extensions (MIT License)\n\nCopyright (c) 2014 Philip Hoppe\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n### [Chart.js](https://www.chartjs.org/) (MIT License)\n\nThe MIT License (MIT)\n\nCopyright (c) 2018 Chart.js Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n### [chartjs-plugin-streaming](https://github.com/nagix/chartjs-plugin-streaming) (MIT License)\n\nCopyright (c) 2021-2019 Akihiko Kusanagi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "NuGet.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <config>\n    <add key=\"signatureValidationMode\" value=\"require\" />\n    <add key=\"globalPackagesFolder\" value=\"%USERPROFILE%/.nuget/TrustedPackages\" />\n  </config>\n\n  <packageSources>\n    <clear/>\n    <add key=\"nuget.org\" value=\"https://api.nuget.org/v3/index.json\" />\n  </packageSources>\n\n  <trustedSigners> \n    <repository name=\"nuget.org\" serviceIndex=\"https://api.nuget.org/v3/index.json\">\n      <owners>Microsoft;aspnet;dotnetframework;HangfireIO;xunit;jamesnk;kzu;castleproject;psake;ILRepack;davidebbo;StackExchange;Dapper;brady.holt;dwhelan;raboof;damianh;</owners>\n      <certificate fingerprint=\"5a2901d6ada3d18260b9c6dfe2133c95d74b9eef6ae0e5dc334c8454d1477df4\" hashAlgorithm=\"SHA256\" allowUntrustedRoot=\"false\" />\n      <certificate fingerprint=\"0e5f38f57dc1bcc806d8494f4f90fbcedd988b46760709cbeec6f4219aa6157d\" hashAlgorithm=\"SHA256\" allowUntrustedRoot=\"false\" />\n      <certificate fingerprint=\"1f4b311d9acc115c8dc8018b5a49e00fce6da8e2855f9f014ca6f34570bc482d\" hashAlgorithm=\"SHA256\" allowUntrustedRoot=\"false\" /> \n    </repository>\n  </trustedSigners>\n\n</configuration>"
  },
  {
    "path": "README.md",
    "content": "Hangfire \n=========\n\n[![Official Site](https://img.shields.io/badge/site-hangfire.io-blue.svg)](https://www.hangfire.io) [![Latest version](https://img.shields.io/nuget/v/Hangfire.svg?label=release)](https://www.nuget.org/packages?q=hangfire) [![Downloads](https://img.shields.io/nuget/dt/Hangfire.Core.svg)](https://www.nuget.org/packages/Hangfire.Core/) [![License LGPLv3](https://img.shields.io/badge/license-LGPLv3-green.svg)](https://www.gnu.org/licenses/lgpl-3.0.html) [![Coverity Scan](https://scan.coverity.com/projects/4423/badge.svg?flat=1)](https://scan.coverity.com/projects/hangfireio-hangfire)\n\n## Build Status\n\n&nbsp; | `main` | `dev`\n--- | --- | --- \n**AppVeyor** | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/70m632jkycqpnsp9/branch/main?svg=true)](https://ci.appveyor.com/project/HangfireIO/hangfire-525)  | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/70m632jkycqpnsp9/branch/dev?svg=true)](https://ci.appveyor.com/project/HangfireIO/hangfire-525) \n\n## Overview\n\nIncredibly easy way to perform **fire-and-forget**, **delayed** and **recurring jobs** in **.NET applications**. CPU and I/O intensive, long-running and short-running jobs are supported. No Windows Service / Task Scheduler required. Backed by Redis, SQL Server, SQL Azure and MSMQ.\n\nHangfire provides a unified programming model to handle background tasks in a **reliable way** and run them on shared hosting, dedicated hosting or in cloud. You can start with a simple setup and grow computational power for background jobs with time for these scenarios:\n\n- mass notifications/newsletters\n- batch import from xml, csv or json\n- creation of archives\n- firing off web hooks\n- deleting users\n- building different graphs\n- image/video processing\n- purging temporary files\n- recurring automated reports\n- database maintenance\n- *…and so on*\n\nHangfire is a .NET alternative to [Resque](https://github.com/resque/resque), [Sidekiq](https://sidekiq.org), [delayed_job](https://github.com/collectiveidea/delayed_job), [Celery](https://www.celeryproject.org).\n\n![Hangfire Dashboard](https://www.hangfire.io/img/ui/dashboard-sm.png)\n\nInstallation\n-------------\n\nHangfire is available as a NuGet package. You can install it using the NuGet Package Console window:\n\n```\nPM> Install-Package Hangfire\n```\n\nAfter installation, update your existing [OWIN Startup](https://www.asp.net/aspnet/overview/owin-and-katana/owin-startup-class-detection) file with the following lines of code. If you do not have this class in your project or don't know what is it, please read the [Quick start](https://docs.hangfire.io/en/latest/getting-started/index.html) guide to learn about how to install Hangfire.\n\n```csharp\npublic void Configuration(IAppBuilder app)\n{\n    GlobalConfiguration.Configuration.UseSqlServerStorage(\"<connection string or its name>\");\n    \n    app.UseHangfireServer();\n    app.UseHangfireDashboard();\n}\n```\n\nUsage\n------\n\nThis is an incomplete list of features; to see all of them, check the [official site](https://www.hangfire.io) and the [documentation](https://docs.hangfire.io).\n\n[**Fire-and-forget tasks**](https://docs.hangfire.io/en/latest/background-methods/calling-methods-in-background.html)\n\nDedicated worker pool threads execute queued background jobs as soon as possible, shortening your request's processing time.\n\n```csharp\nBackgroundJob.Enqueue(() => Console.WriteLine(\"Simple!\"));\n```\n\n[**Delayed tasks**](https://docs.hangfire.io/en/latest/background-methods/calling-methods-with-delay.html)\n\nScheduled background jobs are executed only after a given amount of time.\n\n```csharp\nBackgroundJob.Schedule(() => Console.WriteLine(\"Reliable!\"), TimeSpan.FromDays(7));\n```\n\n[**Recurring tasks**](https://docs.hangfire.io/en/latest/background-methods/performing-recurrent-tasks.html)\n\nRecurring jobs have never been simpler; just call the following method to perform any kind of recurring task using the [CRON expressions](https://en.wikipedia.org/wiki/Cron#CRON_expression).\n\n```csharp\nRecurringJob.AddOrUpdate(() => Console.WriteLine(\"Transparent!\"), Cron.Daily);\n```\n\n**Continuations**\n\nContinuations allow you to define complex workflows by chaining multiple background jobs together.\n\n```csharp\nvar id = BackgroundJob.Enqueue(() => Console.WriteLine(\"Hello, \"));\nBackgroundJob.ContinueWith(id, () => Console.WriteLine(\"world!\"));\n```\n\n**Process background tasks inside a web application…**\n\nYou can process background tasks in any OWIN-compatible application framework, including [ASP.NET MVC](https://www.asp.net/mvc), [ASP.NET Web API](https://www.asp.net/web-api), [FubuMvc](https://fubu-project.org), [Nancy](https://nancyfx.org), etc. Forget about [AppDomain unloads, Web Garden & Web Farm issues](https://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/) – Hangfire is reliable for web applications from scratch, even on shared hosting.\n\n```csharp\napp.UseHangfireServer();\n```\n\n**… or anywhere else**\n\nIn console applications, Windows Service, Azure Worker Role, etc.\n\n```csharp\nusing (new BackgroundJobServer())\n{\n    Console.WriteLine(\"Hangfire Server started. Press ENTER to exit...\");\n    Console.ReadLine();\n}\n```\n\nQuestions? Problems?\n---------------------\n\nOpen-source projects develop more smoothly when discussions are public.\n\nIf you have any questions, problems related to Hangfire usage or if you want to discuss new features, please visit the [discussion forum](https://discuss.hangfire.io). You can sign in there using your existing Google or GitHub account, so it's very simple to start using it.\n\nIf you've discovered a bug, please report it to the [Hangfire GitHub Issues](https://github.com/HangfireIO/Hangfire/issues?state=open). Detailed reports with stack traces, actual and expected behaviours are welcome.\n\nRelated Projects\n-----------------\n\nPlease see the [Extensions](https://www.hangfire.io/extensions.html) page on the official site.\n\nBuilding the sources\n---------------------\n\nPrerequisites:\n* [Razor Generator](https://marketplace.visualstudio.com/items?itemName=DavidEbbo.RazorGenerator): Required if you intend to edit the cshtml files.\n* Install the MSMQ service (Microsoft Message Queue Server), if not already installed.\n\nThen, create an environment variable with Variable name `Hangfire_SqlServer_ConnectionStringTemplate` and put your connection string in the Variable value field. Example:\n\n* Variable name: `Hangfire_SqlServer_ConnectionStringTemplate`\n* Variable value: `Data Source=.\\sqlexpress;Initial Catalog=Hangfire.SqlServer.Tests;Integrated Security=True;`\n\nTo build a solution and get assembly files, just run the following command. All build artifacts, including `*.pdb` files, will be placed into the `build` folder. **Before proposing a pull request, please use this command to ensure everything is ok.** Btw, you can execute this command from the Package Manager Console window.\n\n```\nbuild\n```\n\nTo build NuGet packages as well as an archive file, use the `pack` command as shown below. You can find the result files in the `build` folder.\n\n```\nbuild pack\n```\n\nTo see the full list of available commands, pass the `-docs` switch:\n\n```\nbuild -docs\n```\n\nHangfire uses [psake](https://github.com/psake/psake) build automation tool. All psake tasks and functions defined in `psake-build.ps1` (for this project) and `psake-common.ps1` (for other Hangfire projects) files. Thanks to the psake project, they are very simple to use and modify!\n\nRazor templates are compiled upon save with the [Razor Generator Visual Studio extension](https://marketplace.visualstudio.com/items?itemName=DavidEbbo.RazorGenerator).  You will need this installed if you want to modify the Dashboard UI.\n\nReporting security issues \n--------------------------\n\nIn order to give the community time to respond and upgrade we strongly urge you report all security issues privately. Please email us at [security@hangfire.io](mailto:security@hangfire.io) with details and we will respond ASAP. Security issues always take precedence over bug fixes and feature work. We can and do mark releases as \"urgent\" if they contain serious security fixes. \n\nLicense\n--------\n\nCopyright © 2013-2026 Hangfire OÜ.\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Lesser General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Lesser General Public License for more details.\n\nYou should have received a copy of the GNU Lesser General Public License\nalong with this program.  If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses).\n\nLegal\n------\n\nBy submitting a Pull Request, you disavow any rights or claims to any changes submitted to the Hangfire project and assign the copyright of those changes to Hangfire OÜ.\n\nIf you cannot or do not want to reassign those rights (your employment contract for your employer may not allow this), you should not submit a PR. Open an issue and someone else can do the work.\n\nThis is a legal way of saying \"If you submit a PR to us, that code becomes ours\". 99.9% of the time that's what you intend anyways; we hope it doesn't scare you away from contributing.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Reporting security issues \n\nIn order to give the community time to respond and upgrade we strongly urge you report all security issues privately. Please email us at [security@hangfire.io](security@hangfire.io) with details and we will respond ASAP. Security issues always take precedence over bug fixes and feature work. We can and do mark releases as \"urgent\" if they contain serious security fixes. \n"
  },
  {
    "path": "appveyor.yml",
    "content": "# AppVeyor CI build file, https://ci.appveyor.com/project/odinserj/hangfire\n\n# Notes:\n#   - Minimal appveyor.yml file is an empty file. All sections are optional.\n#   - Indent each level of configuration with 2 spaces. Do not use tabs!\n#   - All section names are case-sensitive.\n#   - Section names should be unique on each level.\n\n# Please don't edit it manually, use the `build.bat version` command instead.\nversion: 1.8.23-build-0{build}\n\nimage:\n  - Visual Studio 2022\n  - Ubuntu2004\n\n#---------------------------------#\n#    environment configuration    #\n#---------------------------------#\n\n# environment variables\nenvironment:\n  Hangfire_SqlServer_ConnectionStringTemplate: Server=.\\SQL2017;Database={0};User Id=sa;Password=Password12!;TrustServerCertificate=True;PoolBlockingPeriod=NeverBlock\n  SIGNPATH_API_TOKEN:\n    secure: nvG+jv/K3utFvpHGx/N6Glpv0Wdj0wfBSl8c/tkHbn2AIwGcNe2e4VSOkod7xVpC\n  COVERITY_TOKEN:\n    secure: r3yBqxgALySnCK9W6uiStqoadsqYtrWQolzxGDVKF74=\n  COVERITY_EMAIL:\n    secure: wf51HXCiUYxuTe+eo3uQOxqyptSLrH4IEqq0958Rmx8=\n\n# enable service required for tests\nservices:\n  - mssql2017\n\n#---------------------------------#\n#       build configuration       #\n#---------------------------------#\n\n# Installing MSMQ manually to avoid \"Cannot initialize 'msmq' service handler\" error\nbefore_build:\n  - cmd: powershell Import-Module ServerManager; Add-WindowsFeature MSMQ; net start msmq\n  - pwsh: Install-PSResource -Name SignPath -TrustRepository\n  - sh: nuget locals all -clear\n\nbuild_script:  \n  - pwsh: IF ($IsWindows -and ($env:APPVEYOR_SCHEDULED_BUILD -or $env:APPVEYOR_REPO_COMMIT_MESSAGE -like \"*covscan*\")) { .\\coverity-scan.ps1 }\n  - cmd: IF NOT DEFINED APPVEYOR_SCHEDULED_BUILD build.bat sign\n  - sh: chmod +x build.sh; ./build.sh\n\n#---------------------------------#\n#       tests configuration       #\n#---------------------------------#\n\ntest: off\n\n#---------------------------------#\n#      artifacts configuration    #\n#---------------------------------#\n\nartifacts:\n  - path: 'build\\**\\*.nupkg'\n  - path: 'build\\**\\*.zip'\n\n#---------------------------------#\n#      deployment configuration   #\n#---------------------------------#\n\ndeploy:\n  - provider: NuGet\n    api_key: \n      secure: eMIULftUVSY15jDNaQZYuEVn7MYcKWXiwlEt2x2Cir6qJPw47EfwH2k+BjaAzxUK\n    on:\n      appveyor_repo_tag: true\n"
  },
  {
    "path": "build.bat",
    "content": "@echo off\n.nuget\\NuGet.exe restore .nuget\\packages.config -OutputDirectory packages -UseLockFile -LockedMode -NoCache || exit /b 666\npwsh.exe -NoProfile -ExecutionPolicy RemoteSigned -Command \"& {Import-Module '.\\packages\\psake.*\\tools\\psake.psm1'; invoke-psake .\\psake-project.ps1 %*; if ($psake.build_success -eq $false) { exit 1 } else { exit 0 }; }\"\nexit /B %errorlevel%\n"
  },
  {
    "path": "build.sh",
    "content": "#!/bin/bash\n\nset -e;\nexport Hangfire_SqlServer_ConnectionStringTemplate=\"Server=tcp:127.0.0.1,1433;Database={0};User Id=sa;Password=Password12!;TrustServerCertificate=True;PoolBlockingPeriod=NeverBlock\";\n\nif hash dotnet 2>/dev/null; \nthen\n  dotnet test -c Release -f netcoreapp3.1 tests/Hangfire.Core.Tests;\n  dotnet test -c Release -f net6.0 tests/Hangfire.Core.Tests;\n  if hash sqlcmd 2>/dev/null;\n  then\n    dotnet test -c Release -f netcoreapp3.1 tests/Hangfire.SqlServer.Tests;\n    dotnet test -c Release -f net6.0 tests/Hangfire.SqlServer.Tests;\n  fi\nfi\n"
  },
  {
    "path": "coverity-scan.ps1",
    "content": "cov-configure --cs\ncov-build.exe --dir cov-int build.bat compile\n\n# Compress results.\n\"Compressing Coverity results...\"\n$zipEncoderDef = @'\n    namespace AnalyseCode {\n        public class PortableFileNameEncoder: System.Text.UTF8Encoding {\n            public PortableFileNameEncoder() {}\n            public override byte[] GetBytes(string entry) {\n                return base.GetBytes(entry.Replace(\"\\\\\", \"/\"));\n            }\n        }\n    }\n'@\nAdd-Type -TypeDefinition $zipEncoderDef\n[IO.Compression.ZipFile]::CreateFromDirectory(\n    \"$env:APPVEYOR_BUILD_FOLDER\\cov-int\",\n    \"$env:APPVEYOR_BUILD_FOLDER\\$env:APPVEYOR_PROJECT_NAME.zip\",\n    [IO.Compression.CompressionLevel]::Optimal,\n    $true,  # include root directory\n    (New-Object AnalyseCode.PortableFileNameEncoder))\n\n# Upload results to Coverity server.\n\"Uploading Coverity results...\"\nAdd-Type -AssemblyName \"System.Net.Http\"\n$client = New-Object Net.Http.HttpClient\n$client.Timeout = [TimeSpan]::FromMinutes(20)\n$form = New-Object Net.Http.MultipartFormDataContent\n\n# Fill token field.\n[Net.Http.HttpContent]$formField =\n    New-Object Net.Http.StringContent($env:COVERITY_TOKEN)\n$form.Add($formField, '\"token\"')\n\n# Fill email field.\n$formField = New-Object Net.Http.StringContent($env:COVERITY_EMAIL)\n$form.Add($formField, '\"email\"')\n\n# Fill file field.\n$fs = New-Object IO.FileStream(\n    \"$env:APPVEYOR_BUILD_FOLDER\\$env:APPVEYOR_PROJECT_NAME.zip\",\n    [IO.FileMode]::Open,\n    [IO.FileAccess]::Read)\n$formField = New-Object Net.Http.StreamContent($fs)\n$form.Add($formField, '\"file\"', \"$env:APPVEYOR_PROJECT_NAME.zip\")\n\n# Fill version field.\n$formField = New-Object Net.Http.StringContent($env:APPVEYOR_BUILD_VERSION)\n$form.Add($formField, '\"version\"')\n\n# Fill description field.\n$formField = New-Object Net.Http.StringContent(\"AppVeyor scheduled build.\")\n$form.Add($formField, '\"description\"')\n\n# Submit form.\n$url = \"https://scan.coverity.com/builds?project=$env:APPVEYOR_REPO_NAME\"\n$task = $client.PostAsync($url, $form)\ntry {\n    $task.Wait()  # throws AggregateException on timeout\n} catch [AggregateException] {\n    throw $_.Exception.InnerException\n}\n$task.Result\n$fs.Close()"
  },
  {
    "path": "nuspecs/Hangfire.AspNetCore.nuspec",
    "content": "<?xml version=\"1.0\"?>\n<package>\n  <metadata>\n    <id>Hangfire.AspNetCore</id>\n    <version>%version%</version>\n    <title>Hangfire ASP.NET Core Support</title>\n    <authors>Sergey Odinokov</authors>\n    <owners>HangfireIO, odinserj</owners>\n    <projectUrl>https://www.hangfire.io/</projectUrl>\n    <repository type=\"git\" url=\"https://github.com/HangfireIO/Hangfire.git\" commit=\"%commit%\" />\n    <license type=\"file\">LICENSE.md</license>\n    <icon>icon.png</icon>\n    <description>ASP.NET Core support for Hangfire, a background job framework for .NET applications.</description>\n    <copyright>Copyright © 2017-2026 Hangfire OÜ</copyright>\n    <tags>hangfire aspnetcore</tags>\n    <releaseNotes><![CDATA[\nRelease notes are available in our blog https://www.hangfire.io/blog/\nPlease see https://docs.hangfire.io/en/latest/upgrade-guides/upgrading-to-hangfire-1.8.html to learn how to upgrade.\n\n1.8.23\n• Fixed – `InvalidOperationException`: The request reached the end of the pipeline without executing the endpoint.\n\n1.8.22\n• Added – `MapHangfireDashboardWithNoAuthorizationFilters` method, which does not include local-only filters.\n• Changed – Set 404 status code when `MapHangfireDashboard` is used and no dispatcher is found.\n• Fixed – `InvalidOperationException` upon receiving a request for 'hangfire/bootstrap.min.css.map'.\n\n1.8.18\n• Fixed – Swallow possible `ObjectDisposedException` in the `StopAsync` method.\n• Fixed – Avoid `NullReferenceException` when `LocalIpAddress` or `RemoteIpAddress` is null.\n\n1.8.10\n• Fixed – Don't check `HasStarted` in `Response.WriteAsync` to avoid breaking dispatchers.\n• Project – Enable NuGet package and DLL signing with a company certificate.\n• Project – Require NuGet package signature validation on restore for dependencies.\n• Project – Add `HangfireIO` as a package owner.\n\n1.8.9\n• Fixed – Don't attempt to write response headers when response has already started (by @maliming).\n• Project – Enable full source link support with embedded symbols and repository-based sources.\n• Project – Enable repeatable package restore using a lock file.\n\n1.8.0\n• Breaking – Make the package to be dependent on Hangfire.NetCore and use the same types.\n• Added – `IApplicationBuilder.UseHangfireServer` that accepts custom factory for `IBackgroundProcessingServer`.\n]]>\n    </releaseNotes>\n    <dependencies>\n      <group targetFramework=\"net451\">\n        <dependency id=\"Microsoft.AspNetCore.Http.Abstractions\" version=\"1.0.0\" />\n        <dependency id=\"Microsoft.AspNetCore.Antiforgery\" version=\"1.0.0\" />\n        <dependency id=\"Hangfire.NetCore\" version=\"[%version%]\" />\n      </group>\n      <group targetFramework=\"netstandard1.3\">\n        <dependency id=\"NETStandard.Library\" version=\"1.6.0\" />\n        <dependency id=\"System.ComponentModel\" version=\"4.0.1\" />\n        <dependency id=\"Microsoft.AspNetCore.Http.Abstractions\" version=\"1.0.0\" />\n        <dependency id=\"Microsoft.AspNetCore.Antiforgery\" version=\"1.0.0\" />\n        <dependency id=\"Hangfire.NetCore\" version=\"[%version%]\" />\n      </group>\n      <group targetFramework=\"netstandard2.0\">\n        <dependency id=\"Microsoft.AspNetCore.Http.Abstractions\" version=\"2.0.0\" />\n        <dependency id=\"Microsoft.AspNetCore.Antiforgery\" version=\"2.0.0\" />\n        <dependency id=\"Hangfire.NetCore\" version=\"[%version%]\" />\n      </group>\n      <group targetFramework=\"net461\">\n        <dependency id=\"Microsoft.AspNetCore.Http.Abstractions\" version=\"2.0.0\" />\n        <dependency id=\"Microsoft.AspNetCore.Antiforgery\" version=\"2.0.0\" />\n        <dependency id=\"Hangfire.NetCore\" version=\"[%version%]\" />\n      </group>\t  \n      <group targetFramework=\"netcoreapp3.0\">\n        <dependency id=\"Hangfire.NetCore\" version=\"[%version%]\" />\n      </group>\n    </dependencies>\n    <frameworkReferences>\n      <group targetFramework=\".NETCoreApp3.0\">\n        <frameworkReference name=\"Microsoft.AspNetCore.App\" />\n      </group>\n    </frameworkReferences>\n  </metadata>\n  <files>\n    <file src=\"net451\\Hangfire.AspNetCore.dll\" target=\"lib\\net451\" />\n    <file src=\"net451\\Hangfire.AspNetCore.xml\" target=\"lib\\net451\" />\n\n    <file src=\"netstandard1.3\\Hangfire.AspNetCore.dll\" target=\"lib\\netstandard1.3\" />\n    <file src=\"netstandard1.3\\Hangfire.AspNetCore.xml\" target=\"lib\\netstandard1.3\" />\n\n    <file src=\"netstandard2.0\\Hangfire.AspNetCore.dll\" target=\"lib\\netstandard2.0\" />\n    <file src=\"netstandard2.0\\Hangfire.AspNetCore.xml\" target=\"lib\\netstandard2.0\" />\n\t\n    <file src=\"net461\\Hangfire.AspNetCore.dll\" target=\"lib\\net461\" />\n    <file src=\"net461\\Hangfire.AspNetCore.xml\" target=\"lib\\net461\" />\n\n    <file src=\"netcoreapp3.0\\Hangfire.AspNetCore.dll\" target=\"lib\\netcoreapp3.0\" />\n    <file src=\"netcoreapp3.0\\Hangfire.AspNetCore.xml\" target=\"lib\\netcoreapp3.0\" />\n\n    <file src=\"..\\nuspecs\\icon.png\" />\n\n    <file src=\"LICENSE.md\" />\n    <file src=\"COPYING\" />\n    <file src=\"COPYING.LESSER\" />\n    <file src=\"NOTICES\" />\n    <file src=\"LICENSE_STANDARD\" />\n    <file src=\"LICENSE_ROYALTYFREE\" />\n  </files>\n</package>"
  },
  {
    "path": "nuspecs/Hangfire.Core.nuspec",
    "content": "<?xml version=\"1.0\"?>\n<package>\n  <metadata>\n    <id>Hangfire.Core</id>\n    <version>%version%</version>\n    <title>Hangfire Core Components</title>\n    <authors>Sergey Odinokov</authors>\n    <owners>HangfireIO, odinserj</owners>\n    <projectUrl>https://www.hangfire.io/</projectUrl>\n    <repository type=\"git\" url=\"https://github.com/HangfireIO/Hangfire.git\" commit=\"%commit%\" />\n    <license type=\"file\">LICENSE.md</license>\n    <icon>icon.png</icon>\n    <readme>README.md</readme>\n    <summary>An easy way to perform fire-and-forget, delayed and recurring tasks in .NET applications. No Windows Service required.</summary>\n    <description>\nAn easy and reliable way to perform fire-and-forget, delayed and recurring, long-running, short-running, CPU or I/O intensive tasks in .NET applications. No Windows Service / Task Scheduler required.\nBacked by Redis, SQL Server, SQL Azure or MSMQ. This is a .NET alternative to Sidekiq, Resque and Celery.\nhttps://www.hangfire.io/\n    </description>\n    <copyright>Copyright © 2013-2026 Hangfire OÜ</copyright>\n    <tags>Hangfire OWIN Long-Running Background Fire-And-Forget Delayed Recurring Tasks Jobs Scheduler Threading Queues</tags>\n    <releaseNotes><![CDATA[\nRelease notes are available in our blog https://www.hangfire.io/blog/\nPlease see https://docs.hangfire.io/en/latest/upgrade-guides/upgrading-to-hangfire-1.8.html to learn how to upgrade.\n\n1.8.23\n• Changed – Use stable sorting algorithm for background job filters again (by @jirikanda).\n• Fixed – Add missing keys for Swedish translation (by @karl-sjogren).\n• Fixed – Custom `AutomaticRetryAttribute` is ignored under certain conditions (by @jirikanda).\n• Project – Use `TypeNameAssemblyFormatHandling` in tests with .NET 6 (by @viktor-vintertass).\n\n1.8.22\n• Added – `IGlobalConfiguration.UseNoOpLogProvider` method to disable logging.\n• Changed – Un-deprecate interval methods in the `Cron` class, add remarks in docs instead.\n• Changed – Bump internalized version of Cronos to 0.11.1.\n• Changed – Bump internalized version of Microsoft.Owin to 4.2.3.\n• Fixed – Serialization of arrays of nested types `SimpleAssemblyTypeSerializer`.\n• Fixed – Remove wrong escaping characters in Portuguese translations on the \"Servers\" page.\n• Fixed – Properly remove registered `IBackgroundProcessingServer` instances on OWIN app shutdown.\n• Fixed – `AspNetShutdownDetector` for early ASP.NET shutdown detection is not working (regression from 1.7.30).\n• Project – Replace the `netcoreapp3.1` target with the `net8.0` one in tests.\n\n1.8.21\n• Added – `FailedState.IncludeFileInfo` to optionally show/hide line numbers in exceptions in Failed state.\n• Changed – Include line numbers for exceptions by default when available.\n• Fixed – Portuguese (Brazil) translations in Strings.pt-BR.resx (by @pedro-cons).\n• Fixed – Static `BackgroundJob` class always acquires the most current `JobStorage.Current` instance.\n• Fixed – Static `RecurringJob` class always acquires the most current `JobStorage.Current` instance.\n\n1.8.20\n• Fixed – Glyphicons from Bootstrap are not displaying after upgrading to version 1.8.19.\n\n1.8.19\n• Changed – Update Bootstrap to the custom version of 3.4.2 to avoid false alerts on unused features.\n• Fixed – Typos in Portuguese translation (by @VianaArthur).\n• Fixed – Unnecessary recurring job update transaction when nothing is changed after an error.\n\n1.8.18\n• Added – `DashboardOptions.ServerPossiblyAbortedThreshold` to configure a custom threshold for \"possibly aborted\" warnings.\n• Fixed – Expired jobs are still shown on the \"Retries\" page in some cases.\n• Fixed – Issues with `CultureInfo`-related differences after upgrading to 1.8.15–1.8.17.\n• Fixed – Don't leak `AsyncLocal` values from synchronous background job methods.\n• Fixed – Don't throw an exception when passing the `Job.Args` property to the `Job` class' constructor.\n• Project – Make the lock file usable for both .NET 8.0 and .NET 9.0 builds.\n• Project – Make code generation for `cshtml` files working on newer platforms.\n\n1.8.16\n• Changed – Include fewer stack frames in exceptions come from `IServerFilter` implementations.\n• Changed – Don't include file information in the `ExceptionDetails` property of a FailedState instance.\n• Changed – Switch back to `CancellationEvent` usage instead of `CancellationToken.WaitHandle`.\n• Fixed – Don't commit external transaction in the `BackgroundJobStateChanger` implementation.\n• Fixed – Use safe default serializer settings for Newtonsoft.Json 12.X and below.\n• Project – Fix builds for the `net451` platform when using .NET 9.0.\n• Project – Significantly reduce execution time of unit tests in the `RecurringJobSchedulerFacts` class.\n• Project – Bump `Microsoft.CodeAnalysis.NetAnalyzers` package to version 9.0.0.\n\n1.8.15\n• Added – New `AutomaticRetryAttribute.ExceptOn` property to skip retries for specific exceptions.\n• Changed – Refactor filters pipeline to use less LINQ magic and fewer allocations.\n• Changed – Use `GetCultureInfo` instead of creating an instance in the `CaptureCultureAttribute` filter.\n• Changed – Cache some immutable data to avoid extra allocations.\n• Fixed – Improve loopback address detection (by @meziantou).\n• Fixed – Reformulate misleading error messages regarding retry timings (by @RGFuaWVs).\n• Fixed – Problem with missing localizations in the previous version.\n• Fixed – Don't hide exception details on Failed Jobs page when the exception message is empty.\n• Fixed – Problems with the first restore when using the `build.bat` command.\n• Fixed – Better display of canceled recurring jobs in dashboard.\n• Fixed – Less overall allocations with using static delegates and struct-based iterators.\n• Fixed – Improve precision of some diagnostic messages in the wait protection logic.\n• Fixed – Make all private and internal classes sealed to improve code consistency.\n• Fixed – Less overall pressure on garbage collector.\n\n1.8.13 and 1.8.14\n• Changed – Partial cache for serialization and deserialization in `InvocationData` to produce less strings.\n• Changed – Add caching for default type serializer and resolver.\n• Changed – Don't let `JobFilter`-related logic to show up in profilers.\n• Changed – Modify `IProfiler` to be less allocatey for diagnostic purposes that almost never run.\n• Changed – Prefer using `CancellationToken.WaitHandle` again, since early .NET Core days are gone.\n• Changed – Fewer allocations when working with `IStateHandler` collections in a state machine.\n• Fixed – Redirect the \"System.Private.Xml.Linq\" assembly to the \"System.Xml.Linq\" one for better interoperability.\n• Fixed – Don't throw `KeyNotFoundException` when recurring job is malformed.\n• Fixed – Proper relative path calculation in `UrlHelper.To` for OWIN-based Dashboard UI (by @LordJZ).\n• Fixed – Typo in the Turkish localization file (by @ismkdc).\n• Project – Switch to a modern PowerShell 7+ to speed up SignPath installation on AppVeyor.\n\n1.8.12\n• Added – `MaxDegreeOfParallelismForSchedulers` experimental server option if supported by storage.\n• Added – Experimental support for parallel execution of the delayed job scheduler.\n• Added – Experimental support for parallel execution of the recurring job scheduler.\n• Fixed – Recurring job is scheduled to the past after recovering from error with `AddOrUpdate`.\n• Fixed – `AddOrUpdate` triggers execution of a recurring job, even if its next execution is in the future.\n• Fixed – Two very minor errors in the Swedish localization file (by @Uglack).\n\n1.8.11\n• Changed – Add icons and fix metadata for NuGet packages.\n• Changed – Bump ILRepack to version 2.0.27 to avoid problems with internalizing.\n• Fixed – \"Type exists in both Cronos and Hangfire.Core\" exception.\n\n1.8.10\n• Changed – Added Norwegian translations for new keys (by @khellang).\n• Changed – Update Brazilian Portuguese translation (by @HugoAlames).\n• Changed – Bump Cronos dependency to version 0.8.3.\n• Project – Enable NuGet package and DLL signing with a company certificate.\n• Project – Require NuGet package signature validation on restore for dependencies.\n• Project – Add `HangfireIO` as a package owner.\n\n1.8.9\n• Changed – Use `Environment.MachineName` as a server name if other environment vars aren't available.\n• Changed – Bump the Cronos package version from 0.7.1 to 0.8.1.\n• Changed – Improve portuguese translations (by @filipe-silva).\n• Fixed – Possible `NullReferenceException` on the Deleted Jobs page (regression from 1.8.7).\n• Project – Enable full source link support with embedded symbols and repository-based sources.\n• Project – Enable repeatable package restore using a lock file.\n• Project – Run unit tests against the `net6.0` platform.\n• Project – Modernise the build system and clean up the build scripts.\n\n1.8.7\n• Added – Allow using macro expressions like `@hourly` for recurring jobs (by @MuhamedAbdalla).\n• Added – Show storage time in page footer when supported by storage implementation.\n• Added – Show duration and latency columns separately on the Succeeded Jobs page when supported.\n• Added – Show the exception column on the Deleted Jobs page when available and supported by storage.\n• Changed – Reduce package size by stripping unnecessary locales in Moment.js.\n• Changed – Bump Microsoft.Owin package to version 4.2.2.\n• Changed – Log a warning message when a server listens to unsupported queue names.\n• Changed – Use storage time, if available, to show delay warnings in the Dashboard UI.\n• Fixed – Proper rendering of generic arguments on the Job Details page (by @olivermue).\n• Fixed – Language inconsistency in the Dashboard UI related to date/time description.\n• Fixed – Big stack traces take too long time to be formatted.\n• Fixed – Don't throw `NullReferenceException` from the Scheduled Jobs page when there's a job with missing data.\n• Fixed – Don't throw `NullReferenceException` from the Processing Jobs page when there's a job with missing data.\n• Fixed – CSS for Enqueued and Deleted state cards in dark theme.\n• Fixed – Log errors instead of throwing an exception when a particular table can't be cleaned.\n• Fixed – Avoid logging fatal exceptions when stopping a faulting background process.\n• Fixed – Don't display checkboxes in the Dashboard UI when job details can not be fetched.\n• Fixed – Scrollbars in WebKit-based browsers are now dark in dark mode.\n• Project – Disable tests for `netcoreapp1.0` and `netcoreapp2.1` targets since they aren't supported in AppVeyor.\n• Project – Add a `net6.0` target for unit tests instead of the removed ones.\n• Project – Modernise projects and build environments to use the newest features.\n\n1.8.6\n• Changed – Update jQuery library in Dashboard UI to version 3.7.1.\n• Changed – Mark all types in Hangfire.Annotations with `EditorBrowsableAttribute(Never)`.\n• Changed – Change state card colors for the Awaiting state to match the Scheduled state.\n• Fixed – Exception when deserializing an instance of the `AutomaticRetryAttribute` class from JSON.\n• Fixed – Add serialization-related constructors for all the exception classes.\n• Fixed – Use invariant culture or ordinal comparisons for internal strings.\n• Fixed – Use invariant culture when formatting key names for metrics.\n• Fixed – Use `CurrentCulture` instead of `CurrentUICulture` when displaying time.\n• Project – Enable running static analysis by Coverity Scan weekly.\n• Project – Enable mandatory static analysis by the Microsoft.CodeAnalysis.NetAnalyzers package.\n• Project – Change MSBuild path when building using newer .NET SDKs for Razor views.\n\n1.8.5\n• Added – Possibility to inform a `FaviconPath` on `DashboardOptions` (by @cezar-pimentel).\n• Fixed – Inability to restore a disabled recurring job, regression in version 1.8.3.\n• Fixed – Make it possible to serialize the `AutomaticRetryAttribute` filter to JSON.\n\n1.8.4\n• Added – Pass server id from a worker to the `PerformContext.ServerId` property available in filters.\n• Fixed – Send heartbeats until full background processing server shutdown.\n\n1.8.3\n• Changed – Allow to configure `MaxLinesInStackTrace` for a particular `FailedState` instance.\n• Fixed – Remove job id from schedule when it's not in the Scheduled state for some reason.\n• Fixed – Missing invocations of recurring jobs when the new \"Ignorable\" option is used.\n• Fixed – Make `DisableConcurrentExecutionAttribute` and `LatencyTimeoutAttribute` serializable.\n\n1.8.2\n• Changed – Disable transactional job creation feature appeared in 1.8.0.\n• Fixed – \"Can not start continuation XXX\" error when storage supports transactional job creation.\n\n1.8.1\n• Added – `MisfireHandlingMode.Ignorable` to avoid scheduling recurring jobs on missed schedules.\n• Added – Support disabling dark mode via the `DashboardOptions.DarkModeEnabled` property.\n• Changed – Remove the 1-hour limitation for the `WithJobExpirationTimeout` configuration method.\n• Fixed – Add missing `UseDefaultCulture` configuration method overloads.\n• Fixed – Add missing `UseDashboardStylesheet` and `UseJobDetailsRenderer` configuration methods.\n• Fixed – Give even more space for identifiers on the Recurring Jobs page.\n• Fixed – `state-card-state-active` color is not very dark (by @coolhome).\n• Fixed – Slightly change chart proportions to fit 4K in Dashboard UI.\n\n1.8.0\n• Breaking – Dropped the `NET45` platform target in favor of the `NET451` target to support Visual Studio 2022.\n• Added – Introduce the `Job.Queue` property, so jobs now can have their own queue specified.\n• Added – Method overloads to create background jobs directly with a custom default queue.\n• Added – Method overloads to create recurring jobs directly with a custom default queue.\n• Added – `IBackgroundJobClient.Create` method overloads with the new `queue` parameter.\n• Added – Allow to filter exception types in `AutomaticRetryAttribute` by using the new `OnlyOn` property.\n• Added – `DeletedState` now has the persisted `Exception` property populated after a failure.\n• Added – `JobContinuationOptions.OnlyOnDeletedState` to create continuations after a failure.\n• Added – `Exception` job parameter is passed to continuation when `UseResultsInContinuations` method is used.\n• Added – `FromExceptionAttribute` to deal with an antecedent exception in a background job continuation.\n• Added – Make it possible to specify multiple `JobContinuationOptions` values for a continuation.\n• Added – `BackgroundJobServerOptions.IsLightweightServer` option to run a server with no storage processes.\n• Added – Ability to use custom formattable resource identifiers for the `DisableConcurrentExecution` filter.\n• Added – Pass `ServerId` to `FailedState` instances to simplify the debugging on different servers.\n• Added – Allow to pass job parameters when creating a job (by @brian-knoll-micronetonline).\n• Added – `MisfireHandlingMode.Strict` to create a job for each missed recurring job occurrence.\n• Added – Support for default culture and UI culture via the `UseDefaultCulture` configuration method.\n• Added – Introduce the `captureDefault` parameter in the `CaptureCulture` filter.\n• Added – `IGlobalConfiguration.UseFilterProvider` extension method to unify the configuration.\n• Added – Built-in `Remove` method for `JobFilterCollection` to remove global filters based on their type.\n• Added – `CompatibilityLevel.Version_180` flag to avoid storing culture parameters when they are the same as the default ones.\n• Changed – Create job atomically when `Transaction.CreateJob` feature is supported by the storage.\n• Changed – Query time from storage in recurring and delayed schedulers when supported by storage.\n• Changed – Move job to the `DeletedState` instead of `SucceededState` when its invocation was canceled by a filter.\n• Changed – Speedup delayed jobs when a custom default queue is specified by avoiding extra state transition.\n• Changed – Use UI culture from `CurrentCulture` parameter when `CurrentUICulture` one is missing.\n• Changed – Increase the default value for the `BackgroundJobServerOptions.StopTimeout` to 500 ms.\n• Deprecated – `AddOrUpdate` overloads with optional params defined in the `RecurringJobManagerExtensions` class.\n• Deprecated – `AddOrUpdate` overloads with optional parameters defined in the `RecurringJob` class.\n• Deprecated – `AddOrUpdate` method overloads with no `recurringJobId` parameter.\n• Deprecated – `RecurringJobOptions.QueueName` property, new methods should be used instead.\n• Breaking – Dropped `NET45` platform target in favor of `NET451` target to support Visual Studio 2022.\n\nDashboard UI\n• Added – Dark mode support for Dashboard UI depending on the system settings (by @danillewin).\n• Added – Dashboard UI now has a full-width layout to display more data (by @danillewin).\n• Added – Allow to add custom JavaScript and CSS files to the Dashboard UI via the `DashboardRoutes` class.\n• Added – `DefaultRecordsPerPage` property on the `DashboardOptions` class (by @PaulARoy).\n• Added – `IGlobalConfiguration.UseJobDetailsRenderer` method for custom renderers for the Job Details page.\n• Added – Display deleted jobs in the Realtime and History graphs when supported by storage.\n• Added – `IGlobalConfiguration.UseDashboardMetrics` extension method to pass multiple metrics at once.\n• Added – State renderer for the `DeletedState` to display its new exception property.\n• Added – Support for new `MonitoringApi` methods for the Awaiting Jobs page.\n• Changed – Make it possible to display methods of non-loaded jobs in the Dashboard UI when supported by storage.\n• Changed – Improved display of realtime chart with more accents on failed and deleted jobs.\n• Changed – Don't display the queue name in the state transition list when it's the `default` one.\n• Changed – Display scheduled job count when the enqueued count is zero on the main metric.\n\nExtensibility\n• Added – `Factory`, `StateMachine`, and `Performer` properties to context classes to avoid injecting services.\n• Added – Allow to pass custom data to `ApplyStateContext` and `ElectStateContext` instances.\n• Added – Preserve custom data dictionary between the entire filter chain.\n• Added – Allow to pass a transaction to background job state changer when new methods are implemented.\n• Changed – Ignore some members when serializing a `JobFilterAttribute` instance to decrease the payload size.\n\nStorage\n• Added – Virtual `JobStorage.GetReadOnlyConnection` method intended to return `JobStorageConnection` for replicas.\n• Added – Virtual `JobStorage.HasFeature` method for querying optional features.\n• Added – The `JobStorageFeatures` class to avoid using magic strings in storage features.\n• Added – Optional `GetSetCount`, `GetSetContains`, and `GetUtcDateTime` methods for the `JobStorageConnection` class.\n• Added – Optional `AcquireDistributedLock` and `RemoveFromQueue` methods for the `JobStorageTransaction` class.\n• Added – Optional `CreateJob` and `SetJobParameter` methods for the `JobStorageTransaction` class.\n• Added – Optional `ParametersSnapshot` property for `BackgroundJob` and `JobData` classes to minimize roundtrips in the future.\n• Added – Support for transactional acknowledgment using a new storage method for better handling some data loss scenarios.\n• Added – Fetch `Retries` and `Awaiting` metrics in `StatisticsDto` properties when supported by storage.\n• Added – The `JobStorageMonitor` class with more available methods for the new features.\n• Changed – Allow to query job parameters without additional roundtrip when supported by storage.\n• Changed – Expose state data dictionaries in list DTOs when supported by storage.\n• Changed – Rely on storage indexing with the `Monitoring.AwaitingJobs` feature.\n\nInternals\n• Added – `IBackgroundProcess.UseBackgroundPool` now allows to pass thread configuration logic.\n• Added – `BackgroundJobServerOptions.WorkerThreadConfigurationAction` option for custom thread configuration.\n• Changed – Allow changing queues on the fly with custom worker configuration.\n• Changed – Avoid storage roundtrip to query job data in worker, take data from previous state change.\n• Changed – `FromParameterAttribute`-based logic now always overwrites arguments, even with non-null values.\n• Changed – Turn the `JobContinuationOptions` enum into flags while still possible.\n• Changed – Re-implement `TaskExtensions.WaitOneAsync` only with the `RegisterWaitForSingleObject` method.\n• Changed – `ServerHeartbeatProcess` now uses `ThreadPriority.AboveNormal` to prioritize heartbeats.\n]]>\n    </releaseNotes>\n    <dependencies>\n      <group targetFramework=\"net451\">\n        <dependency id=\"Owin\" version=\"1.0\" />\n        <dependency id=\"Newtonsoft.Json\" version=\"5.0.1\" />\n      </group>\n      <group targetFramework=\"net46\">\n        <dependency id=\"Owin\" version=\"1.0\" />\n        <dependency id=\"Newtonsoft.Json\" version=\"5.0.1\" />\n      </group>\n      <group targetFramework=\"netstandard1.3\">\n        <dependency id=\"NETStandard.Library\" version=\"1.6.0\" />\n        <dependency id=\"System.Threading.Thread\" version=\"4.0.0\" />\n        <dependency id=\"System.Threading.ThreadPool\" version=\"4.0.10\" />\n        <dependency id=\"Newtonsoft.Json\" version=\"9.0.1\" />\n      </group>\n      <group targetFramework=\"netstandard2.0\">\n        <dependency id=\"Newtonsoft.Json\" version=\"11.0.1\" />\n      </group>\n    </dependencies>\n  </metadata>\n  <files>\n    <file src=\"net451\\Hangfire.Core.dll\" target=\"lib\\net451\" />\n    <file src=\"net451\\Hangfire.Core.xml\" target=\"lib\\net451\" />\n\n    <file src=\"net451\\**\\Hangfire.Core.resources.dll\" target=\"lib\\net451\" />\n\n    <file src=\"net46\\Hangfire.Core.dll\" target=\"lib\\net46\" />\n    <file src=\"net46\\Hangfire.Core.xml\" target=\"lib\\net46\" />\n\n    <file src=\"net46\\**\\Hangfire.Core.resources.dll\" target=\"lib\\net46\" />\n    \n    <file src=\"netstandard1.3\\Hangfire.Core.dll\" target=\"lib\\netstandard1.3\" />\n    <file src=\"netstandard1.3\\Hangfire.Core.xml\" target=\"lib\\netstandard1.3\" />\n\n    <file src=\"netstandard1.3\\**\\Hangfire.Core.resources.dll\" target=\"lib\\netstandard1.3\" />\n\n    <file src=\"netstandard2.0\\Hangfire.Core.dll\" target=\"lib\\netstandard2.0\" />\n    <file src=\"netstandard2.0\\Hangfire.Core.xml\" target=\"lib\\netstandard2.0\" />\n\n    <file src=\"netstandard2.0\\**\\Hangfire.Core.resources.dll\" target=\"lib\\netstandard2.0\" />\n\n    <file src=\"..\\nuspecs\\icon.png\" />\n\n    <file src=\"README.md\" />    \n    <file src=\"LICENSE.md\" />\n    <file src=\"COPYING\" />\n    <file src=\"COPYING.LESSER\" />\n    <file src=\"NOTICES\" />\n    <file src=\"LICENSE_STANDARD\" />\n    <file src=\"LICENSE_ROYALTYFREE\" />\n  </files>\n</package>"
  },
  {
    "path": "nuspecs/Hangfire.NetCore.nuspec",
    "content": "<?xml version=\"1.0\"?>\n<package>\n  <metadata>\n    <id>Hangfire.NetCore</id>\n    <version>%version%</version>\n    <title>Hangfire .NET Core's Worker Service Support</title>\n    <authors>Sergey Odinokov</authors>\n    <owners>HangfireIO, odinserj</owners>\n    <projectUrl>https://www.hangfire.io/</projectUrl>\n    <repository type=\"git\" url=\"https://github.com/HangfireIO/Hangfire.git\" commit=\"%commit%\" />\n    <license type=\"file\">LICENSE.md</license>\n    <icon>icon.png</icon>\n    <description>.NET Core's Worker Service host support for Hangfire, a background job framework for .NET applications.</description>\n    <copyright>Copyright © 2019-2026 Hangfire OÜ</copyright>\n    <tags>hangfire netcore</tags>\n    <releaseNotes><![CDATA[\nRelease notes are available in our blog https://www.hangfire.io/blog/\nPlease see https://docs.hangfire.io/en/latest/upgrade-guides/upgrading-to-hangfire-1.8.html to learn how to upgrade.\n\n1.8.10\n• Project – Enable NuGet package and DLL signing with a company certificate.\n• Project – Require NuGet package signature validation on restore for dependencies.\n• Project – Add `HangfireIO` as a package owner.\n\n1.8.9\n• Project – Enable full source link support with embedded symbols and repository-based sources.\n• Project – Enable repeatable package restore using a lock file.\n\n1.8.6\n• Fixed – Include assembly information to the Hangfire.NetCore assembly.\n\n1.8.4\n• Changed – Send the stop signal earlier in the shutdown pipeline when hosting in .NET Core 3.1 or higher.\n• Changed – Set processing server to null in hosted service to avoid `ObjectDisposedException`.\n• Fixed – Other `IHostedService` implementations can block Hangfire server from being stopped.\n      \n1.8.1\n• Fixed – Add `net461` target for Hangfire.NetCore package to avoid missing method exceptions.\n\n1.8.0\n• Added – `IApplicationBuilder.UseHangfireServer` that accepts custom factory for `IBackgroundProcessingServer`.\n• Added – `net451` and `netstandard1.3` targets for the package.\n• Changed – Use `netstandard2.1` target instead of `netcoreapp3.0` for the package.\n• Changed – Send the \"stop\" signal earlier when the host supports .NET Standard 2.1.\n• Changed – Don't throw `ObjectDisposedException` when hosted service is disposed twice.\n]]>\n    </releaseNotes>\n    <dependencies>\n      <group targetFramework=\"net451\">\n        <dependency id=\"Microsoft.Extensions.DependencyInjection.Abstractions\" version=\"1.0.0\" />\n        <dependency id=\"Microsoft.Extensions.Logging.Abstractions\" version=\"1.0.0\" />\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n      </group>\n      <group targetFramework=\"net461\">\n        <dependency id=\"Microsoft.Extensions.DependencyInjection.Abstractions\" version=\"2.0.0\" />\n        <dependency id=\"Microsoft.Extensions.Hosting.Abstractions\" version=\"2.0.0\" />\n        <dependency id=\"Microsoft.Extensions.Logging.Abstractions\" version=\"2.0.0\" />\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n      </group>\n      <group targetFramework=\"netstandard1.3\">\n        <dependency id=\"NETStandard.Library\" version=\"1.6.0\" />\n        <dependency id=\"System.ComponentModel\" version=\"4.0.1\" />\n        <dependency id=\"Microsoft.Extensions.DependencyInjection.Abstractions\" version=\"1.0.0\" />\n        <dependency id=\"Microsoft.Extensions.Logging.Abstractions\" version=\"1.0.0\" />\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n      </group>\n      <group targetFramework=\"netstandard2.0\">\n        <dependency id=\"Microsoft.Extensions.DependencyInjection.Abstractions\" version=\"2.0.0\" />\n        <dependency id=\"Microsoft.Extensions.Hosting.Abstractions\" version=\"2.0.0\" />\n        <dependency id=\"Microsoft.Extensions.Logging.Abstractions\" version=\"2.0.0\" />\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n      </group>\n      <group targetFramework=\"netstandard2.1\">\n        <dependency id=\"Microsoft.Extensions.DependencyInjection.Abstractions\" version=\"3.0.0\" />\n        <dependency id=\"Microsoft.Extensions.Hosting.Abstractions\" version=\"3.0.0\" />\n        <dependency id=\"Microsoft.Extensions.Logging.Abstractions\" version=\"3.0.0\" />\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n      </group>\n    </dependencies>\n  </metadata>\n  <files>\n    <file src=\"net451\\Hangfire.NetCore.dll\" target=\"lib\\net451\" />\n    <file src=\"net451\\Hangfire.NetCore.xml\" target=\"lib\\net451\" />\n\n    <file src=\"net461\\Hangfire.NetCore.dll\" target=\"lib\\net461\" />\n    <file src=\"net461\\Hangfire.NetCore.xml\" target=\"lib\\net461\" />\n\n    <file src=\"netstandard1.3\\Hangfire.NetCore.dll\" target=\"lib\\netstandard1.3\" />\n    <file src=\"netstandard1.3\\Hangfire.NetCore.xml\" target=\"lib\\netstandard1.3\" />\n\n    <file src=\"netstandard2.0\\Hangfire.NetCore.dll\" target=\"lib\\netstandard2.0\" />\n    <file src=\"netstandard2.0\\Hangfire.NetCore.xml\" target=\"lib\\netstandard2.0\" />\n\n    <file src=\"netstandard2.1\\Hangfire.NetCore.dll\" target=\"lib\\netstandard2.1\" />\n    <file src=\"netstandard2.1\\Hangfire.NetCore.xml\" target=\"lib\\netstandard2.1\" />\n\n    <file src=\"..\\nuspecs\\icon.png\" />\n\n    <file src=\"LICENSE.md\" />\n    <file src=\"COPYING\" />\n    <file src=\"COPYING.LESSER\" />\n    <file src=\"NOTICES\" />\n    <file src=\"LICENSE_STANDARD\" />\n    <file src=\"LICENSE_ROYALTYFREE\" />\n  </files>\n</package>\n"
  },
  {
    "path": "nuspecs/Hangfire.SqlServer.MSMQ.nuspec",
    "content": "<?xml version=\"1.0\"?>\n<package>\n  <metadata>\n    <id>Hangfire.SqlServer.MSMQ</id>\n    <version>%version%</version>\n    <title>Hangfire MSMQ Queues for SQL Server Storage</title>\n    <authors>Sergey Odinokov</authors>\n    <owners>HangfireIO, odinserj</owners>\n    <projectUrl>https://www.hangfire.io/</projectUrl>\n    <repository type=\"git\" url=\"https://github.com/HangfireIO/Hangfire.git\" commit=\"%commit%\" />\n    <license type=\"file\">LICENSE.md</license>\n    <icon>icon.png</icon>\n    <description>MSMQ queues support for SQL Server job storage implementation for Hangfire, a background job framework for .NET applications.</description>\n    <copyright>Copyright © 2014-2026 Hangfire OÜ</copyright>\n    <tags>Hangfire SqlServer MSMQ</tags>\n    <releaseNotes><![CDATA[https://www.hangfire.io/blog/\n\n1.8.10\n• Project – Enable NuGet package and DLL signing with a company certificate.\n• Project – Require NuGet package signature validation on restore for dependencies.\n• Project – Add `HangfireIO` as a package owner.\n  \n1.8.9\n• Project – Enable full source link support with embedded symbols and repository-based sources.\n• Project – Enable repeatable package restore using a lock file.\n\n1.8.0\n• Breaking – Dropped the `NET45` platform target in favor of the `NET451` target to support Visual Studio 2022.\n    \n1.6.3\n• Fixed – Prevent MSMQ transactions from timing out after 1 minute of processing.\n    \n1.6.2\n• Fixed – Public MSMQ queue paths are parsed correctly now, when determining the queue length.\n    \n1.6.0\n• Fixed – Package now depends on the latest Hangfire.SqlServer instead of version 1.2.2.\n    \n1.5.7\n• Fixed – Dashboard crashing when trying to get the MSMQ queue length (by @yangman).\n    \n1.5.0\n• Added – Support for remote MSMQ queues through DTC transactions.\n\n1.4.0\n• Changed – Speed up `GetCount` method with native implementation.\n• Fixed – Incorrect order of fetching when multiple queues used.\n]]>\n    </releaseNotes>\n    <dependencies>\n      <group targetFramework=\"net451\">\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n        <dependency id=\"Hangfire.SqlServer\" version=\"[%version%]\" />\n      </group>\n    </dependencies>\n  </metadata>\n  <files>\n    <file src=\"net451\\Hangfire.SqlServer.Msmq.dll\" target=\"lib\\net451\" />\n    <file src=\"net451\\Hangfire.SqlServer.Msmq.xml\" target=\"lib\\net451\" />\n\n    <file src=\"..\\nuspecs\\icon.png\" />\n\n    <file src=\"LICENSE.md\" />\n    <file src=\"COPYING\" />\n    <file src=\"COPYING.LESSER\" />\n    <file src=\"NOTICES\" />\n    <file src=\"LICENSE_STANDARD\" />\n    <file src=\"LICENSE_ROYALTYFREE\" />\n  </files>\n</package>"
  },
  {
    "path": "nuspecs/Hangfire.SqlServer.nuspec",
    "content": "<?xml version=\"1.0\"?>\n<package>\n  <metadata>\n    <id>Hangfire.SqlServer</id>\n    <version>%version%</version>\n    <title>Hangfire SQL Server Storage</title>\n    <authors>Sergey Odinokov</authors>\n    <owners>HangfireIO, odinserj</owners>\n    <projectUrl>https://www.hangfire.io/</projectUrl>\n    <repository type=\"git\" url=\"https://github.com/HangfireIO/Hangfire.git\" commit=\"%commit%\" />\n    <license type=\"file\">LICENSE.md</license>\n    <icon>icon.png</icon>\n    <description>SQL Server 2008+ (including Express), SQL Server LocalDB and SQL Azure storage support for Hangfire, a background job framework for .NET applications.</description>\n    <copyright>Copyright © 2013-2026 Hangfire OÜ</copyright>\n    <tags>Hangfire SqlServer SqlAzure LocalDB</tags>\n    <releaseNotes><![CDATA[\nRelease notes are available in our blog https://www.hangfire.io/blog/\nPlease see https://docs.hangfire.io/en/latest/upgrade-guides/upgrading-to-hangfire-1.8.html to learn how to upgrade.\n\n1.8.22\n• Fixed – `InvalidCastException` when creating a background job with Schema 5 (regression from 1.8.15).\n• Project – Replace the `netcoreapp3.1` target with the `net8.0` one in tests.\n\n1.8.21\n• Added – `SqlServerStorageOptions.DisableTransactionScope` option for .NET Framework targets.\n• Project – Port Monitoring API tests from the Hangfire.InMemory storage for better coverage.\n• Project – Run tests for different targets in parallel with different databases.\n\n1.8.19\n• Fixed – Sliding invisibility timeout isn't prolonged in lightweight servers, causing jobs to be restarted.\n\n1.8.17\n• Fixed – `InvalidCastException` while fetching a job with older schemas regression from 1.8.16.\n\n1.8.16\n• Changed – Use vanilla ADO.NET when fetching a job in the `SqlServerJobQueue` implementation.\n• Fixed – SqlException: Must declare the scalar variable \"@key\" in delayed and recurring job schedulers.\n• Fixed – Decrease the `LockTimeout` time when calling the `sp_getapplock` procedure to 1 second for less blocking.\n• Project – Disable parallel tests execution when building under .NET 9.0.\n• Project – Run tests over the latest Microsoft.Data.SqlClient package and the `net6.0` platform.\n• Project – Reduce execution time of integration tests.\n• Project – Disable `PoolBlockingPeriod` setting on AppVeyor to handle transient test failures.\n\n1.8.15\n• Changed – Use query template caching based on schema name to avoid excessive `string` allocations.\n• Changed – Use static callbacks almost anywhere to avoid unnecessary delegate allocations.\n• Changed – Use `QuerySingle`* or `ReadSingle`* where possible to avoid allocating lists.\n• Changed – Unify `DbCommand` and `DbParameter` creation logic to improve code consistency.\n\n1.8.13 and 1.8.14\n• Changed – Limit polling queries when queues are empty with a semaphore for all configurations.\n• Changed – Use per-queue signaling for same-process workers, instead of having a global signal.\n• Fixed – Don't silently truncate queue names, throw an exception instead.\n• Project – Decrease delays in SQL Server-related tests to complete them faster.\n\n1.8.12\n• Fixed – Populate `InvocationData` and `LoadException` properties in `JobDetails` method results.\n\n1.8.10\n• Changed – Bump Dapper for the `netstandard2.0` platform to version 2.1.28.\n• Changed – Bump Dapper for `net451` and `netstandard1.3` platforms to version 1.60.6.\n• Project – Enable NuGet package and DLL signing with a company certificate.\n• Project – Require NuGet package signature validation on restore for dependencies.\n• Project – Add `HangfireIO` as a package owner.\n      \n1.8.9\n• Project – Enable full source link support with embedded symbols and repository-based sources.\n• Project – Enable repeatable package restore using a lock file.\n• Project – Run unit tests against the `net6.0` platform.\n\n1.8.7\n• Changed – Avoid throwing an exception when a connection string has duplicate property names.\n• Project – Disable tests for `netcoreapp1.0` and `netcoreapp2.1` targets since they aren't supported in AppVeyor.\n• Project – Add a `net6.0` target for unit tests instead of the removed ones.\n• Project – Modernise projects and build environments to use the newest features.\n\n1.8.6\n• Fixed – Exception in Dashboard UI when schema version is not present in a database.\n• Fixed – `DbCommand` resource leak when releasing a lock detected by static analysis.\n• Fixed – Don't add SQL Server-related metrics multiple times in Dashboard UI.\n\n1.8.5\n• Fixed – \"Query processor could not produce a query plan\" when removing expired counters in `Schema 5`.\n\n1.8.2\n• Fixed – `InvalidOperationException` with new dashboard metrics when a database has multiple data/log files.\n\n1.8.1\n• Fixed – Blocked workers regression since 1.7.33 when using multiple servers inside a process.\n• Fixed – Target schema version is less than the current schema version error.\n• Fixed – Implement database metrics without the need for additional permissions.\n• Fixed – Use the `forceseek` table hint whenever possible to avoid performance drops.\n\n1.8.0\n• Breaking – Prioritise Microsoft.Data.SqlClient package over System.Data.SqlClient one.\n• Breaking – Dropped the `NET45` platform target in favor of the `NET451` target to support Visual Studio 2022.\n• Added – `Schema 8` migration with fixed `JobQueue.Id` column to use the `bigint` type.\n• Added – `Schema 9` migration that creates an index for the `State.CreatedAt` column.\n• Added – Automatic client package detection based on available types, preferring `System.Data.SqlClient` (by @0xced).\n• Added – `SqlServerStorageOptions.DbProviderFactory` option to use a custom provider factory.\n• Added – Clean up of old state entries of a non-finished job when `InactiveStateExpirationTimeout` is set.\n• Added – `TryAutoDetectSchemaDependentOptions` option to automatically enable options based on the schema.\n• Added – Optional experimental transactional acknowledge for SQL Server (`UseTransactionalAcknowledge` option).\n• Added – Implement the `Connection.GetUtcDateTime` feature to make work the new changes in schedulers.\n• Added – `SqlServerStorage.SchemaVersion` metric for Dashboard UI.\n• Added – `DefaultQueueProvider` option to specify a custom default queue provider.\n• Changed – Remove dependency on System.Data.SqlClient for Hangfire.SqlServer (by @0xced).\n• Changed – Set default value for the `QueuePollInterval` option to `TimeSpan.Zero`.\n• Changed – Polling delay when `QueuePollInterval` is set to zero now defaults to 200 ms.\n• Changed – Sliding invisibility timeout-based fetching method is now used by default with a 5-minute timeout.\n• Changed – Use command batching by default with a 5-minute maximum timeout.\n• Changed – Enable the `UseRecommendedIsolationLevel` option by default.\n• Changed – `GetJobData` now populates the `JobData.ParametersSnapshot` property to avoid additional roundtrips.\n• Changed – Display scheduled and processing jobs in ascending order in Dashboard UI.\n• Changed – Implement the `Transaction.AcquireDistributedLock` feature.\n• Changed – Implement the `GetSetCount.Limited feature`.\n• Changed – Implement the `GetSetContains feature`.\n• Changed – Bump the internal version of Dapper to 2.0.123.\n• Changed – Enable common metrics for SQL Server storage to be shown by default.\n• Changed – Enable the `Monitoring.AwaitingJobs` feature for SQL storage.\n• Deprecated – `UsePageLocksOnDequeue` option is now obsolete and doesn't affect anything.\n]]>\n    </releaseNotes>\n    <dependencies>\n      <group targetFramework=\"net451\">\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n      </group>\n      \n      <group targetFramework=\"netstandard1.3\">\n        <dependency id=\"NETStandard.Library\" version=\"1.6.0\" />\n        <dependency id=\"System.Data.Common\" version=\"4.1.0\" />\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n      </group>\n\n      <group targetFramework=\"netstandard2.0\">\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n      </group>\n    </dependencies>\n  </metadata>\n  <files>\n    <file src=\"net451\\Hangfire.SqlServer.dll\" target=\"lib\\net451\" />\n    <file src=\"net451\\Hangfire.SqlServer.xml\" target=\"lib\\net451\" />\n    \n    <file src=\"netstandard1.3\\Hangfire.SqlServer.dll\" target=\"lib\\netstandard1.3\" />\n    <file src=\"netstandard1.3\\Hangfire.SqlServer.xml\" target=\"lib\\netstandard1.3\" />\n\n    <file src=\"netstandard2.0\\Hangfire.SqlServer.dll\" target=\"lib\\netstandard2.0\" />\n    <file src=\"netstandard2.0\\Hangfire.SqlServer.xml\" target=\"lib\\netstandard2.0\" />\n    \n    <file src=\"Tools\\DefaultInstall.sql\" target=\"tools\\install.sql\" />\n\n    <file src=\"..\\nuspecs\\icon.png\" />\n\n    <file src=\"LICENSE.md\" />\n    <file src=\"COPYING\" />\n    <file src=\"COPYING.LESSER\" />\n    <file src=\"NOTICES\" />\n    <file src=\"LICENSE_STANDARD\" />\n    <file src=\"LICENSE_ROYALTYFREE\" />\n  </files>\n</package>"
  },
  {
    "path": "nuspecs/Hangfire.nuspec",
    "content": "<?xml version=\"1.0\"?>\n<package>\n  <metadata>\n    <id>Hangfire</id>\n    <version>%version%</version>\n    <title>Hangfire</title>\n    <authors>Sergey Odinokov</authors>\n    <owners>HangfireIO, odinserj</owners>\n    <projectUrl>https://www.hangfire.io/</projectUrl>\n    <repository type=\"git\" url=\"https://github.com/HangfireIO/Hangfire.git\" commit=\"%commit%\" />\n    <license type=\"file\">LICENSE.md</license>\n    <icon>icon.png</icon>\n    <readme>README.md</readme>\n    <summary>An easy way to perform fire-and-forget, delayed and recurring tasks inside ASP.NET applications. No Windows Service required.</summary>\n    <description>\nAn easy and reliable way to perform fire-and-forget, delayed and recurring, long-running, short-running, CPU or I/O intensive tasks inside ASP.NET applications. No Windows Service / Task Scheduler required. Even ASP.NET is not required.\nBacked by Redis, SQL Server, SQL Azure or MSMQ. This is a .NET alternative to Sidekiq, Resque and Celery.\nhttps://www.hangfire.io/\n    </description>\n    <copyright>Copyright © 2013-2026 Hangfire OÜ</copyright>\n    <tags>Hangfire AspNet MVC AspNetCore NetCore SqlServer Long-Running Background Fire-And-Forget Delayed Recurring Tasks Jobs Scheduler Threading Queues</tags>\n    <releaseNotes><![CDATA[\nRelease notes are available in our blog https://www.hangfire.io/blog/\nPlease see https://docs.hangfire.io/en/latest/upgrade-guides/upgrading-to-hangfire-1.8.html to learn how to upgrade.\n\n1.8.23\n\nHangfire.Core\n\n• Changed – Use stable sorting algorithm for background job filters again (by @jirikanda).\n• Fixed – Add missing keys for Swedish translation (by @karl-sjogren).\n• Fixed – Custom `AutomaticRetryAttribute` is ignored under certain conditions (by @jirikanda).\n• Project – Use `TypeNameAssemblyFormatHandling` in tests with .NET 6 (by @viktor-vintertass).\n\nHangfire.AspNetCore\n\n• Fixed – `InvalidOperationException`: The request reached the end of the pipeline without executing the endpoint.\n\n1.8.22\n\nHangfire.Core\n\n• Added – `IGlobalConfiguration.UseNoOpLogProvider` method to disable logging.\n• Changed – Un-deprecate interval methods in the `Cron` class, add remarks in docs instead.\n• Changed – Bump internalized version of Cronos to 0.11.1.\n• Changed – Bump internalized version of Microsoft.Owin to 4.2.3.\n• Fixed – Serialization of arrays of nested types `SimpleAssemblyTypeSerializer`.\n• Fixed – Remove wrong escaping characters in Portuguese translations on the \"Servers\" page.\n• Fixed – Properly remove registered `IBackgroundProcessingServer` instances on OWIN app shutdown.\n• Fixed – `AspNetShutdownDetector` for early ASP.NET shutdown detection is not working (regression from 1.7.30).\n• Project – Replace the `netcoreapp3.1` target with the `net8.0` one in tests.\n\nHangfire.SqlServer\n\n• Fixed – `InvalidCastException` when creating a background job with Schema 5 (regression from 1.8.15).\n• Project – Replace the `netcoreapp3.1` target with the `net8.0` one in tests.\n\nHangfire.AspNetCore\n\n• Added – `MapHangfireDashboardWithNoAuthorizationFilters` method, which does not include local-only filters.\n• Changed – Set 404 status code when `MapHangfireDashboard` is used and no dispatcher is found.\n• Fixed – `InvalidOperationException` upon receiving a request for 'hangfire/bootstrap.min.css.map'.\n\n1.8.21\n\nHangfire.Core\n\n• Added – `FailedState.IncludeFileInfo` to optionally show/hide line numbers in exceptions in Failed state.\n• Changed – Include line numbers for exceptions by default when available.\n• Fixed – Portuguese (Brazil) translations in Strings.pt-BR.resx (by @pedro-cons).\n• Fixed – Static `BackgroundJob` class always acquires the most current `JobStorage.Current` instance.\n• Fixed – Static `RecurringJob` class always acquires the most current `JobStorage.Current` instance.\n\nHangfire.SqlServer\n\n• Added – `SqlServerStorageOptions.DisableTransactionScope` option for .NET Framework targets.\n• Project – Port Monitoring API tests from the Hangfire.InMemory storage for better coverage.\n• Project – Run tests for different targets in parallel with different databases.\n\n1.8.20\n\nHangfire.Core\n\n• Fixed – Glyphicons from Bootstrap are not displaying after upgrading to version 1.8.19.\n\n1.8.19\n\nHangfire.Core\n\n• Changed – Update Bootstrap to the custom version of 3.4.2 to avoid false alerts on unused features.\n• Fixed – Typos in Portuguese translation (by @VianaArthur).\n• Fixed – Unnecessary recurring job update transaction when nothing is changed after an error.\n\nHangfire.SqlServer\n\n• Fixed – Sliding invisibility timeout isn't prolonged in lightweight servers, causing jobs to be restarted.\n\n1.8.18\n\nHangfire.Core\n\n• Added – `DashboardOptions.ServerPossiblyAbortedThreshold` to configure a custom threshold for \"possibly aborted\" warnings.\n• Fixed – Expired jobs are still shown on the \"Retries\" page in some cases.\n• Fixed – Issues with `CultureInfo`-related differences after upgrading to 1.8.15–1.8.17.\n• Fixed – Don't leak `AsyncLocal` values from synchronous background job methods.\n• Fixed – Don't throw an exception when passing the `Job.Args` property to the `Job` class' constructor.\n• Project – Make the lock file usable for both .NET 8.0 and .NET 9.0 builds.\n• Project – Make code generation for `cshtml` files working on newer platforms.\n\nHangfire.AspNetCore\n\n• Fixed – Swallow possible `ObjectDisposedException` in the `StopAsync` method.\n• Fixed – Avoid `NullReferenceException` when `LocalIpAddress` or `RemoteIpAddress` is null.\n\n1.8.17\n\nHangfire.SqlServer\n\n• Fixed – `InvalidCastException` while fetching a job with older schemas regression from 1.8.16.\n\n1.8.16\n\nHangfire.Core\n\n• Changed – Include fewer stack frames in exceptions come from `IServerFilter` implementations.\n• Changed – Don't include file information in the `ExceptionDetails` property of a FailedState instance.\n• Changed – Switch back to `CancellationEvent` usage instead of `CancellationToken.WaitHandle`.\n• Fixed – Don't commit external transaction in the `BackgroundJobStateChanger` implementation.\n• Fixed – Use safe default serializer settings for Newtonsoft.Json 12.X and below.\n• Project – Fix builds for the `net451` platform when using .NET 9.0.\n• Project – Significantly reduce execution time of unit tests in the `RecurringJobSchedulerFacts` class.\n• Project – Bump `Microsoft.CodeAnalysis.NetAnalyzers` package to version 9.0.0.\n\nHangfire.SqlServer\n\n• Changed – Use vanilla ADO.NET when fetching a job in the `SqlServerJobQueue` implementation.\n• Fixed – SqlException: Must declare the scalar variable \"@key\" in delayed and recurring job schedulers.\n• Fixed – Decrease the `LockTimeout` time when calling the `sp_getapplock` procedure to 1 second for less blocking.\n• Project – Disable parallel tests execution when building under .NET 9.0.\n• Project – Run tests over the latest Microsoft.Data.SqlClient package and the `net6.0` platform.\n• Project – Reduce execution time of integration tests.\n• Project – Disable `PoolBlockingPeriod` setting on AppVeyor to handle transient test failures.\n\n1.8.15\n\nHangfire.Core\n\n• Added – New `AutomaticRetryAttribute.ExceptOn` property to skip retries for specific exceptions.\n• Changed – Refactor filters pipeline to use less LINQ magic and fewer allocations.\n• Changed – Use `GetCultureInfo` instead of creating an instance in the `CaptureCultureAttribute` filter.\n• Changed – Cache some immutable data to avoid extra allocations.\n• Fixed – Improve loopback address detection (by @meziantou).\n• Fixed – Reformulate misleading error messages regarding retry timings (by @RGFuaWVs).\n• Fixed – Problem with missing localizations in the previous version.\n• Fixed – Don't hide exception details on Failed Jobs page when the exception message is empty.\n• Fixed – Problems with the first restore when using the `build.bat` command.\n• Fixed – Better display of canceled recurring jobs in dashboard.\n• Fixed – Less overall allocations with using static delegates and struct-based iterators.\n• Fixed – Improve precision of some diagnostic messages in the wait protection logic.\n• Fixed – Make all private and internal classes sealed to improve code consistency.\n• Fixed – Less overall pressure on garbage collector.\n\nHangfire.SqlServer\n\n• Changed – Use query template caching based on schema name to avoid excessive `string` allocations.\n• Changed – Use static callbacks almost anywhere to avoid unnecessary delegate allocations.\n• Changed – Use `QuerySingle`* or `ReadSingle`* where possible to avoid allocating lists.\n• Changed – Unify `DbCommand` and `DbParameter` creation logic to improve code consistency.\n\n1.8.13 and 1.8.14\n\nHangfire.Core\n\n• Changed – Partial cache for serialization and deserialization in `InvocationData` to produce less strings.\n• Changed – Add caching for default type serializer and resolver.\n• Changed – Don't let `JobFilter`-related logic to show up in profilers.\n• Changed – Modify `IProfiler` to be less allocatey for diagnostic purposes that almost never run.\n• Changed – Prefer using `CancellationToken.WaitHandle` again, since early .NET Core days are gone.\n• Changed – Fewer allocations when working with `IStateHandler` collections in a state machine.\n• Fixed – Redirect the \"System.Private.Xml.Linq\" assembly to the \"System.Xml.Linq\" one for better interoperability.\n• Fixed – Don't throw `KeyNotFoundException` when recurring job is malformed.\n• Fixed – Proper relative path calculation in `UrlHelper.To` for OWIN-based Dashboard UI (by @LordJZ).\n• Fixed – Typo in the Turkish localization file (by @ismkdc).\n• Project – Switch to a modern PowerShell 7+ to speed up SignPath installation on AppVeyor.\n\nHangfire.SqlServer\n\n• Changed – Limit polling queries when queues are empty with a semaphore for all configurations.\n• Changed – Use per-queue signaling for same-process workers, instead of having a global signal.\n• Fixed – Don't silently truncate queue names, throw an exception instead.\n• Project – Decrease delays in SQL Server-related tests to complete them faster.\n\n1.8.12\n\nHangfire.Core\n\n• Added – `MaxDegreeOfParallelismForSchedulers` experimental server option if supported by storage.\n• Added – Experimental support for parallel execution of the delayed job scheduler.\n• Added – Experimental support for parallel execution of the recurring job scheduler.\n• Fixed – Recurring job is scheduled to the past after recovering from error with `AddOrUpdate`.\n• Fixed – `AddOrUpdate` triggers execution of a recurring job, even if its next execution is in the future.\n• Fixed – Two very minor errors in the Swedish localization file (by @Uglack).\n\nHangfire.SqlServer\n\n• Fixed – Populate `InvocationData` and `LoadException` properties in `JobDetails` method results.\n\n1.8.11\n\nHangfire.Core\n\n• Changed – Add icons and fix metadata for NuGet packages.\n• Changed – Bump ILRepack to version 2.0.27 to avoid problems with internalizing.\n• Fixed – \"Type exists in both Cronos and Hangfire.Core\" exception.\n\n1.8.10\n\nHangfire.Core\n\n• Changed – Added Norwegian translations for new keys (by @khellang).\n• Changed – Update Brazilian Portuguese translation (by @HugoAlames).\n• Changed – Bump Cronos dependency to version 0.8.3.\n\nHangfire.AspNetCore\n\n• Fixed – Don't check `HasStarted` in `Response.WriteAsync` to avoid breaking dispatchers.\n\nHangfire.SqlServer\n\n• Changed – Bump Dapper for the `netstandard2.0` platform to version 2.1.28.\n• Changed – Bump Dapper for `net451` and `netstandard1.3` platforms to version 1.60.6.\n\nHangfire.Core, Hangfire.NetCore, Hangfire.AspNetCore, Hangfire.SqlServer, Hangfire.SqlServer.Msmq\n\n• Project – Enable NuGet package and DLL signing with a company certificate.\n• Project – Require NuGet package signature validation on restore for dependencies.\n• Project – Add `HangfireIO` as a package owner.\n\n1.8.9\n\nHangfire.Core\n\n• Changed – Use `Environment.MachineName` as a server name if other environment vars aren't available.\n• Changed – Bump the Cronos package version from 0.7.1 to 0.8.1.\n• Changed – Improve portuguese translations (by @filipe-silva).\n• Fixed – Possible `NullReferenceException` on the Deleted Jobs page (regression from 1.8.7).\n• Project – Enable full source link support with embedded symbols and repository-based sources.\n• Project – Enable repeatable package restore using a lock file.\n• Project – Run unit tests against the `net6.0` platform.\n• Project – Modernise the build system and clean up the build scripts.\n\nHangfire.SqlServer\n\n• Project – Enable full source link support with embedded symbols and repository-based sources.\n• Project – Enable repeatable package restore using a lock file.\n• Project – Run unit tests against the `net6.0` platform.\n\nHangfire.NetCore\n\n• Project – Enable full source link support with embedded symbols and repository-based sources.\n• Project – Enable repeatable package restore using a lock file.\n\nHangfire.AspNetCore\n\n• Fixed – Don't attempt to write response headers when response has already started (by @maliming).\n• Project – Enable full source link support with embedded symbols and repository-based sources.\n• Project – Enable repeatable package restore using a lock file.\n\n1.8.7\n\nHangfire.Core\n\n• Added – Allow using macro expressions like `@hourly` for recurring jobs (by @MuhamedAbdalla).\n• Added – Show storage time in page footer when supported by storage implementation.\n• Added – Show duration and latency columns separately on the Succeeded Jobs page when supported.\n• Added – Show the exception column on the Deleted Jobs page when available and supported by storage.\n• Changed – Reduce package size by stripping unnecessary locales in Moment.js.\n• Changed – Bump Microsoft.Owin package to version 4.2.2.\n• Changed – Log a warning message when a server listens to unsupported queue names (by @MuhamedAbdalla).\n• Changed – Use storage time, if available, to show delay warnings in the Dashboard UI.\n• Fixed – Proper rendering of generic arguments on the Job Details page (by @olivermue).\n• Fixed – Language inconsistency in the Dashboard UI related to date/time description.\n• Fixed – Big stack traces take too long time to be formatted.\n• Fixed – Don't throw `NullReferenceException` from the Scheduled Jobs page when there's a job with missing data.\n• Fixed – Don't throw `NullReferenceException` from the Processing Jobs page when there's a job with missing data.\n• Fixed – CSS for Enqueued and Deleted state cards in dark theme.\n• Fixed – Log errors instead of throwing an exception when a particular table can't be cleaned.\n• Fixed – Avoid logging fatal exceptions when stopping a faulting background process.\n• Fixed – Don't display checkboxes in the Dashboard UI when job details can’t be fetched.\n• Fixed – Scrollbars in WebKit-based browsers are now dark in dark mode.\n• Project – Disable tests for `netcoreapp1.0` and `netcoreapp2.1` targets since they aren't supported in AppVeyor.\n• Project – Add a `net6.0` target for unit tests instead of the removed ones.\n• Project – Modernise projects and build environments to use the newest features.\n\nHangfire.SqlServer\n\n• Changed – Avoid throwing an exception when a connection string has duplicate property names.\n• Project – Disable tests for `netcoreapp1.0` and `netcoreapp2.1` targets since they aren't supported in AppVeyor.\n• Project – Add a `net6.0` target for unit tests instead of the removed ones.\n• Project – Modernise projects and build environments to use the newest features.\n\n1.8.6\n\n• Changed – Update jQuery library in Dashboard UI to version 3.7.1.\n• Changed – Mark all types in Hangfire.Annotations with `EditorBrowsableAttribute(Never)`.\n• Changed – Change state card colors for the Awaiting state to match the Scheduled state.\n• Fixed – Exception when deserializing an instance of the `AutomaticRetryAttribute` class from JSON.\n• Fixed – Add serialization-related constructors for all the exception classes.\n• Fixed – Use invariant culture or ordinal comparisons for internal strings.\n• Fixed – Use invariant culture when formatting key names for metrics.\n• Fixed – Use `CurrentCulture` instead of `CurrentUICulture` when displaying time.\n• Project – Enable running static analysis by Coverity Scan weekly.\n• Project – Enable mandatory static analysis by the Microsoft.CodeAnalysis.NetAnalyzers package.\n• Project – Change MSBuild path when building using newer .NET SDKs for Razor views.\n        \nHangfire.SqlServer\n\n• Fixed – Exception in Dashboard UI when schema version is not present in a database.\n• Fixed – `DbCommand` resource leak when releasing a lock detected by static analysis.\n• Fixed – Don't add SQL Server-related metrics multiple times in Dashboard UI.\n\nHangfire.NetCore\n\n• Fixed – Include assembly information to the Hangfire.NetCore assembly.\n\n1.8.5\n\nHangfire.Core\n\n• Added – Possibility to inform a `FaviconPath` on `DashboardOptions` (by @cezar-pimentel).\n• Fixed – Inability to restore a disabled recurring job, regression in version 1.8.3.\n• Fixed – Make it possible to serialize the `AutomaticRetryAttribute` filter to JSON.\n\nHangfire.SqlServer\n\n• Fixed – \"Query processor could not produce a query plan\" when removing expired counters in `Schema 5`.\n\n1.8.4\n\nHangfire.Core\n\n• Added – Pass server id from a worker to the `PerformContext.ServerId` property available in filters.\n• Fixed – Send heartbeats until full background processing server shutdown.\n      \nHangfire.NetCore\n\n• Changed – Send the stop signal earlier in the shutdown pipeline when hosting in .NET Core 3.1 or higher.\n• Changed – Set processing server to null in hosted service to avoid `ObjectDisposedException`.\n• Fixed – Other `IHostedService` implementations can block Hangfire server from being stopped.\n\n1.8.3\n\nHangfire.Core\n\n• Changed – Allow to configure `MaxLinesInStackTrace` for a particular `FailedState` instance.\n• Fixed – Remove job id from schedule when it's not in the Scheduled state for some reason.\n• Fixed – Missing invocations of recurring jobs when the new \"Ignorable\" option is used.\n• Fixed – Make `DisableConcurrentExecutionAttribute` and `LatencyTimeoutAttribute` serializable.\n\n1.8.2\n\nHangfire.Core\n\n• Changed – Disable transactional job creation feature appeared in 1.8.0.\n• Fixed – \"Can not start continuation XXX\" error when storage supports transactional job creation.\n\nHangfire.SqlServer\n\n• Fixed – `InvalidOperationException` with new dashboard metrics when a database has multiple data/log files.\n\n1.8.1\n      \nHangfire.Core\n\n• Added – `MisfireHandlingMode.Ignorable` to avoid scheduling recurring jobs on missed schedules.\n• Added – Support disabling dark mode via the `DashboardOptions.DarkModeEnabled` property.\n• Changed – Remove the 1-hour limitation for the `WithJobExpirationTimeout` configuration method.\n• Fixed – Add missing `UseDefaultCulture` configuration method overloads.\n• Fixed – Add missing `UseDashboardStylesheet` and `UseJobDetailsRenderer` configuration methods.\n• Fixed – Give even more space for identifiers on the Recurring Jobs page.\n• Fixed – `state-card-state-active` color is not very dark (by @coolhome).\n• Fixed – Slightly change chart proportions to fit 4K in Dashboard UI.\n\nHangfire.SqlServer\n\n• Fixed – Blocked workers regression since 1.7.33 when using multiple servers inside a process.\n• Fixed – Target schema version is less than the current schema version error.\n• Fixed – Implement database metrics without the need for additional permissions.\n• Fixed – Use the `forceseek` table hint whenever possible to avoid performance drops.\n      \nHangfire.NetCore\n\n• Fixed – Add `net461` target for Hangfire.NetCore package to avoid missing method exceptions.\n\n1.8.0\n\nHangfire.Core\n\n• Breaking – Dropped the `NET45` platform target in favor of the `NET451` target to support Visual Studio 2022.\n\n• Added – Introduce the `Job.Queue` property, so jobs now can have their own queue specified.\n• Added – Method overloads to create background jobs directly with a custom default queue.\n• Added – Method overloads to create recurring jobs directly with a custom default queue.\n• Added – `IBackgroundJobClient.Create` method overloads with the new `queue` parameter.\n• Added – Allow to filter exception types in `AutomaticRetryAttribute` by using the new `OnlyOn` property.\n• Added – `DeletedState` now has the persisted `Exception` property populated after a failure.\n• Added – `JobContinuationOptions.OnlyOnDeletedState` to create continuations after a failure.\n• Added – `Exception` job parameter is passed to continuation when `UseResultsInContinuations` method is used.\n• Added – `FromExceptionAttribute` to deal with an antecedent exception in a background job continuation.\n• Added – Make it possible to specify multiple `JobContinuationOptions` values for a continuation.\n• Added – `BackgroundJobServerOptions.IsLightweightServer` option to run a server with no storage processes.\n• Added – Ability to use custom formattable resource identifiers for the `DisableConcurrentExecution` filter.\n• Added – Pass `ServerId` to `FailedState` instances to simplify the debugging on different servers.\n• Added – Allow to pass job parameters when creating a job (by @brian-knoll-micronetonline).\n• Added – `MisfireHandlingMode.Strict` to create a job for each missed recurring job occurrence.\n• Added – Support for default culture and UI culture via the `UseDefaultCulture` configuration method.\n• Added – Introduce the `captureDefault` parameter in the `CaptureCulture` filter.\n• Added – `IGlobalConfiguration.UseFilterProvider` extension method to unify the configuration.\n• Added – Built-in `Remove` method for `JobFilterCollection` to remove global filters based on their type.\n• Added – `CompatibilityLevel.Version_180` flag to avoid storing culture parameters when they are the same as the default ones.\n• Changed – Create job atomically when `Transaction.CreateJob` feature is supported by the storage.\n• Changed – Query time from storage in recurring and delayed schedulers when supported by storage.\n• Changed – Move job to the `DeletedState` instead of `SucceededState` when its invocation was canceled by a filter.\n• Changed – Speedup delayed jobs when a custom default queue is specified by avoiding extra state transition.\n• Changed – Use UI culture from `CurrentCulture` parameter when `CurrentUICulture` one is missing.\n• Changed – Increase the default value for the `BackgroundJobServerOptions.StopTimeout` to 500 ms.\n• Deprecated – `AddOrUpdate` overloads with optional params defined in the `RecurringJobManagerExtensions` class.\n• Deprecated – `AddOrUpdate` overloads with optional parameters defined in the `RecurringJob` class.\n• Deprecated – `AddOrUpdate` method overloads with no `recurringJobId` parameter.\n• Deprecated – `RecurringJobOptions.QueueName` property, new methods should be used instead.\n• Breaking – Dropped `NET45` platform target in favor of `NET451` target to support Visual Studio 2022.\n\nDashboard UI\n• Added – Dark mode support for Dashboard UI depending on the system settings (by @danillewin).\n• Added – Dashboard UI now has a full-width layout to display more data (by @danillewin).\n• Added – Allow to add custom JavaScript and CSS files to the Dashboard UI via the `DashboardRoutes` class.\n• Added – `DefaultRecordsPerPage` property on the `DashboardOptions` class (by @PaulARoy).\n• Added – `IGlobalConfiguration.UseJobDetailsRenderer` method for custom renderers for the Job Details page.\n• Added – Display deleted jobs in the Realtime and History graphs when supported by storage.\n• Added – `IGlobalConfiguration.UseDashboardMetrics` extension method to pass multiple metrics at once.\n• Added – State renderer for the `DeletedState` to display its new exception property.\n• Added – Support for new `MonitoringApi` methods for the Awaiting Jobs page.\n• Changed – Make it possible to display methods of non-loaded jobs in the Dashboard UI when supported by storage.\n• Changed – Improved display of realtime chart with more accents on failed and deleted jobs.\n• Changed – Don't display the queue name in the state transition list when it's the `default` one.\n• Changed – Display scheduled job count when the enqueued count is zero on the main metric.\n\nExtensibility\n• Added – `Factory`, `StateMachine`, and `Performer` properties to context classes to avoid injecting services.\n• Added – Allow to pass custom data to `ApplyStateContext` and `ElectStateContext` instances.\n• Added – Preserve custom data dictionary between the entire filter chain.\n• Added – Allow to pass a transaction to background job state changer when new methods are implemented.\n• Changed – Ignore some members when serializing a `JobFilterAttribute` instance to decrease the payload size.\n\nStorage\n• Added – Virtual `JobStorage.GetReadOnlyConnection` method intended to return `JobStorageConnection` for replicas.\n• Added – Virtual `JobStorage.HasFeature` method for querying optional features.\n• Added – The `JobStorageFeatures` class to avoid using magic strings in storage features.\n• Added – Optional `GetSetCount`, `GetSetContains`, and `GetUtcDateTime` methods for the `JobStorageConnection` class.\n• Added – Optional `AcquireDistributedLock` and `RemoveFromQueue` methods for the `JobStorageTransaction` class.\n• Added – Optional `CreateJob` and `SetJobParameter` methods for the `JobStorageTransaction` class.\n• Added – Optional `ParametersSnapshot` property for `BackgroundJob` and `JobData` classes to minimize roundtrips in the future.\n• Added – Support for transactional acknowledgment using a new storage method for better handling some data loss scenarios.\n• Added – Fetch `Retries` and `Awaiting` metrics in `StatisticsDto` properties when supported by storage.\n• Added – The `JobStorageMonitor` class with more available methods for the new features.\n• Changed – Allow to query job parameters without additional roundtrip when supported by storage.\n• Changed – Expose state data dictionaries in list DTOs when supported by storage.\n• Changed – Rely on storage indexing with the `Monitoring.AwaitingJobs` feature.\n\nInternals\n• Added – `IBackgroundProcess.UseBackgroundPool` now allows to pass thread configuration logic.\n• Added – `BackgroundJobServerOptions.WorkerThreadConfigurationAction` option for custom thread configuration.\n• Changed – Allow changing queues on the fly with custom worker configuration.\n• Changed – Avoid storage roundtrip to query job data in worker, take data from previous state change.\n• Changed – `FromParameterAttribute`-based logic now always overwrites arguments, even with non-null values.\n• Changed – Turn the `JobContinuationOptions` enum into flags while still possible.\n• Changed – Re-implement `TaskExtensions.WaitOneAsync` only with the `RegisterWaitForSingleObject` method.\n• Changed – `ServerHeartbeatProcess` now uses `ThreadPriority.AboveNormal` to prioritize heartbeats.\n\nHangfire.NetCore\n\n• Added – `IApplicationBuilder.UseHangfireServer` that accepts custom factory for `IBackgroundProcessingServer`.\n• Added – `net451` and `netstandard1.3` targets for the package.\n• Changed – Use `netstandard2.1` target instead of `netcoreapp3.0` for the package.\n• Changed – Send the \"stop\" signal earlier when the host supports .NET Standard 2.1.\n• Changed – Don't throw `ObjectDisposedException` when hosted service is disposed twice.\n\nHangfire.AspNetCore\n\n• Breaking – Make the package to be dependent on Hangfire.NetCore and use the same types.\n• Added – `IApplicationBuilder.UseHangfireServer` that accepts custom factory for `IBackgroundProcessingServer`.\n \nHangfire.SqlServer\n\n• Breaking – Prioritise Microsoft.Data.SqlClient package over System.Data.SqlClient one.\n• Breaking – Dropped the `NET45` platform target in favor of the `NET451` target to support Visual Studio 2022.\n\n• Added – `Schema 8` migration with fixed `JobQueue.Id` column to use the `bigint` type.\n• Added – `Schema 9` migration that creates an index for the `State.CreatedAt` column.\n• Added – Automatic client package detection based on available types, preferring `System.Data.SqlClient` (by @0xced).\n• Added – `SqlServerStorageOptions.DbProviderFactory` option to use a custom provider factory.\n• Added – Clean up of old state entries of a non-finished job when `InactiveStateExpirationTimeout` is set.\n• Added – `TryAutoDetectSchemaDependentOptions` option to automatically enable options based on the schema.\n• Added – Optional experimental transactional acknowledge for SQL Server (`UseTransactionalAcknowledge` option).\n• Added – Implement the `Connection.GetUtcDateTime` feature to make work the new changes in schedulers.\n• Added – `SqlServerStorage.SchemaVersion` metric for Dashboard UI.\n• Added – `DefaultQueueProvider` option to specify a custom default queue provider.\n• Changed – Remove dependency on System.Data.SqlClient for Hangfire.SqlServer (by @0xced).\n• Changed – Set default value for the `QueuePollInterval` option to `TimeSpan.Zero`.\n• Changed – Polling delay when `QueuePollInterval` is set to zero now defaults to 200 ms.\n• Changed – Sliding invisibility timeout-based fetching method is now used by default with a 5-minute timeout.\n• Changed – Use command batching by default with a 5-minute maximum timeout.\n• Changed – Enable the `UseRecommendedIsolationLevel` option by default.\n• Changed – `GetJobData` now populates the `JobData.ParametersSnapshot` property to avoid additional roundtrips.\n• Changed – Display scheduled and processing jobs in ascending order in Dashboard UI.\n• Changed – Implement the `Transaction.AcquireDistributedLock` feature.\n• Changed – Implement the `GetSetCount.Limited feature`.\n• Changed – Implement the `GetSetContains feature`.\n• Changed – Bump the internal version of Dapper to 2.0.123.\n• Changed – Enable common metrics for SQL Server storage to be shown by default.\n• Changed – Enable the `Monitoring.AwaitingJobs` feature for SQL storage.\n• Deprecated – `UsePageLocksOnDequeue` option is now obsolete and doesn't affect anything.\n\nHangfire.SqlServer.Msmq\n\n• Breaking – Dropped the `NET45` platform target in favor of the `NET451` target to support Visual Studio 2022.\n]]>\n    </releaseNotes>\n    <dependencies>\n      <group targetFramework=\"net451\">\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n        <dependency id=\"Hangfire.SqlServer\" version=\"[%version%]\" />\n        <dependency id=\"Microsoft.Owin.Host.SystemWeb\" version=\"3.0.0\" />\n      </group>\n      <group targetFramework=\"netstandard1.3\">\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n        <dependency id=\"Hangfire.SqlServer\" version=\"[%version%]\" />\n        <dependency id=\"Hangfire.AspNetCore\" version=\"[%version%]\" />\n      </group>\n      <group targetFramework=\"netstandard2.0\">\n        <dependency id=\"Hangfire.Core\" version=\"[%version%]\" />\n        <dependency id=\"Hangfire.SqlServer\" version=\"[%version%]\" />\n        <dependency id=\"Hangfire.AspNetCore\" version=\"[%version%]\" />\n      </group>\n    </dependencies>\n  </metadata>\n  <files>\n    <file src=\"..\\nuspecs\\icon.png\" />\n\n    <file src=\"README.md\" />\n    <file src=\"LICENSE.md\" />\n    <file src=\"COPYING\" />\n    <file src=\"COPYING.LESSER\" />\n    <file src=\"NOTICES\" />\n    <file src=\"LICENSE_STANDARD\" />\n    <file src=\"LICENSE_ROYALTYFREE\" />\n  </files>\n</package>\n"
  },
  {
    "path": "psake-project.ps1",
    "content": "Include \"packages\\Hangfire.Build.0.5.0\\tools\\psake-common.ps1\"\n\nTask Default -Depends Pack\n\nTask Merge -Depends Compile -Description \"Run ILRepack /internalize to merge required assemblies.\" {\n    Repack-Assembly @(\"Hangfire.Core\", \"net451\") @(\"Cronos\", \"CronExpressionDescriptor\", \"Microsoft.Owin\")\n    Repack-Assembly @(\"Hangfire.Core\", \"net46\") @(\"Cronos\", \"CronExpressionDescriptor\", \"Microsoft.Owin\")\n    Repack-Assembly @(\"Hangfire.SqlServer\", \"net451\") @(\"Dapper\")\n\n    Repack-Assembly @(\"Hangfire.Core\", \"netstandard1.3\") @(\"Cronos\")\n    Repack-Assembly @(\"Hangfire.Core\", \"netstandard2.0\") @(\"Cronos\")\n    Repack-Assembly @(\"Hangfire.SqlServer\", \"netstandard1.3\") @(\"Dapper\")\n    Repack-Assembly @(\"Hangfire.SqlServer\", \"netstandard2.0\") @(\"Dapper\")\n}\n\nTask Test -Depends Merge -Description \"Run unit and integration tests against merged assemblies.\" {\n    # Dependencies shouldn't be re-built, because we need to run tests against merged assemblies to test\n    # the same assemblies that are distributed to users. Since the `dotnet test` command doesn't support\n    # the `--no-dependencies` command directly, we need to re-build tests themselves first.\n    Exec { ls \"tests\\**\\*.csproj\" | % { dotnet build -c Release --no-restore --no-dependencies $_.FullName } }\n\n    # We are running unit test project one by one, because pipelined version like the line above does not\n    # support halting the whole execution pipeline when \"dotnet test\" command fails due to a failed test,\n    # silently allowing build process to continue its execution even with failed tests.\n    Exec { dotnet test -c Release --no-build \"tests\\Hangfire.Core.Tests\" }\n    Exec { dotnet test -c Release --no-build \"tests\\Hangfire.SqlServer.Tests\" }\n    Exec { dotnet test -c Release --no-build -p:TestTfmsInParallel=false \"tests\\Hangfire.SqlServer.Msmq.Tests\" }\n}\n\nTask Collect -Depends Test -Description \"Copy all artifacts to the build folder.\" {\n    Collect-Assembly \"Hangfire.Core\" \"net451\"\n    Collect-Assembly \"Hangfire.SqlServer\" \"net451\"\n    Collect-Assembly \"Hangfire.SqlServer.Msmq\" \"net451\"\n    Collect-Assembly \"Hangfire.NetCore\" \"net451\"\n    Collect-Assembly \"Hangfire.AspNetCore\" \"net451\"\n\n    Collect-Assembly \"Hangfire.Core\" \"net46\"\n\n    Collect-Assembly \"Hangfire.Core\" \"netstandard1.3\"\n    Collect-Assembly \"Hangfire.SqlServer\" \"netstandard1.3\"\n    Collect-Assembly \"Hangfire.NetCore\" \"netstandard1.3\"\n    Collect-Assembly \"Hangfire.AspNetCore\" \"netstandard1.3\"\n    \n    Collect-Assembly \"Hangfire.Core\" \"netstandard2.0\"\n    Collect-Assembly \"Hangfire.SqlServer\" \"netstandard2.0\"\n    Collect-Assembly \"Hangfire.AspNetCore\" \"netstandard2.0\"\n    Collect-Assembly \"Hangfire.NetCore\" \"netstandard2.0\"\n\n    Collect-Assembly \"Hangfire.NetCore\" \"net461\"\n    Collect-Assembly \"Hangfire.AspNetCore\" \"net461\"\n\n    Collect-Assembly \"Hangfire.AspNetCore\" \"netcoreapp3.0\"\n    Collect-Assembly \"Hangfire.NetCore\" \"netstandard2.1\"\n    \n    Collect-Tool \"src\\Hangfire.SqlServer\\DefaultInstall.sql\"\n\n    Collect-Localizations \"Hangfire.Core\" \"net451\"\n    Collect-Localizations \"Hangfire.Core\" \"net46\"\n    Collect-Localizations \"Hangfire.Core\" \"netstandard1.3\"\n    Collect-Localizations \"Hangfire.Core\" \"netstandard2.0\"\n\n    Collect-File \"README.md\"\n    Collect-File \"LICENSE.md\"\n    Collect-File \"NOTICES\"\n    Collect-File \"COPYING.LESSER\"\n    Collect-File \"COPYING\"\n    Collect-File \"LICENSE_STANDARD\"\n    Collect-File \"LICENSE_ROYALTYFREE\"\n}\n\nTask Pack -Depends Collect -Description \"Create NuGet packages and archive files.\" {\n    $version = Get-PackageVersion\n\n    Create-Package \"Hangfire\" $version\n    Create-Package \"Hangfire.Core\" $version\n    Create-Package \"Hangfire.SqlServer\" $version\n    Create-Package \"Hangfire.SqlServer.Msmq\" $version\n    Create-Package \"Hangfire.AspNetCore\" $version\n    Create-Package \"Hangfire.NetCore\" $version\n\n    Create-Archive \"Hangfire-$version\"\n}\n\nTask Sign -Depends Pack -Description \"Sign artifacts.\" {\n    $version = Get-PackageVersion\n    Sign-ArchiveContents \"Hangfire-$version\" \"hangfire\"\n}\n"
  },
  {
    "path": "samples/ConsoleSample/ConsoleSample.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n  <PropertyGroup>\r\n    <OutputType>Exe</OutputType>\r\n    <TargetFramework>net451</TargetFramework>\r\n    <UseRidGraph>true</UseRidGraph>\r\n    <DebugType>full</DebugType>\r\n  </PropertyGroup>\r\n\r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.Core\\Hangfire.Core.csproj\" />\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.SqlServer\\Hangfire.SqlServer.csproj\" />\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.SqlServer.Msmq\\Hangfire.SqlServer.Msmq.csproj\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <Folder Include=\"Properties\\\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies\" Version=\"1.0.3\" PrivateAssets=\"all\" />\r\n    <PackageReference Include=\"Microsoft.Owin.SelfHost\" Version=\"4.0.0\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.2\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <Reference Include=\"System.Transactions\" />\r\n  </ItemGroup>\r\n\r\n</Project>"
  },
  {
    "path": "samples/ConsoleSample/GenericServices.cs",
    "content": "﻿using System;\n\nnamespace ConsoleSample\n{\n    public class GenericServices<TType>\n    {\n        public void Method<TMethod>(TType arg1, TMethod arg2)\n        {\n            Console.WriteLine($\"Arg1: {arg1}, Arg2: {arg2}\");\n        }\n    }\n}"
  },
  {
    "path": "samples/ConsoleSample/NewFeatures.cs",
    "content": "﻿using System;\nusing Hangfire;\n\nnamespace ConsoleSample\n{\n    public static class NewFeatures\n    {\n        [AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)]\n        public static bool TryExceptional(bool throwException)\n        {\n            if (throwException) throw new InvalidOperationException();\n            return true;\n        }\n\n        public static void Continuation([FromResult] bool result)\n        {\n            Console.WriteLine(\"Success continuation, result: \" + result);\n        }\n\n        public static void CatchExceptional([FromException] ExceptionInfo exception)\n        {\n            if (exception.Type.Contains(\"OperationCanceledException\"))\n            {\n                Console.WriteLine(\"Failure continuation: Operation was canceled\");\n            }\n            else\n            {\n                Console.WriteLine(\"Failure continuation: \" + exception);\n            }\n        }\n\n        public static void FinallyExceptional([FromException] ExceptionInfo exception)\n        {\n            if (exception == null)\n            {\n                Console.WriteLine(\"Finally clause, after success\");\n            }\n            else\n            {\n                Console.WriteLine(\"Finally clause, after failure: \" + exception);\n            }\n        }\n\n        public static void FinallyExceptional2([FromResult] bool result, [FromException] ExceptionInfo exception)\n        {\n            if (exception == null)\n            {\n                Console.WriteLine(\"Finally clause 2, after success: \" + result);\n            }\n            else\n            {\n                Console.WriteLine(\"Finally clause 2, after failure: \" + exception);\n            }\n        }\n\n        public static void Test(bool throwException)\n        {\n            var exceptionalId = BackgroundJob.Enqueue(() => TryExceptional(throwException));\n\n            BackgroundJob.ContinueJobWith(exceptionalId, () => Continuation(default), JobContinuationOptions.OnlyOnSucceededState);\n            BackgroundJob.ContinueJobWith(exceptionalId, () => CatchExceptional(default), JobContinuationOptions.OnlyOnDeletedState);\n            BackgroundJob.ContinueJobWith(exceptionalId, () => FinallyExceptional(default), JobContinuationOptions.OnlyOnSucceededState | JobContinuationOptions.OnlyOnDeletedState);\n            BackgroundJob.ContinueJobWith(exceptionalId, () => FinallyExceptional2(default, default), JobContinuationOptions.OnAnyFinishedState);\n        }\n    }\n}"
  },
  {
    "path": "samples/ConsoleSample/Program.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire;\nusing Hangfire.Common;\nusing Hangfire.Dashboard;\nusing Hangfire.SqlServer;\nusing Hangfire.States;\nusing Microsoft.Owin.Hosting;\n\nnamespace ConsoleSample\n{\n    public static class Program\n    {\n        public static void Main()\n        {\n            GlobalConfiguration.Configuration\n                .UseColouredConsoleLogProvider()\n                .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)\n                .UseSimpleAssemblyNameTypeSerializer()\n                .UseIgnoredAssemblyVersionTypeResolver()\n                .UseRecommendedSerializerSettings()\n                .UseResultsInContinuations()\n                .UseJobDetailsRenderer(10, dto => throw new InvalidOperationException())\n                .UseJobDetailsRenderer(10, dto => new NonEscapedString(\"<h4>Hello, world!</h4>\"))\n                .UseDefaultCulture(CultureInfo.CurrentCulture)\n                .UseSqlServerStorage(@\"Server=.\\;Database=Hangfire.Sample;Trusted_Connection=True;\", new SqlServerStorageOptions\n                {\n                    EnableHeavyMigrations = true,\n                    DisableTransactionScope = true\n                });\n\n            Console.WriteLine(SerializationHelper.Serialize(new ExceptionInfo(new OperationCanceledException()), SerializationOption.Internal));\n\n            var backgroundJobs = new BackgroundJobClient();\n            backgroundJobs.RetryAttempts = 5;\n\n            NewFeatures.Test(throwException: false);\n            NewFeatures.Test(throwException: true);\n\n            var job1 = BackgroundJob.Enqueue<Services>(x => x.WriteIndex(0));\n            var job2 = BackgroundJob.ContinueJobWith<Services>(job1, \"default\",  x => x.WriteIndex(default));\n            var job3 = BackgroundJob.ContinueJobWith<Services>(job2, \"critical\", x => x.WriteIndex(default));\n            var job4 = BackgroundJob.ContinueJobWith<Services>(job3, \"default\",  x => x.WriteIndex(default));\n            var job5 = BackgroundJob.ContinueJobWith<Services>(job4, \"critical\", x => x.WriteIndex(default));\n\n            RecurringJob.AddOrUpdate(\"seconds\", () => Console.WriteLine(\"Hello, seconds!\"), \"*/15 * * * * *\");\n            RecurringJob.AddOrUpdate(\"Console.WriteLine\", () => Console.WriteLine(\"Hello, world!\"), Cron.Minutely);\n            RecurringJob.AddOrUpdate(\"hourly\", () => Console.WriteLine(\"Hello\"), \"25 15 * * *\");\n            RecurringJob.AddOrUpdate(\"neverfires\", () => Console.WriteLine(\"Can only be triggered\"), \"0 0 31 2 *\");\n\n            RecurringJob.AddOrUpdate(\"Hawaiian\", () => Console.WriteLine(\"Hawaiian\"), \"15 08 * * *\", new RecurringJobOptions\n            {\n                TimeZone = TimeZoneInfo.FindSystemTimeZoneById(\"Hawaiian Standard Time\")\n            });\n\n            RecurringJob.AddOrUpdate(\"UTC\", \"critical\", () => Console.WriteLine(\"UTC\"), \"15 18 * * *\");\n\n            RecurringJob.AddOrUpdate(\"Russian\", () => Console.WriteLine(\"Russian\"), \"15 21 * * *\", new RecurringJobOptions\n            {\n                TimeZone = TimeZoneInfo.Local\n            });\n\n            using (WebApp.Start<Startup>(\"http://localhost:12345\"))\n            {\n                var count = 1;\n\n                while (true)\n                {\n                    var command = Console.ReadLine();\n\n                    if (command == null || command.Equals(\"stop\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        break;\n                    }\n\n                    if (command.StartsWith(\"add\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        try\n                        {\n                            var workCount = int.Parse(command.Substring(4));\n                            for (var i = 0; i < workCount; i++)\n                            {\n                                var number = i;\n                                BackgroundJob.Enqueue<Services>(x => x.Random(number));\n                            }\n                            Console.WriteLine(\"Jobs enqueued.\");\n                        }\n                        catch (Exception ex)\n                        {\n                            Console.WriteLine(ex.Message);\n                        }\n                    }\n\n                    if (command.StartsWith(\"async\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        try\n                        {\n                            var workCount = int.Parse(command.Substring(6));\n                            for (var i = 0; i < workCount; i++)\n                            {\n                                BackgroundJob.Enqueue<Services>(x => x.Async(CancellationToken.None));\n                            }\n                            Console.WriteLine(\"Jobs enqueued.\");\n                        }\n                        catch (Exception ex)\n                        {\n                            Console.WriteLine(ex.Message);\n                        }\n                    }\n\n                    if (command.StartsWith(\"static\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        try\n                        {\n                            var workCount = int.Parse(command.Substring(7));\n                            for (var i = 0; i < workCount; i++)\n                            {\n                                BackgroundJob.Enqueue(() => Console.WriteLine(\"Hello, {0}!\", \"world\"));\n                            }\n                            Console.WriteLine(\"Jobs enqueued.\");\n                        }\n                        catch (Exception ex)\n                        {\n                            Console.WriteLine(ex.Message);\n                        }\n                    }\n\n                    if (command.StartsWith(\"error\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        var workCount = int.Parse(command.Substring(6));\n                        for (var i = 0; i < workCount; i++)\n                        {\n                            BackgroundJob.Enqueue<Services>(x => x.Error());\n                        }\n                    }\n\n                    if (command.StartsWith(\"args\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        var workCount = int.Parse(command.Substring(5));\n                        for (var i = 0; i < workCount; i++)\n                        {\n                            BackgroundJob.Enqueue<Services>(x => x.Args(Guid.NewGuid().ToString(), 14442, DateTime.UtcNow));\n                        }\n                    }\n\n                    if (command.StartsWith(\"custom\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        var workCount = int.Parse(command.Substring(7));\n                        for (var i = 0; i < workCount; i++)\n                        {\n                            BackgroundJob.Enqueue<Services>(x => x.Custom(\n                                new Random().Next(),\n                                new[] { \"Hello\", \"world!\" },\n                                new Services.CustomObject { Id = 123 },\n                                DayOfWeek.Friday\n                                ));\n                        }\n                    }\n\n                    if (command.StartsWith(\"fullargs\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        var workCount = int.Parse(command.Substring(9));\n                        for (var i = 0; i < workCount; i++)\n                        {\n                            BackgroundJob.Enqueue<Services>(x => x.FullArgs(\n                                false,\n                                123,\n                                'c',\n                                DayOfWeek.Monday,\n                                \"hello\",\n                                new TimeSpan(12, 13, 14),\n                                new DateTime(2012, 11, 10),\n                                new Services.CustomObject { Id = 123 },\n                                new[] { \"1\", \"2\", \"3\" },\n                                new[] { 4, 5, 6 },\n                                new long[0],\n                                null,\n                                new List<string> { \"7\", \"8\", \"9\" }));\n                        }\n                    }\n\n                    if (command.StartsWith(\"in\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        var seconds = int.Parse(command.Substring(2));\n                        var number = count++;\n                        BackgroundJob.Schedule<Services>(\"default\", x => x.Random(number), TimeSpan.FromSeconds(seconds));\n                    }\n\n                    if (command.StartsWith(\"cancelable\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        var iterations = int.Parse(command.Substring(11));\n                        BackgroundJob.Enqueue<Services>(x => x.Cancelable(iterations, JobCancellationToken.Null));\n                    }\n\n                    if (command.StartsWith(\"delete\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        var workCount = int.Parse(command.Substring(7));\n                        var client = new BackgroundJobClient();\n                        for (var i = 0; i < workCount; i++)\n                        {\n                            client.Create<Services>(x => x.EmptyDefault(), new DeletedState(new ExceptionInfo(new OperationCanceledException())));\n                        }\n                    }\n\n                    if (command.StartsWith(\"fast\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        try\n                        {\n                            var workCount = int.Parse(command.Substring(5));\n                            Parallel.For(0, workCount, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, i =>\n                            {\n                                BackgroundJob.Enqueue<Services>(\n                                    i % 2 == 0 ? \"critical\" : \"default\",\n                                    x => x.EmptyDefault());\n                            });\n                            Console.WriteLine(\"Jobs enqueued.\");\n                        }\n                        catch (Exception ex)\n                        {\n                            Console.WriteLine(ex.Message);\n                        }\n                    }\n\n                    if (command.StartsWith(\"generic\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        BackgroundJob.Enqueue<GenericServices<string>>(x => x.Method(\"hello\", 1));\n                    }\n\n                    if (command.StartsWith(\"continuations\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        WriteString(\"Hello, Hangfire continuations!\");\n                    }\n                }\n            }\n\n            Console.WriteLine(\"Press Enter to exit...\");\n            Console.ReadLine();\n        }\n\n        public static void WriteString(string value)\n        {\n            var lastId = BackgroundJob.Enqueue<Services>(x => x.Write(value[0]));\n\n            for (var i = 1; i < value.Length; i++)\n            {\n                lastId = BackgroundJob.ContinueJobWith<Services>(lastId, x => x.Write(value[i]));\n            }\n\n            BackgroundJob.ContinueJobWith<Services>(lastId, x => x.WriteBlankLine());\n        }\n    }\n}\n"
  },
  {
    "path": "samples/ConsoleSample/Services.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire;\nusing Hangfire.States;\n\nnamespace ConsoleSample\n{\n    public class Services\n    {\n        private static readonly Random Rand = new Random();\n\n        public async Task<int> WriteIndex([FromResult] int? index)\n        {\n            if (index == null) throw new ArgumentNullException(nameof(index));\n\n            Console.Write(\"Hello, world!\\r\\n\"[index.Value]);\n            await Task.Yield();\n            return index.Value + 1;\n        }\n\n        public async Task EmptyDefault()\n        {\n            await Task.Yield();\n        }\n\n        public async Task Async(CancellationToken cancellationToken)\n        {\n            await Task.Yield();\n            await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken);\n        }\n\n        [Obsolete(\"Please use EmptyDefault method instead with `critical` queue directly\")]\n        [Queue(\"critical\")]\n        public async Task EmptyCritical()\n        {\n            await Task.Yield();\n        }\n\n        [AutomaticRetry(Attempts = 0), LatencyTimeout(30)]\n        public async Task Error()\n        {\n            await Task.Yield();\n            Console.WriteLine(\"Beginning error task...\");\n            throw new InvalidOperationException(null, new FileLoadException());\n        }\n\n        [Queue(\"critical\")]\n        public async Task<int> Random([FromResult] int? number)\n        {\n            int time;\n            lock (Rand)\n            {\n                time = Rand.Next(10);\n            }\n\n            if (time < 5)\n            {\n                throw new Exception();\n            }\n\n            await Task.Delay(TimeSpan.FromSeconds(5 + time));\n            Console.WriteLine(\"Finished task: \" + number);\n            return time;\n        }\n\n        public async Task Cancelable(int iterationCount, IJobCancellationToken token)\n        {\n            try\n            {\n                for (var i = 1; i <= iterationCount; i++)\n                {\n                    await Task.Delay(1000);\n                    Console.WriteLine(\"Performing step {0} of {1}...\", i, iterationCount);\n\n                    token.ThrowIfCancellationRequested();\n                }\n            }\n            catch (OperationCanceledException)\n            {\n                Console.WriteLine(\"Cancellation requested, exiting...\");\n                throw;\n            }\n        }\n\n        [DisplayName(\"Name: {0}\")]\n        public async Task Args(string name, int authorId, DateTime createdAt)\n        {\n            await Task.Yield();\n            Console.WriteLine($\"{name}, {authorId}, {createdAt}\");\n        }\n\n        public async Task Custom(int id, string[] values, CustomObject objects, DayOfWeek dayOfWeek)\n        {\n            await Task.Yield();\n        }\n\n        public async Task FullArgs(\n            bool b,\n            int i,\n            char c,\n            DayOfWeek e,\n            string s,\n            TimeSpan t,\n            DateTime d,\n            CustomObject o,\n            string[] sa,\n            int[] ia,\n            long[] ea,\n            object[] na,\n            List<string> sl)\n        {\n            await Task.Yield();\n        }\n\n        public async Task LongRunning(IJobCancellationToken token)\n        {\n            await Task.Delay(TimeSpan.FromMinutes(30), token.ShutdownToken);\n        }\n\n        public class CustomObject\n        {\n            public int Id { get; set; }\n            public CustomObject[] Children { get; set; }\n        }\n\n        public async Task Write(char character)\n        {\n            await Task.Yield();\n            Console.Write(character);\n        }\n\n        public async Task WriteBlankLine()\n        {\n            await Task.Yield();\n            Console.WriteLine();\n        }\n\n        [IdempotentCompletion]\n        public static async Task <IState> WriteLine(string value)\n        {\n            await Task.Yield();\n            Console.WriteLine(value);\n            return new AwaitingState(\"asfafs\", new EnqueuedState(\"criticalll\"));\n        }\n    }\n}"
  },
  {
    "path": "samples/ConsoleSample/Startup.cs",
    "content": "﻿using System;\nusing Hangfire;\nusing Owin;\n\nnamespace ConsoleSample\n{\n    public class Startup\n    {\n        public void Configuration(IAppBuilder app)\n        {\n            app.UseErrorPage();\n            app.UseHangfireDashboard(String.Empty);\n\n            app.UseHangfireServer(new BackgroundJobServerOptions\n            {\n                Queues = new[] { \"critical\", \"default\" },\n                TaskScheduler = null,\n                SchedulePollingInterval = TimeSpan.FromSeconds(1)\n            });\n        }\n    }\n}"
  },
  {
    "path": "samples/ConsoleSample/app.config",
    "content": "<?xml version=\"1.0\"?>\n<configuration>\n  <startup>\n    <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.5\"/>\n  </startup>\n</configuration>\n"
  },
  {
    "path": "samples/ConsoleSample/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \".NETFramework,Version=v4.5.1\": {\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net451\": \"1.0.3\"\n        }\n      },\n      \"Microsoft.Owin.SelfHost\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.0.0, )\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"Gsxjg466eKBykIMZyrPP1IA8MIbYPd/1gcSWc6JLGb+CUO/elctCJomSbOJFceh9YU9hj9qURjpx0ns6/AghSA==\",\n        \"dependencies\": {\n          \"Microsoft.Owin\": \"4.0.0\",\n          \"Microsoft.Owin.Diagnostics\": \"4.0.0\",\n          \"Microsoft.Owin.Host.HttpListener\": \"4.0.0\",\n          \"Microsoft.Owin.Hosting\": \"4.0.0\",\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[13.0.2, )\",\n        \"resolved\": \"13.0.2\",\n        \"contentHash\": \"R2pZ3B0UjeyHShm9vG+Tu0EBb2lC8b0dFzV9gVn50ofHXh9Smjk6kTn7A/FdAsC8B5cKib1OnGYOXxRBz5XQDg==\"\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Dapper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.60.6\",\n        \"contentHash\": \"mmnJNhKMeF2KhvVXDoVQlFxre8aJAo71YBJrKqFlvuqzYC2QiXUq94/GCDBJzU7paq4GqpkV2glw3308TcGibw==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net451\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vVPinxdLrwoX81ApbNIHDBI6qymQEy8eSOxDNBgKJtc2+cifnF0oT1U2d3EFx+V5O68yaqna2myZJNsgKCpVkA==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.Owin.Diagnostics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"96t+ud+Ai2kPnkdThUPL8wM9C1MAzZ80zlfTvjK0VwJsy+9F2rylFrsRx+dD7KSsN+ANKAuxSQlaYuzU9O8pZA==\",\n        \"dependencies\": {\n          \"Microsoft.Owin\": \"4.0.0\",\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.Owin.Host.HttpListener\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"AC0fcRPFg/gf6AmATmCJBGnD14URhCYOHtAW0g248YzZ4ByIkVA7jHBQbfij+zyIOeOya8uaAwFPCIN1wyiOQw==\"\n      },\n      \"Microsoft.Owin.Hosting\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"rAkxeyiIliRbMZyPxo83QE+UY7HNTsFOH9+wqQTcQyWBVbYkBQp373meNt4+DpupMB56A1BopLDeyoX9DZJrbA==\",\n        \"dependencies\": {\n          \"Microsoft.Owin\": \"4.0.0\",\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      },\n      \"hangfire.sqlserver\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Dapper\": \"[1.60.6, )\",\n          \"Hangfire.Core\": \"[1.0.0, )\"\n        }\n      },\n      \"hangfire.sqlserver.msmq\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Hangfire.Core\": \"[1.0.0, )\",\n          \"Hangfire.SqlServer\": \"[1.0.0, )\"\n        }\n      }\n    },\n    \".NETFramework,Version=v4.5.1/win7-x86\": {}\n  }\n}"
  },
  {
    "path": "samples/NetCoreSample/NetCoreSample.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n  <PropertyGroup>\r\n    <OutputType>Exe</OutputType>\r\n    <TargetFramework>net6.0</TargetFramework>\r\n    <LangVersion>latest</LangVersion>\r\n    <NoWarn>NU1701;NU1702</NoWarn>\r\n    <CheckEolTargetFramework>false</CheckEolTargetFramework>\r\n  </PropertyGroup>\r\n\r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.Core\\Hangfire.Core.csproj\" />\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.AspNetCore\\Hangfire.AspNetCore.csproj\" />\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.SqlServer\\Hangfire.SqlServer.csproj\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <PackageReference Include=\"Microsoft.Extensions.Hosting\" Version=\"8.0.1\" />\r\n    <PackageReference Include=\"Microsoft.Extensions.Logging.Console\" Version=\"8.0.1\" />\r\n  </ItemGroup>\r\n\r\n</Project>\r\n"
  },
  {
    "path": "samples/NetCoreSample/Program.cs",
    "content": "﻿using System;\nusing System.Data;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Hangfire.SqlServer;\nusing Hangfire.States;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.DependencyInjection.Extensions;\nusing Microsoft.Extensions.Hosting;\nusing Microsoft.Extensions.Logging;\n\nnamespace NetCoreSample\n{\n    class Program\n    {\n        static async Task Main(string[] args)\n        {\n            var host = new HostBuilder()\n                .ConfigureLogging(x => x.AddConsole().SetMinimumLevel(LogLevel.Information))\n                .ConfigureServices((hostContext, services) =>\n                {\n                    services.Configure<HostOptions>(option =>\n                    {\n                        option.ShutdownTimeout = TimeSpan.FromSeconds(60);\n                    });\n\n                    services.TryAddSingleton<SqlServerStorageOptions>(new SqlServerStorageOptions\n                    {\n                        CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),\n                        QueuePollInterval = TimeSpan.FromTicks(1),\n                        UseRecommendedIsolationLevel = true,\n                        SlidingInvisibilityTimeout = TimeSpan.FromMinutes(1)\n                    });\n\n                    services.TryAddSingleton<IBackgroundJobFactory>(x => new CustomBackgroundJobFactory(\n                        new BackgroundJobFactory(x.GetRequiredService<IJobFilterProvider>())));\n\n                    services.TryAddSingleton<IBackgroundJobPerformer>(x => new CustomBackgroundJobPerformer(\n                        new BackgroundJobPerformer(\n                            x.GetRequiredService<IJobFilterProvider>(),\n                            x.GetRequiredService<JobActivator>(),\n                            TaskScheduler.Default)));\n\n                    services.TryAddSingleton<IBackgroundJobStateChanger>(x => new CustomBackgroundJobStateChanger(\n                            new BackgroundJobStateChanger(x.GetRequiredService<IJobFilterProvider>())));\n\n                    services.AddHangfire((provider, configuration) => configuration\n                        .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)\n                        .UseSimpleAssemblyNameTypeSerializer()\n                        .UseSqlServerStorage(\n                            @\"Server=.\\;Database=Hangfire.Sample;Trusted_Connection=True;\", \n                            provider.GetRequiredService<SqlServerStorageOptions>()));\n\n                    services.AddHostedService<RecurringJobsService>();\n                    services.AddHangfireServer(options =>\n                    {\n                        options.StopTimeout = TimeSpan.FromSeconds(15);\n                        options.ShutdownTimeout = TimeSpan.FromSeconds(30);\n                    });\n                })\n                .Build();\n\n            await host.RunAsync();\n        }\n    }\n\n    internal class CustomBackgroundJobFactory : IBackgroundJobFactory\n    {\n        private readonly IBackgroundJobFactory _inner;\n\n        public CustomBackgroundJobFactory([NotNull] IBackgroundJobFactory inner)\n        {\n            _inner = inner ?? throw new ArgumentNullException(nameof(inner));\n        }\n\n        public IStateMachine StateMachine => _inner.StateMachine;\n\n        public BackgroundJob Create(CreateContext context)\n        {\n            Console.WriteLine($\"Create: {context.Job.Type.FullName}.{context.Job.Method.Name} in {context.InitialState?.Name} state\");\n            return _inner.Create(context);\n        }\n    }\n\n    internal class CustomBackgroundJobPerformer : IBackgroundJobPerformer\n    {\n        private readonly IBackgroundJobPerformer _inner;\n\n        public CustomBackgroundJobPerformer([NotNull] IBackgroundJobPerformer inner)\n        {\n            _inner = inner ?? throw new ArgumentNullException(nameof(inner));\n        }\n\n        public object Perform(PerformContext context)\n        {\n            Console.WriteLine($\"Perform {context.BackgroundJob.Id} ({context.BackgroundJob.Job.Type.FullName}.{context.BackgroundJob.Job.Method.Name})\");\n            return _inner.Perform(context);\n        }\n    }\n\n    internal class CustomBackgroundJobStateChanger : IBackgroundJobStateChanger\n    {\n        private readonly IBackgroundJobStateChanger _inner;\n\n        public CustomBackgroundJobStateChanger([NotNull] IBackgroundJobStateChanger inner)\n        {\n            _inner = inner ?? throw new ArgumentNullException(nameof(inner));\n        }\n\n        public IState ChangeState(StateChangeContext context)\n        {\n            Console.WriteLine($\"ChangeState {context.BackgroundJobId} to {context.NewState}\");\n            return _inner.ChangeState(context);\n        }\n    }\n\n    internal class RecurringJobsService : BackgroundService\n    {\n        private readonly IBackgroundJobClient _backgroundJobs;\n        private readonly IRecurringJobManager _recurringJobs;\n        private readonly ILogger<RecurringJobScheduler> _logger;\n\n        public RecurringJobsService(\n            [NotNull] IBackgroundJobClient backgroundJobs,\n            [NotNull] IRecurringJobManager recurringJobs,\n            [NotNull] ILogger<RecurringJobScheduler> logger)\n        {\n            _backgroundJobs = backgroundJobs ?? throw new ArgumentNullException(nameof(backgroundJobs));\n            _recurringJobs = recurringJobs ?? throw new ArgumentNullException(nameof(recurringJobs));\n            _logger = logger ?? throw new ArgumentNullException(nameof(logger));\n        }\n\n        protected override Task ExecuteAsync(CancellationToken stoppingToken)\n        {\n            try\n            {\n                _recurringJobs.AddOrUpdate(\"seconds\", () => Console.WriteLine(\"Hello, seconds!\"), \"*/15 * * * * *\");\n                _recurringJobs.AddOrUpdate(\"minutely\", () => Console.WriteLine(\"Hello, world!\"), Cron.Minutely);\n                _recurringJobs.AddOrUpdate(\"hourly\", () => Console.WriteLine(\"Hello\"), \"25 15 * * *\");\n                _recurringJobs.AddOrUpdate(\"neverfires\", () => Console.WriteLine(\"Can only be triggered\"), \"0 0 31 2 *\");\n\n                _recurringJobs.AddOrUpdate(\"Hawaiian\", () => Console.WriteLine(\"Hawaiian\"),  \"15 08 * * *\", new RecurringJobOptions\n                {\n                    TimeZone = TimeZoneInfo.FindSystemTimeZoneById(\"Hawaiian Standard Time\")\n                });\n                _recurringJobs.AddOrUpdate(\"UTC\", () => Console.WriteLine(\"UTC\"), \"15 18 * * *\");\n                _recurringJobs.AddOrUpdate(\"Russian\", () => Console.WriteLine(\"Russian\"), \"15 21 * * *\", new RecurringJobOptions\n                {\n                    TimeZone = TimeZoneInfo.Local\n                });\n            }\n            catch (Exception e)\n            {\n                _logger.LogError(e, \"An exception occurred while creating recurring jobs.\");\n            }\n\n            return Task.CompletedTask;\n        }\n    }\n}\n"
  },
  {
    "path": "samples/NetCoreSample/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \"net6.0\": {\n      \"Microsoft.Extensions.Hosting\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.1, )\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"bP9EEkHBEfjgYiG8nUaXqMk/ujwJrffOkNPP7onpRMO8R+OUSESSP4xHkCAXgYZ1COP2Q9lXlU5gkMFh20gRuw==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.Binder\": \"8.0.2\",\n          \"Microsoft.Extensions.Configuration.CommandLine\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.EnvironmentVariables\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.FileExtensions\": \"8.0.1\",\n          \"Microsoft.Extensions.Configuration.Json\": \"8.0.1\",\n          \"Microsoft.Extensions.Configuration.UserSecrets\": \"8.0.1\",\n          \"Microsoft.Extensions.DependencyInjection\": \"8.0.1\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Diagnostics\": \"8.0.1\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.FileProviders.Physical\": \"8.0.0\",\n          \"Microsoft.Extensions.Hosting.Abstractions\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Logging.Configuration\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.Console\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.Debug\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.EventLog\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.EventSource\": \"8.0.1\",\n          \"Microsoft.Extensions.Options\": \"8.0.2\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Console\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.1, )\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"uzcg/5U2eLyn5LIKlERkdSxw6VPC1yydnOSQiRRWGBGN3kphq3iL4emORzrojScDmxRhv49gp5BI8U3Dz7y4iA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Logging\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Logging.Configuration\": \"8.0.1\",\n          \"Microsoft.Extensions.Options\": \"8.0.2\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"6.0.0\",\n          \"System.Text.Json\": \"8.0.5\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Dapper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.1.28\",\n        \"contentHash\": \"ha49pzOEDmCPkMxwfPSR/wxa/6RD3r42TESIgpzpi7FXq/gNVUuJVEO+wtUzntNRhtmq3BKCl0s0aAlSZLkBUA==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"vvVR/B08YVghQ4jHEloxqw2ZWzEGE1AOA5E0DioUM3ujbXz6FD3AfB/0Jl2ohJPd0nXYGwmPe1En6HTsSriq1A==\"\n      },\n      \"Microsoft.Extensions.Configuration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Configuration.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Configuration.Binder\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.2\",\n        \"contentHash\": \"7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Configuration.CommandLine\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"NZuZMz3Q8Z780nKX3ifV1fE7lS+6pynDHK71OfU4OZ1ItgvDOhyOC7E6z+JMZrAj63zRpwbdldYFk499t3+1dQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Configuration.EnvironmentVariables\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Configuration.FileExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.FileProviders.Physical\": \"8.0.0\",\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Configuration.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.FileExtensions\": \"8.0.1\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"8.0.0\",\n          \"System.Text.Json\": \"8.0.5\"\n        }\n      },\n      \"Microsoft.Extensions.Configuration.UserSecrets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"7tYqdPPpAK+3jO9d5LTuCK2VxrEdf85Ol4trUr6ds4jclBecadWZ/RyPCbNjfbN5iGTfUnD/h65TOQuqQv2c+A==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.Json\": \"8.0.1\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.FileProviders.Physical\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\"\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.2\",\n        \"contentHash\": \"3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==\"\n      },\n      \"Microsoft.Extensions.Diagnostics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"doVPCUUCY7c6LhBsEfiy3W1bvS7Mi6LkfQMS8nlC22jZWNxBv8VO8bdfeyvpYFst6Kxqk7HBC6lytmEoBssvSQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"8.0.0\",\n          \"Microsoft.Extensions.Diagnostics.Abstractions\": \"8.0.1\",\n          \"Microsoft.Extensions.Options.ConfigurationExtensions\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Diagnostics.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Options\": \"8.0.2\",\n          \"System.Diagnostics.DiagnosticSource\": \"8.0.1\"\n        }\n      },\n      \"Microsoft.Extensions.FileProviders.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.FileProviders.Physical\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.FileSystemGlobbing\": \"8.0.0\",\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.FileSystemGlobbing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==\"\n      },\n      \"Microsoft.Extensions.Hosting.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Diagnostics.Abstractions\": \"8.0.1\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"8.0.2\"\n        }\n      },\n      \"Microsoft.Extensions.Logging\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Options\": \"8.0.2\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.2\",\n        \"contentHash\": \"nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\",\n          \"System.Diagnostics.DiagnosticSource\": \"8.0.1\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Configuration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"QWwTrsgOnJMmn+XUslm8D2H1n3PkP/u/v52FODtyBc/k4W9r3i2vcXXeeX/upnzllJYRRbrzVzT0OclfNJtBJA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.Binder\": \"8.0.2\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Logging\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Options\": \"8.0.2\",\n          \"Microsoft.Extensions.Options.ConfigurationExtensions\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"B8hqNuYudC2RB+L/DI33uO4rf5by41fZVdcVL2oZj0UyoAZqnwTwYHp1KafoH4nkl1/23piNeybFFASaV2HkFg==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Logging\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"8.0.2\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.EventLog\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"ZD1m4GXoxcZeDJIq8qePKj+QAWeQNO/OG8skvrOG8RQfxLp9MAKRoliTc27xanoNUzeqvX5HhS/I7c0BvwAYUg==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Logging\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Options\": \"8.0.2\",\n          \"System.Diagnostics.EventLog\": \"8.0.1\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.EventSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"YMXMAla6B6sEf/SnfZYTty633Ool3AH7KOw2LOaaEqwSo2piK4f7HMtzyc3CNiipDnq1fsUSuG5Oc7ZzpVy8WQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Logging\": \"8.0.1\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Options\": \"8.0.2\",\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"6.0.0\",\n          \"System.Text.Json\": \"8.0.5\"\n        }\n      },\n      \"Microsoft.Extensions.Options\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.2\",\n        \"contentHash\": \"dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Options.ConfigurationExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.Configuration.Binder\": \"8.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.Options\": \"8.0.0\",\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==\",\n        \"dependencies\": {\n          \"System.Runtime.CompilerServices.Unsafe\": \"6.0.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.0.1\",\n        \"contentHash\": \"pNN4l+J6LlpIvHOeNdXlwxv39NPJ2B5klz+Rd2UQZIx30Squ5oND1Yy3wEAUoKn0GPUj6Yxt9lxlYWQqfZcvKg==\"\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==\",\n        \"dependencies\": {\n          \"System.Runtime.CompilerServices.Unsafe\": \"6.0.0\"\n        }\n      },\n      \"System.Diagnostics.EventLog\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"n1ZP7NM2Gkn/MgD8+eOT5MulMj6wfeQMNS2Pizvq5GHCZfjlFMXV2irQlQmJhwA2VABC57M0auudO89Iu2uRLg==\"\n      },\n      \"System.Runtime.CompilerServices.Unsafe\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==\"\n      },\n      \"System.Text.Encodings.Web\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==\",\n        \"dependencies\": {\n          \"System.Runtime.CompilerServices.Unsafe\": \"6.0.0\"\n        }\n      },\n      \"System.Text.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.5\",\n        \"contentHash\": \"0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==\",\n        \"dependencies\": {\n          \"System.Runtime.CompilerServices.Unsafe\": \"6.0.0\",\n          \"System.Text.Encodings.Web\": \"8.0.0\"\n        }\n      },\n      \"hangfire.aspnetcore\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Hangfire.NetCore\": \"[1.0.0, )\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.CSharp\": \"[4.4.0, )\",\n          \"Newtonsoft.Json\": \"[11.0.1, )\"\n        }\n      },\n      \"hangfire.netcore\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Hangfire.Core\": \"[1.0.0, )\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"[3.0.0, )\",\n          \"Microsoft.Extensions.Hosting.Abstractions\": \"[3.0.0, )\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"[3.0.0, )\"\n        }\n      },\n      \"hangfire.sqlserver\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Dapper\": \"[2.1.28, )\",\n          \"Hangfire.Core\": \"[1.0.0, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/Directory.Build.props",
    "content": "<Project>\n    <Import Project=\"$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))\" />\n    <!-- Custom AssemblyInfo.cs file -->\n    <PropertyGroup>\n        <GenerateAssemblyInfo>false</GenerateAssemblyInfo>\n        <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>\n        <DebugType>embedded</DebugType>\n        <EmbedUntrackedSources>true</EmbedUntrackedSources>\n        <LangVersion>Latest</LangVersion>\n        <CheckNotRecommendedTargetFramework>false</CheckNotRecommendedTargetFramework>\n    </PropertyGroup>\n    <ItemGroup>\n        <Compile Include=\"..\\SharedAssemblyInfo.cs\" Link=\"Properties\\SharedAssemblyInfo.cs\" />\n    </ItemGroup>\n\n    <!-- Plugging in Roslyn Analysers -->\n    <ItemGroup>\n        <PackageReference Include=\"Microsoft.CodeAnalysis.NetAnalyzers\" Version=\"9.0.0\" PrivateAssets=\"all\" />\n    </ItemGroup>\n\n    <PropertyGroup>\n        <NoWarn>CA1200;CA1859;1591</NoWarn>\n        <EnableNETAnalyzers>true</EnableNETAnalyzers>\n        <AnalysisLevel>latest</AnalysisLevel>\n        <AnalysisMode>Recommended</AnalysisMode>\n        <TreatWarningsAsErrors>true</TreatWarningsAsErrors>\n    </PropertyGroup>\n\n    <ItemGroup>\n        <PackageReference Include=\"Microsoft.SourceLink.GitHub\" Version=\"8.0.0\" PrivateAssets=\"all\" />\n    </ItemGroup>\n\n    <PropertyGroup Condition=\"'$(APPVEYOR)' == 'True'\">\n        <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>\n        <!-- Deterministic paths work bad when using ILRepack, because it combines everything with a wrong repository -->\n        <DeterministicSourcePaths>false</DeterministicSourcePaths>\n    </PropertyGroup>\n</Project>"
  },
  {
    "path": "src/Hangfire.AspNetCore/Dashboard/AspNetCoreDashboardContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Microsoft.AspNetCore.Antiforgery;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace Hangfire.Dashboard\n{\n    public sealed class AspNetCoreDashboardContext : DashboardContext\n    {\n        public AspNetCoreDashboardContext(\n            [NotNull] JobStorage storage,\n            [NotNull] DashboardOptions options,\n            [NotNull] HttpContext httpContext) \n            : base(storage, options)\n        {\n            HttpContext = httpContext ?? throw new ArgumentNullException(nameof(httpContext));\n            Request = new AspNetCoreDashboardRequest(httpContext);\n            Response = new AspNetCoreDashboardResponse(httpContext);\n\n            if (!options.IgnoreAntiforgeryToken)\n            {\n                var antiforgery = HttpContext.RequestServices.GetService<IAntiforgery>();\n                var tokenSet = antiforgery?.GetAndStoreTokens(HttpContext);\n\n                if (tokenSet != null)\n                {\n                    AntiforgeryHeader = tokenSet.HeaderName;\n                    AntiforgeryToken = tokenSet.RequestToken;\n                }\n            }\n        }\n\n        public HttpContext HttpContext { get; }\n\n        public override IBackgroundJobClient GetBackgroundJobClient()\n        {\n            var factory = HttpContext.RequestServices.GetService<IBackgroundJobClientFactory>();\n            if (factory != null)\n            {\n                return factory.GetClient(Storage);\n            }\n\n            return HttpContext.RequestServices.GetService<IBackgroundJobClient>() ?? base.GetBackgroundJobClient();\n        }\n\n        public override IRecurringJobManager GetRecurringJobManager()\n        {\n            var factory = HttpContext.RequestServices.GetService<IRecurringJobManagerFactory>();\n            if (factory != null)\n            {\n                return factory.GetManager(Storage);\n            }\n\n            return HttpContext.RequestServices.GetService<IRecurringJobManager>() ?? base.GetRecurringJobManager();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.AspNetCore/Dashboard/AspNetCoreDashboardContextExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Hangfire.Dashboard\n{\n    public static class AspNetCoreDashboardContextExtensions\n    {\n        public static HttpContext GetHttpContext([NotNull] this DashboardContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            var aspNetCoreContext = context as AspNetCoreDashboardContext;\n            if (aspNetCoreContext == null)\n            {\n                throw new ArgumentException($\"Context argument should be of type `{nameof(AspNetCoreDashboardContext)}`!\", nameof(context));\n            }\n\n            return aspNetCoreContext.HttpContext;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.AspNetCore/Dashboard/AspNetCoreDashboardMiddleware.cs",
    "content": "// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Net;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Microsoft.AspNetCore.Antiforgery;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace Hangfire.Dashboard\n{\n    public class AspNetCoreDashboardMiddleware\n    {\n        private readonly RequestDelegate _next;\n        private readonly JobStorage _storage;\n        private readonly DashboardOptions _options;\n        private readonly RouteCollection _routes;\n        private readonly bool _finalizeWhenNotFound;\n\n        public AspNetCoreDashboardMiddleware(\n            [NotNull] RequestDelegate next,\n            [NotNull] JobStorage storage,\n            [NotNull] DashboardOptions options,\n            [NotNull] RouteCollection routes)\n            : this(next, storage, options, routes, finalizeWhenNotFound: false)\n        {\n        }\n\n        public AspNetCoreDashboardMiddleware(\n            [NotNull] RequestDelegate next,\n            [NotNull] JobStorage storage,\n            [NotNull] DashboardOptions options,\n            [NotNull] RouteCollection routes,\n            bool finalizeWhenNotFound)\n        {\n            if (next == null) throw new ArgumentNullException(nameof(next));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n            if (routes == null) throw new ArgumentNullException(nameof(routes));\n\n            _next = next;\n            _storage = storage;\n            _options = options;\n            _routes = routes;\n            _finalizeWhenNotFound = finalizeWhenNotFound;\n        }\n\n        public async Task Invoke(HttpContext httpContext)\n        {\n            var context = new AspNetCoreDashboardContext(_storage, _options, httpContext);\n            var findResult = _routes.FindDispatcher(httpContext.Request.Path.Value);\n\n            if (findResult == null)\n            {\n                if (_finalizeWhenNotFound)\n                {\n                    // When UsePathBase method is used, such as in MapHangfireDashboard, we should\n                    // set 404 status code explicitly to handle non-found endpoints, because no one\n                    // will do this for us.\n                    // https://github.com/HangfireIO/Hangfire/issues/1729\n                    // https://github.com/HangfireIO/Hangfire/issues/2541\n                    SetResponseStatusCode(httpContext, (int)HttpStatusCode.NotFound);\n                    return;\n                }\n\n                await _next.Invoke(httpContext);\n                return;\n            }\n\n            // ReSharper disable once LoopCanBeConvertedToQuery\n            foreach (var filter in _options.Authorization)\n            {\n                if (!filter.Authorize(context))\n                {\n                    SetResponseStatusCode(httpContext, GetUnauthorizedStatusCode(httpContext));\n                    return;\n                }\n            }\n\n            foreach (var filter in _options.AsyncAuthorization)\n            {\n                if (!await filter.AuthorizeAsync(context))\n                {\n                    SetResponseStatusCode(httpContext, GetUnauthorizedStatusCode(httpContext));\n                    return;\n                }\n            }\n\n            if (!_options.IgnoreAntiforgeryToken)\n            {\n                var antiforgery = httpContext.RequestServices.GetService<IAntiforgery>();\n\n                if (antiforgery != null)\n                {\n                    var requestValid = await antiforgery.IsRequestValidAsync(httpContext);\n\n                    if (!requestValid)\n                    {\n                        // Invalid or missing CSRF token\n                        SetResponseStatusCode(httpContext, (int) HttpStatusCode.Forbidden);\n                        return;\n                    }\n                }\n            }\n\n            context.UriMatch = findResult.Item2;\n\n            await findResult.Item1.Dispatch(context);\n        }\n\n        private static void SetResponseStatusCode(HttpContext httpContext, int statusCode)\n        {\n            if (!httpContext.Response.HasStarted)\n            {\n                httpContext.Response.StatusCode = statusCode;\n            }\n        }\n\n        private static int GetUnauthorizedStatusCode(HttpContext httpContext)\n        {\n            return httpContext.User?.Identity?.IsAuthenticated == true\n                ? (int)HttpStatusCode.Forbidden\n                : (int)HttpStatusCode.Unauthorized;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.AspNetCore/Dashboard/AspNetCoreDashboardRequest.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Microsoft.AspNetCore.Http;\n\nnamespace Hangfire.Dashboard\n{\n    internal sealed class AspNetCoreDashboardRequest : DashboardRequest\n    {\n        private readonly HttpContext _context;\n\n        public AspNetCoreDashboardRequest([NotNull] HttpContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n            _context = context;\n        }\n\n        public override string Method => _context.Request.Method;\n        public override string Path => _context.Request.Path.Value;\n        public override string PathBase => _context.Request.PathBase.Value;\n        public override string LocalIpAddress => _context.Connection.LocalIpAddress?.ToString();\n        public override string RemoteIpAddress => _context.Connection.RemoteIpAddress?.ToString();\n        public override string GetQuery(string key) => _context.Request.Query[key];\n\n        public override async Task<IList<string>> GetFormValuesAsync(string key)\n        {\n            var form = await _context.Request.ReadFormAsync();\n            return form[key];\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.AspNetCore/Dashboard/AspNetCoreDashboardResponse.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Globalization;\nusing System.IO;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard\n{\n    internal sealed class AspNetCoreDashboardResponse : DashboardResponse\n    {\n        private readonly HttpContext _context;\n\n        public AspNetCoreDashboardResponse([NotNull] HttpContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n            _context = context;\n        }\n\n        public override string ContentType\n        {\n            get { return _context.Response.ContentType; }\n            set\n            {\n                if (!_context.Response.HasStarted)\n                {\n                    _context.Response.ContentType = value;\n                }\n            }\n        }\n\n        public override int StatusCode\n        {\n            get { return _context.Response.StatusCode; }\n            set\n            {\n                if (!_context.Response.HasStarted)\n                {\n                    _context.Response.StatusCode = value;\n                }\n            }\n        }\n\n        public override Stream Body => _context.Response.Body;\n\n        public override Task WriteAsync(string text)\n        {\n            return _context.Response.WriteAsync(text);\n        }\n\n        public override void SetExpire(DateTimeOffset? value)\n        {\n            if (!_context.Response.HasStarted)\n            {\n                _context.Response.Headers[\"Expires\"] = value?.ToString(\"r\", CultureInfo.InvariantCulture);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.AspNetCore/Hangfire.AspNetCore.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n  <PropertyGroup>\r\n    <TargetFrameworks>net451;net461;netstandard1.3;netstandard2.0;netcoreapp3.0</TargetFrameworks>\r\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\r\n    <NoWarn>$(NoWarn);1591</NoWarn>\r\n    <RootNamespace>Hangfire</RootNamespace>\r\n  </PropertyGroup>\r\n\r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\Hangfire.NetCore\\Hangfire.NetCore.csproj\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net451' or '$(TargetFramework)'=='net461'\">\r\n    <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies\" Version=\"1.0.3\" PrivateAssets=\"all\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net451' or '$(TargetFramework)'=='netstandard1.3'\">\r\n    <PackageReference Include=\"Microsoft.AspNetCore.Http.Abstractions\" Version=\"1.0.0\" />\r\n    <PackageReference Include=\"Microsoft.AspNetCore.Antiforgery\" Version=\"1.0.0\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net461' or '$(TargetFramework)'=='netstandard2.0'\">\r\n    <PackageReference Include=\"Microsoft.AspNetCore.Http.Abstractions\" Version=\"2.0.0\" />\r\n    <PackageReference Include=\"Microsoft.AspNetCore.Antiforgery\" Version=\"2.0.0\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='netcoreapp3.0'\">\r\n    <FrameworkReference Include=\"Microsoft.AspNetCore.App\" />\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "src/Hangfire.AspNetCore/HangfireApplicationBuilderExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Dashboard;\nusing Hangfire.Server;\nusing Microsoft.AspNetCore.Builder;\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\nusing Microsoft.Extensions.Hosting;\n#else\nusing Microsoft.AspNetCore.Hosting;\n#endif\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.Extensions.DependencyInjection;\nusing Hangfire.Common;\n\nnamespace Hangfire\n{\n    public static class HangfireApplicationBuilderExtensions\n    {\n        public static IApplicationBuilder UseHangfireDashboard(\n            [NotNull] this IApplicationBuilder app,\n            [NotNull] string pathMatch = \"/hangfire\",\n            [CanBeNull] DashboardOptions options = null,\n            [CanBeNull] JobStorage storage = null)\n        {\n            if (app == null) throw new ArgumentNullException(nameof(app));\n            if (pathMatch == null) throw new ArgumentNullException(nameof(pathMatch));\n\n            HangfireServiceCollectionExtensions.ThrowIfNotConfigured(app.ApplicationServices);\n\n            var services = app.ApplicationServices;\n\n            storage = storage ?? services.GetRequiredService<JobStorage>();\n            options = options ?? services.GetService<DashboardOptions>() ?? new DashboardOptions();\n            options.TimeZoneResolver = options.TimeZoneResolver ?? services.GetService<ITimeZoneResolver>();\n\n            var routes = app.ApplicationServices.GetRequiredService<RouteCollection>();\n\n            app.Map(new PathString(pathMatch), x => x.UseMiddleware<AspNetCoreDashboardMiddleware>(storage, options, routes));\n\n            return app;\n        }\n\n#if !NET451 && !NETSTANDARD1_3\n        [Obsolete(\"Please use IServiceCollection.AddHangfireServer extension method instead in the ConfigureServices method. Will be removed in 2.0.0.\")]\n#endif\n        public static IApplicationBuilder UseHangfireServer(\n            [NotNull] this IApplicationBuilder app,\n            [CanBeNull] BackgroundJobServerOptions options = null,\n            [CanBeNull] IEnumerable<IBackgroundProcess> additionalProcesses = null,\n            [CanBeNull] JobStorage storage = null)\n        {\n            if (app == null) throw new ArgumentNullException(nameof(app));\n            \n            HangfireServiceCollectionExtensions.ThrowIfNotConfigured(app.ApplicationServices);\n\n            var services = app.ApplicationServices;\n\n            storage = storage ?? services.GetRequiredService<JobStorage>();\n            options = options ?? services.GetService<BackgroundJobServerOptions>() ?? new BackgroundJobServerOptions();\n            additionalProcesses = additionalProcesses ?? services.GetServices<IBackgroundProcess>();\n\n            options.Activator = options.Activator ?? services.GetService<JobActivator>();\n            options.FilterProvider = options.FilterProvider ?? services.GetService<IJobFilterProvider>();\n            options.TimeZoneResolver = options.TimeZoneResolver ?? services.GetService<ITimeZoneResolver>();\n\n            services.RegisterHangfireServer(HangfireServiceCollectionExtensions.GetInternalServices(services, out var factory, out var stateChanger, out var performer)\n#pragma warning disable 618\n                ? new BackgroundJobServer(options, storage, additionalProcesses, null, null, factory, performer, stateChanger)\n#pragma warning restore 618\n                : new BackgroundJobServer(options, storage, additionalProcesses));\n\n            return app;\n        }\n\n        public static IApplicationBuilder UseHangfireServer(\n            [NotNull] this IApplicationBuilder app,\n            [NotNull] Func<IBackgroundProcessingServer> serverFactory)\n        {\n            if (app == null) throw new ArgumentNullException(nameof(app));\n            if (serverFactory == null) throw new ArgumentNullException(nameof(serverFactory));\n\n            HangfireServiceCollectionExtensions.ThrowIfNotConfigured(app.ApplicationServices);\n            app.ApplicationServices.RegisterHangfireServer(serverFactory());\n\n            return app;\n        }\n\n        public static IServiceProvider RegisterHangfireServer(\n            [NotNull] this IServiceProvider services,\n            [NotNull] IBackgroundProcessingServer server)\n        {\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\n            var lifetime = services.GetRequiredService<IHostApplicationLifetime>();\n#else\n            var lifetime = services.GetRequiredService<IApplicationLifetime>();\n#endif\n\n            lifetime.ApplicationStopping.Register(server.SendStop);\n            lifetime.ApplicationStopped.Register(server.Dispose);\n\n            return services;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.AspNetCore/HangfireEndpointRouteBuilderExtensions.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Dashboard;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Routing;\nusing Microsoft.Extensions.DependencyInjection;\nusing System;\nusing System.Linq;\n\nnamespace Hangfire\n{\n    public static class HangfireEndpointRouteBuilderExtensions\n    {\n        public static IEndpointConventionBuilder MapHangfireDashboard(\n            [NotNull] this IEndpointRouteBuilder endpoints,\n            [CanBeNull] DashboardOptions options = null,\n            [CanBeNull] JobStorage storage = null)\n        {\n            return MapHangfireDashboard(endpoints, \"/hangfire\", options, storage);\n        }\n\n        public static IEndpointConventionBuilder MapHangfireDashboard(\n            [NotNull] this IEndpointRouteBuilder endpoints,\n            [NotNull] string pattern,\n            [CanBeNull] DashboardOptions options = null,\n            [CanBeNull] JobStorage storage = null)\n        {\n            if (endpoints == null) throw new ArgumentNullException(nameof(endpoints));\n            if (pattern == null) throw new ArgumentNullException(nameof(pattern));\n\n            var app = endpoints.CreateApplicationBuilder();\n\n            HangfireServiceCollectionExtensions.ThrowIfNotConfigured(app.ApplicationServices);\n\n            var services = app.ApplicationServices;\n\n            storage = storage ?? services.GetRequiredService<JobStorage>();\n            options = options ?? services.GetService<DashboardOptions>() ?? new DashboardOptions();\n            options.TimeZoneResolver = options.TimeZoneResolver ?? services.GetService<ITimeZoneResolver>();\n\n            var routes = app.ApplicationServices.GetRequiredService<Dashboard.RouteCollection>();\n\n            var pipeline = app\n                .UsePathBase(pattern)\n                .UseMiddleware<AspNetCoreDashboardMiddleware>(storage, options, routes, true)\n                .Build();\n\n            return endpoints.Map(pattern + \"/{**path}\", pipeline);\n        }\n\n        public static IEndpointConventionBuilder MapHangfireDashboardWithNoAuthorizationFilters(\n            [NotNull] this IEndpointRouteBuilder endpoints,\n            [NotNull] string pattern = \"/hangfire\",\n            [CanBeNull] DashboardOptions options = null,\n            [CanBeNull] JobStorage storage = null)\n        {\n            if (endpoints == null) throw new ArgumentNullException(nameof(endpoints));\n\n            options = options ?? new DashboardOptions();\n\n            // We don't require the default LocalRequestsOnlyAuthorizationFilter since we provide our own policy\n            options.Authorization = Enumerable.Empty<IDashboardAuthorizationFilter>();\n            options.AsyncAuthorization = Enumerable.Empty<IDashboardAsyncAuthorizationFilter>();\n\n            return endpoints.MapHangfireDashboard(pattern, options, storage);\n        }\n\n        public static IEndpointConventionBuilder MapHangfireDashboardWithAuthorizationPolicy(\n            [NotNull] this IEndpointRouteBuilder endpoints,\n            [NotNull] string authorizationPolicyName,\n            [NotNull] string pattern = \"/hangfire\",\n            [CanBeNull] DashboardOptions options = null,\n            [CanBeNull] JobStorage storage = null)\n        {\n            if (endpoints == null) throw new ArgumentNullException(nameof(endpoints));\n            if (authorizationPolicyName == null) throw new ArgumentNullException(nameof(authorizationPolicyName));\n\n            return endpoints\n                .MapHangfireDashboardWithNoAuthorizationFilters(pattern, options, storage)\n                .RequireAuthorization(authorizationPolicyName);\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "src/Hangfire.AspNetCore/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\n\n[assembly: AssemblyTitle(\"Hangfire.AspNetCore\")]\n[assembly: AssemblyDescription(\"ASP.NET Core support for Hangfire\")]\n"
  },
  {
    "path": "src/Hangfire.AspNetCore/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \".NETCoreApp,Version=v3.0\": {\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"vvVR/B08YVghQ4jHEloxqw2ZWzEGE1AOA5E0DioUM3ujbXz6FD3AfB/0Jl2ohJPd0nXYGwmPe1En6HTsSriq1A==\"\n      },\n      \"Microsoft.Extensions.Configuration.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"Lge/PbXC53jI1MF2J92X5EZOeKV8Q/rlB1aV3H9I/ZTDyQGOyBcL03IAvnviWpHKj43BDkNy6kU2KKoh8kAS0g==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"3.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"ofQRroDlzJ0xKOtzNuaVt6QKNImFkhkG0lIMpGl7PtXnIf5SuLWBeiQZAP8DNSxDBJJdcsPkiJiMYK2WA5H8dQ==\"\n      },\n      \"Microsoft.Extensions.FileProviders.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"kahEeykb6FyQytoZNNXuz74X85B4weIEt8Kd+0klK48bkXDWOIHAOvNjlGsPMcS9CL935Te8QGQS83JqCbpdHA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"3.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Hosting.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"qeDWS5ErmkUN96BdQqpmeCmLk5HJWQ/SPw3ux5v5/Qb0hKZS5wojBMulnBC7JUEiBwg7Ir71Yjf1lFiRT5MdtQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"3.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"3.0.0\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"3.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"3.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"+PsosTYZn+omucI0ff9eywo9QcPLwcbIWf7dz7ZLM1zGR8gVZXJ3wo6+tkuIedUNW5iWENlVJPEvrGjiVeoNNQ==\"\n      },\n      \"Microsoft.Extensions.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"6gwewTbmOh+ZVBicVkL1XRp79sx4O7BVY6Yy+7OYZdwn3pyOKe9lOam+3gXJ3TZMjhJZdV0Ub8hxHt2vkrmN5Q==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.0.1\",\n        \"contentHash\": \"pNN4l+J6LlpIvHOeNdXlwxv39NPJ2B5klz+Rd2UQZIx30Squ5oND1Yy3wEAUoKn0GPUj6Yxt9lxlYWQqfZcvKg==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.CSharp\": \"[4.4.0, )\",\n          \"Newtonsoft.Json\": \"[11.0.1, )\"\n        }\n      },\n      \"hangfire.netcore\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Hangfire.Core\": \"[1.0.0, )\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"[3.0.0, )\",\n          \"Microsoft.Extensions.Hosting.Abstractions\": \"[3.0.0, )\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"[3.0.0, )\"\n        }\n      }\n    },\n    \".NETFramework,Version=v4.5.1\": {\n      \"Microsoft.AspNetCore.Antiforgery\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.0, )\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"oJnrSvL6S7jM2eD/TR/Kyp/7O6pKvN+8FcnYvUaxaHbKlISwl98o44uidzePBjGxTf4fh9NFEx/q3OuuxAvBzw==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.DataProtection\": \"1.0.0\",\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"1.0.0\",\n          \"Microsoft.AspNetCore.WebUtilities\": \"1.0.0\",\n          \"Microsoft.Extensions.ObjectPool\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.0, )\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OJHlqdJOWKKBfsiVdX4Z4KCNuqvBIu6+1MVKuejRDyHnGyMkNHNoP/dtVzhPqvJXaJg9N4HlD0XNc6GDCFVffg==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"1.0.0\",\n          \"System.Text.Encodings.Web\": \"4.0.0\"\n        }\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net451\": \"1.0.3\"\n        }\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.AspNetCore.Cryptography.Internal\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"0btvxwOqYNpKTUQrD7LA3p6Wi0vrhfWGBVqIKPS1KtEdkCv3QoVgFO4eJYuClGDS9NXhqk7TWh46/8x8wtZHaw==\"\n      },\n      \"Microsoft.AspNetCore.DataProtection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"gt4URT+8ljPk0ePspLqOGPJBm+s6iMvsZqweplhf7wiZSjFiG1uYBNpQ/0dFY7wSx3NMRjekyXzCjvkGAV570g==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Cryptography.Internal\": \"1.0.0\",\n          \"Microsoft.AspNetCore.DataProtection.Abstractions\": \"1.0.0\",\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.Options\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.DataProtection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"h5ycDgkqmRdManmYMQVJgzNI7YtVp2X2/os1cKmdfrpfq+m9L8bMKhbd7PCksoLci+aYTOSn45khPl+hpPb9ug==\"\n      },\n      \"Microsoft.AspNetCore.Hosting.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"8r6qOl1jYyC523ZKM1QNl+6ijIoYWELWm0tpEWqtTIOg9DytHJWshB7usgqiuRmfHXM0EUziR6ouFY7iP7Tuzw==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Hosting.Server.Abstractions\": \"1.0.0\",\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Hosting.Server.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"sHZyhQEoW15T9E36rfdm5Ux6a6RZB0KNM79ccf2IplWASqmlRGhX4ydU3dzQRLhkHpLx16fnWOL0KScsO6BevQ==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"1.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Features\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"6x7zgfbTo1gL9xMEb7EMO2ES/48bqwnWyfH09z+ubWhnzxdhHls8rtqstPylu5FPD9nid6Vo2pgDm5vufRAy5Q==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.WebUtilities\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"D0licSnS1JgqQ/gYlN41wXbeYG3dFIdjY781YzMHZ5gBB7kczacshW+H6plZkXRr/cCnAJWGa31o1R8c5GEy/A==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"1.0.0\",\n          \"System.Buffers\": \"4.0.0\",\n          \"System.Text.Encodings.Web\": \"4.0.0\"\n        }\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.Extensions.Configuration.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"nJ+Et/rnDMDmGhxvFAKdN3va7y+YDPICv1nUEP8I4IKgOkWwr/dCZHMqxVhJFrkbW9ux8Kd7erC4mvxfZh0WnA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"1.0.0\",\n          \"System.Linq\": \"4.1.0\"\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"+XwaNo3o9RhLQhUnnOBCaukeRi1X9yYc0Fzye9RlErSflKZdw0VgHtn6rvKo0FTionsW0x8QVULhKH+nkqVjQA==\",\n        \"dependencies\": {\n          \"System.ComponentModel\": \"4.0.1\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\"\n        }\n      },\n      \"Microsoft.Extensions.FileProviders.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"4jsqTxG3py/hYSsOtZMkNJ2/CQqPdpwyK7bDUkrwHgqowCFSmx/C+R4IzQ+2AK2Up1fVcu+ldC0gktwidL828A==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"1.0.0\",\n          \"System.IO\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"wHT6oY50q36mAXBRKtFaB7u07WxKC5u2M8fi3PqHOOnHyUo9gD0u1TlCNR8UObHQxKMYwqlgI8TLcErpt29n8A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Collections.Concurrent\": \"4.0.12\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.InteropServices\": \"4.1.0\"\n        }\n      },\n      \"Microsoft.Extensions.ObjectPool\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"BTXoWSTrv/saLlNSg8l41YOoSKeUUanQLykUqRTtiUJz2xxQOCgm4ckPzrdmSK6w0mdjR2h7IrUDGdBF78Z7yg==\"\n      },\n      \"Microsoft.Extensions.Options\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"SdP3yPKF++JTkoa91pBDiE70uQkR/gdXWzOnMPbSj+eOqY1vgY+b8RVl+gh7TrJ2wlCK2QqnQtvCQlPPZRK36w==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.Primitives\": \"1.0.0\",\n          \"System.ComponentModel\": \"4.0.1\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"Microsoft.Extensions.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"3q2vzfKEDjL6JFkRpk5SFA3zarYsO6+ZYgoucNImrUMzDn0mFbEOL5p9oPoWiypwypbJVVjWTf557bXZ0YFLig==\",\n        \"dependencies\": {\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net451\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vVPinxdLrwoX81ApbNIHDBI6qymQEy8eSOxDNBgKJtc2+cifnF0oT1U2d3EFx+V5O68yaqna2myZJNsgKCpVkA==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"msXumHfjjURSkvxUjYuq4N2ghHoRi2VpXcKMA7gK6ujQfU3vGpl+B6ld0ATRg+FZFpRyA6PgEPA+VlIkTeNf2w==\"\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.11\",\n        \"contentHash\": \"YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==\"\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.12\",\n        \"contentHash\": \"2gBcbb3drMLgxlI0fBfxMA31ec6AEyYCHygGse4vxceJan8mRIWeKJ24BFzN7+bi/NFTgdIgufzb94LWO5EERQ==\"\n      },\n      \"System.ComponentModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"oBZFnm7seFiVfugsIyOvQCWobNZs7FzqDV/B7tx20Ep/l3UUFCPDkdTnCNaJZTU27zjeODmy2C/cP60u3D4c9w==\"\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.11\",\n        \"contentHash\": \"w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==\"\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.11\",\n        \"contentHash\": \"B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==\"\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"3KlTJceQc3gnGIaHZ7UBZO26SHL1SHE4ddrmiwumFnId+CEHP+O8r386tZKaE6zlk5/mF8vifMBzHj9SaXN+mQ==\"\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"bQ0iYFOQI0nuTnt+NQADns6ucV4DUvMdwN6CbkB1yj8i7arTGiTN5eok1kQwdnnNWSDZfIUySQY+J3d5KjWn0g==\"\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"I+y02iqkgmCAyfbqOmSDOgqdZQ5tTj80Akm5BPSS8EeB0VGWdy6X1KCoYe8Pk6pwDoAKZUOdLVxnTJcExiv5zw==\"\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==\"\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==\"\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"v6c/4Yaa9uWsq+JMhnOFewrYkgdNHNG2eMKuNqRn8P733rNXeRCGvV5FkkjBXn2dbVkPXOsO0xjsEeM1q2zC0g==\"\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==\"\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==\"\n      },\n      \"System.Text.Encodings.Web\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"TWZnuiJgPDAEEUfobD7njXvSVR2Toz+jvKWds6yL4oSztmKQfnWzucczjzA+6Dv1bktBdY71sZW1YN0X6m9chQ==\"\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.11\",\n        \"contentHash\": \"N+3xqIcg3VDKyjwwCGaZ9HawG9aC6cSDI+s7ROma310GQo8vilFZa86hqKppwTHleR/G0sfOzhvgnUxWCR/DrQ==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      },\n      \"hangfire.netcore\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Hangfire.Core\": \"[1.0.0, )\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"[1.0.0, )\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"[1.0.0, )\"\n        }\n      }\n    },\n    \".NETFramework,Version=v4.6.1\": {\n      \"Microsoft.AspNetCore.Antiforgery\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.0, )\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"BFdjKs38tu7UEHhe1eyZ340+oVfusWhtYGGrOKB/JmjAO8nfaF3NrT6oGUVyXGaZzWxTsdJr9BhsEEN/GoQxkQ==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.DataProtection\": \"2.0.0\",\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.0.0\",\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.0.0\",\n          \"Microsoft.AspNetCore.WebUtilities\": \"2.0.0\",\n          \"Microsoft.Extensions.ObjectPool\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.0, )\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"pblZLY7IfNqhQ5wwGQ0vNq2mG6W5YgZI1fk7suEuwZsGxGEADNBAyNlTALM9L8nMXdvbp6aHP/t4wHrFpcL3Sw==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"2.0.0\",\n          \"System.Text.Encodings.Web\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net461\": \"1.0.3\"\n        }\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.AspNetCore.Cryptography.Internal\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"SY6GQyZZ5o09rqFmy3nhyJzx3lkFDBl0wO2Kb7EoLCPyH6dC7KB+QXysHfa9P5jHPiYB9VEkcQ9H7kQKcXQ1sw==\"\n      },\n      \"Microsoft.AspNetCore.DataProtection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"CjRLA26BpKrzBqpw1g9F3rGYNGisPd+zsnYdpJbHsjH4iIbi/OHfgKzGdHZCwmfQWrlL4e8Q0SpS+DMvgf6Jpg==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Cryptography.Internal\": \"2.0.0\",\n          \"Microsoft.AspNetCore.DataProtection.Abstractions\": \"2.0.0\",\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Options\": \"2.0.0\",\n          \"Microsoft.Win32.Registry\": \"4.4.0\",\n          \"System.Security.Cryptography.Xml\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.DataProtection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"BiFPWLZTKw253oQ5lAXcCkFkNFSRNi8fDCUB2yOTQyuYVMR8pnBAhVJ37o/E6bnuFYrE6eFCU4iDYrShmBIBYA==\"\n      },\n      \"Microsoft.AspNetCore.Hosting.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"IR2zlm3d/CmYbkw+cMM7M6mUAi+xsFUPfWqGYqzZVC5o6jX3xD2Z4Uf44UBaWKMBf5Z7q9dodIdXxwFPF2Hxhg==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Hosting.Server.Abstractions\": \"2.0.0\",\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Hosting.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Hosting.Server.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"v2H65ix/O11HKoxhKQpljtozsD5/1tqeXr3TYnrLgfAPIsp6kTFxIcTSENoxtew7h9X14ENqUf2lBCkyCNRUuQ==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"2.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"lA7Bwvur19MhXrlW0w+WBXONJMSFYY5kNazflz4MNwMZMtzwHxNA6fC5sQsssYd/XvA0gMyKwp52s68uuKLR1w==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"2.0.0\",\n          \"Microsoft.Net.Http.Headers\": \"2.0.0\",\n          \"System.Buffers\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Features\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"yk62muzFTZTKCQuo3nmVPkPvGBlM2qbdSxbX62TufuONuKQrTGQ/SwhwBbYutk5/YY7u4HETu0n9BKOn7mMgmA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.WebUtilities\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"RqDEwy7jdHJ0NunWydSzJrpODnsF7NPdB0KaRdG60H1bMEt4DbjcWkUb+XxjZ15uWCMi7clTQClpPuIFLwD1yQ==\",\n        \"dependencies\": {\n          \"Microsoft.Net.Http.Headers\": \"2.0.0\",\n          \"System.Text.Encodings.Web\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.Extensions.Configuration.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"rHFrXqMIvQNq51H8RYTO4IWmDOYh8NUzyqGlh0xHWTP6XYnKk7Ryinys2uDs+Vu88b3AMlM3gBBSs78m6OQpYQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"eUdJ0Q/GfVyUJc0Jal5L1QZLceL78pvEM9wEKcHeI24KorqMDoVX+gWsMGLulQMfOwsUaPtkpQM2pFERTzSfSg==\"\n      },\n      \"Microsoft.Extensions.FileProviders.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"Z0AK+hmLO33WAXQ5P1uPzhH7z5yjDHX/XnUefXxE//SyvCb9x4cVjND24dT5566t/yzGp8/WLD7EG9KQKZZklQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Hosting.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"qPG6Ip/AdHxMJ7j3z8FkkpCbV8yjtiFpf/aOpN3TwfJWbtYpN+BKV8Q+pqPMgk7XZivcju9yARaEVCS++hWopA==\"\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==\"\n      },\n      \"Microsoft.Extensions.ObjectPool\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"drOmgNZCJiNEqFM/TvyqwtogS8wqoWGQCW5KB/CVGKL6VXHw8OOMdaHyspp8HPstP9UDnrnuq+8eaCaAcQg6tA==\"\n      },\n      \"Microsoft.Extensions.Options\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"sAKBgjl2gWsECBLLR9K54T7/uZaP2n9GhMYHay/oOLfvpvX0+iNAlQ2NJgVE352C9Fs5CDV3VbNTK8T2aNKQFA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Primitives\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"ukg53qNlqTrK38WA30b5qhw0GD7y3jdI9PHHASjdKyTcBHTevFM2o23tyk3pWCgAV27Bbkm+CPQ2zUe1ZOuYSA==\",\n        \"dependencies\": {\n          \"System.Runtime.CompilerServices.Unsafe\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.Net.Http.Headers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"Rm9zeNCWyNrGnysHdRXJpNfeDVlPzzFuidSuRLRNvOrnw71vgNPlR4H9wHo2hG/oSaruukqNjK06MDQqb+eXhA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.0.0\",\n          \"System.Buffers\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net461\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Microsoft.Win32.Registry\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"dA36TlNVn/XfrZtmf0fiI/z1nd3Wfp2QVzTdj26pqgP9LFWq0i1hYEUAW50xUjGFYn1+/cP3KGuxT2Yn1OUNBQ==\",\n        \"dependencies\": {\n          \"System.Security.AccessControl\": \"4.4.0\",\n          \"System.Security.Principal.Windows\": \"4.4.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==\"\n      },\n      \"System.Runtime.CompilerServices.Unsafe\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"9dLLuBxr5GNmOfl2jSMcsHuteEg32BEfUotmmUkmZjpR3RpVHE8YQwt0ow3p6prwA1ME8WqDVZqrr8z6H8G+Kw==\"\n      },\n      \"System.Security.AccessControl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"2NRFPX/V81ucKQmqNgGBZrKGH/5ejsvivSGMRum0SMgPnJxwhuNkzVS1+7gC3R2X0f57CtwrPrXPPSe6nOp82g==\",\n        \"dependencies\": {\n          \"System.Security.Principal.Windows\": \"4.4.0\"\n        }\n      },\n      \"System.Security.Cryptography.Xml\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"1Xubvo4i+K+DO6YzVh6vBKmCl5xx/cAoiJEze6VQ+XwVQU25KQC9pPrmniz2EbbJnmoQ5Rm2FFjHsfQAi0Rs+Q==\"\n      },\n      \"System.Security.Principal.Windows\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"pP+AOzt1o3jESOuLmf52YQTF7H3Ng9hTnrOESQiqsnl2IbBh1HInsAMHYtoh75iUYV0OIkHmjvveraYB6zM97w==\"\n      },\n      \"System.Text.Encodings.Web\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"l/tYeikqMHX2MD2jzrHDfR9ejrpTTF7wvAEbR51AMvzip1wSJgiURbDik4iv/w7ZgytmTD/hlwpplEhF9bmFNw==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      },\n      \"hangfire.netcore\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Hangfire.Core\": \"[1.0.0, )\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"[2.0.0, )\",\n          \"Microsoft.Extensions.Hosting.Abstractions\": \"[2.0.0, )\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"[2.0.0, )\"\n        }\n      }\n    },\n    \".NETStandard,Version=v1.3\": {\n      \"Microsoft.AspNetCore.Antiforgery\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.0, )\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"oJnrSvL6S7jM2eD/TR/Kyp/7O6pKvN+8FcnYvUaxaHbKlISwl98o44uidzePBjGxTf4fh9NFEx/q3OuuxAvBzw==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.DataProtection\": \"1.0.0\",\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"1.0.0\",\n          \"Microsoft.AspNetCore.WebUtilities\": \"1.0.0\",\n          \"Microsoft.Extensions.ObjectPool\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.0, )\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OJHlqdJOWKKBfsiVdX4Z4KCNuqvBIu6+1MVKuejRDyHnGyMkNHNoP/dtVzhPqvJXaJg9N4HlD0XNc6GDCFVffg==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"1.0.0\",\n          \"System.Globalization.Extensions\": \"4.0.1\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.Reflection.TypeExtensions\": \"4.1.0\",\n          \"System.Runtime.InteropServices\": \"4.1.0\",\n          \"System.Text.Encodings.Web\": \"4.0.0\"\n        }\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.6.1, )\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"Microsoft.AspNetCore.Cryptography.Internal\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"0btvxwOqYNpKTUQrD7LA3p6Wi0vrhfWGBVqIKPS1KtEdkCv3QoVgFO4eJYuClGDS9NXhqk7TWh46/8x8wtZHaw==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime.Handles\": \"4.0.1\",\n          \"System.Runtime.InteropServices\": \"4.1.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.0.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"Microsoft.AspNetCore.DataProtection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"gt4URT+8ljPk0ePspLqOGPJBm+s6iMvsZqweplhf7wiZSjFiG1uYBNpQ/0dFY7wSx3NMRjekyXzCjvkGAV570g==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Cryptography.Internal\": \"1.0.0\",\n          \"Microsoft.AspNetCore.DataProtection.Abstractions\": \"1.0.0\",\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.Options\": \"1.0.0\",\n          \"Microsoft.Win32.Registry\": \"4.0.0\",\n          \"System.IO.FileSystem\": \"4.0.1\",\n          \"System.Reflection.Extensions\": \"4.0.1\",\n          \"System.Security.Claims\": \"4.0.1\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.1.0\",\n          \"System.Security.Principal.Windows\": \"4.0.0\",\n          \"System.Xml.XDocument\": \"4.0.11\"\n        }\n      },\n      \"Microsoft.AspNetCore.DataProtection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"h5ycDgkqmRdManmYMQVJgzNI7YtVp2X2/os1cKmdfrpfq+m9L8bMKhbd7PCksoLci+aYTOSn45khPl+hpPb9ug==\",\n        \"dependencies\": {\n          \"System.ComponentModel\": \"4.0.1\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime.Extensions\": \"4.1.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Hosting.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"8r6qOl1jYyC523ZKM1QNl+6ijIoYWELWm0tpEWqtTIOg9DytHJWshB7usgqiuRmfHXM0EUziR6ouFY7iP7Tuzw==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Hosting.Server.Abstractions\": \"1.0.0\",\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Hosting.Server.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"sHZyhQEoW15T9E36rfdm5Ux6a6RZB0KNM79ccf2IplWASqmlRGhX4ydU3dzQRLhkHpLx16fnWOL0KScsO6BevQ==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"1.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Features\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"6x7zgfbTo1gL9xMEb7EMO2ES/48bqwnWyfH09z+ubWhnzxdhHls8rtqstPylu5FPD9nid6Vo2pgDm5vufRAy5Q==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"1.0.0\",\n          \"System.Collections\": \"4.0.11\",\n          \"System.ComponentModel\": \"4.0.1\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Net.Primitives\": \"4.0.11\",\n          \"System.Net.WebSockets\": \"4.0.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Security.Claims\": \"4.0.1\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.1.0\",\n          \"System.Security.Principal\": \"4.0.1\"\n        }\n      },\n      \"Microsoft.AspNetCore.WebUtilities\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"D0licSnS1JgqQ/gYlN41wXbeYG3dFIdjY781YzMHZ5gBB7kczacshW+H6plZkXRr/cCnAJWGa31o1R8c5GEy/A==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"1.0.0\",\n          \"System.Buffers\": \"4.0.0\",\n          \"System.Collections\": \"4.0.11\",\n          \"System.IO\": \"4.1.0\",\n          \"System.IO.FileSystem\": \"4.0.1\",\n          \"System.Text.Encodings.Web\": \"4.0.0\"\n        }\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"17h8b5mXa87XYKrrVqdgZ38JefSUqLChUQpXgSnpzsM0nDOhE40FTeNWOJ/YmySGV6tG6T8+hjz6vxbknHJr6A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Dynamic.Runtime\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Extensions\": \"4.0.1\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Reflection.TypeExtensions\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.InteropServices\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"Microsoft.Extensions.Configuration.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"nJ+Et/rnDMDmGhxvFAKdN3va7y+YDPICv1nUEP8I4IKgOkWwr/dCZHMqxVhJFrkbW9ux8Kd7erC4mvxfZh0WnA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"1.0.0\",\n          \"System.Linq\": \"4.1.0\"\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"+XwaNo3o9RhLQhUnnOBCaukeRi1X9yYc0Fzye9RlErSflKZdw0VgHtn6rvKo0FTionsW0x8QVULhKH+nkqVjQA==\",\n        \"dependencies\": {\n          \"System.ComponentModel\": \"4.0.1\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\"\n        }\n      },\n      \"Microsoft.Extensions.FileProviders.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"4jsqTxG3py/hYSsOtZMkNJ2/CQqPdpwyK7bDUkrwHgqowCFSmx/C+R4IzQ+2AK2Up1fVcu+ldC0gktwidL828A==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"1.0.0\",\n          \"System.IO\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"wHT6oY50q36mAXBRKtFaB7u07WxKC5u2M8fi3PqHOOnHyUo9gD0u1TlCNR8UObHQxKMYwqlgI8TLcErpt29n8A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Collections.Concurrent\": \"4.0.12\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.InteropServices\": \"4.1.0\"\n        }\n      },\n      \"Microsoft.Extensions.ObjectPool\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"BTXoWSTrv/saLlNSg8l41YOoSKeUUanQLykUqRTtiUJz2xxQOCgm4ckPzrdmSK6w0mdjR2h7IrUDGdBF78Z7yg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"Microsoft.Extensions.Options\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"SdP3yPKF++JTkoa91pBDiE70uQkR/gdXWzOnMPbSj+eOqY1vgY+b8RVl+gh7TrJ2wlCK2QqnQtvCQlPPZRK36w==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"1.0.0\",\n          \"Microsoft.Extensions.Primitives\": \"1.0.0\",\n          \"System.ComponentModel\": \"4.0.1\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"Microsoft.Extensions.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"3q2vzfKEDjL6JFkRpk5SFA3zarYsO6+ZYgoucNImrUMzDn0mFbEOL5p9oPoWiypwypbJVVjWTf557bXZ0YFLig==\",\n        \"dependencies\": {\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"Microsoft.Win32.Registry\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"q+eLtROUAQ3OxYA5mpQrgyFgzLQxIyrfT2eLpYX5IEPlHmIio2nh4F5bgOaQoGOV865kFKZZso9Oq9RlazvXtg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.0.1\",\n          \"System.Collections\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.Handles\": \"4.0.1\",\n          \"System.Runtime.InteropServices\": \"4.1.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"9.0.1\",\n        \"contentHash\": \"U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==\",\n        \"dependencies\": {\n          \"Microsoft.CSharp\": \"4.0.1\",\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Dynamic.Runtime\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.IO\": \"4.1.0\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Extensions\": \"4.0.1\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.Serialization.Primitives\": \"4.1.1\",\n          \"System.Text.Encoding\": \"4.0.11\",\n          \"System.Text.Encoding.Extensions\": \"4.0.11\",\n          \"System.Text.RegularExpressions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\",\n          \"System.Threading.Tasks\": \"4.0.11\",\n          \"System.Xml.ReaderWriter\": \"4.0.11\",\n          \"System.Xml.XDocument\": \"4.0.11\"\n        }\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"oBZFnm7seFiVfugsIyOvQCWobNZs7FzqDV/B7tx20Ep/l3UUFCPDkdTnCNaJZTU27zjeODmy2C/cP60u3D4c9w==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Dynamic.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.11\",\n        \"contentHash\": \"db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Emit\": \"4.0.1\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.0.1\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Reflection.TypeExtensions\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"KKo23iKeOaIg61SSXwjANN7QYDr/3op3OWGGzDzz7mypx0Za0fZSeG0l6cco8Ntp8YMYkIQcAqlk8yhm5/Uhcg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.0.1\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.InteropServices\": \"4.1.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Net.WebSockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"2KJo8hir6Edi9jnMDAMhiJoI691xRBmKcbNpwjrvpIMOCTYOtBpSsSEGBxBDV7PKbasJNaFp1+PZz1D7xS41Hg==\",\n        \"dependencies\": {\n          \"Microsoft.Win32.Primitives\": \"4.0.1\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Threading.Tasks\": \"4.0.11\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"P2wqAj72fFjpP6wb9nSfDqNBMab+2ovzSDzUZK7MVIm54tBJEPr9jWfSjjoTpPwj1LeKcmX3vr0ttyjSSFM47g==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.0.1\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"Ov6dU8Bu15Bc7zuqttgHF12J5lwSWyTf1S+FJouUXVMSqImLZzYaQ+vRr1rQ0OZ0HqsrwWl4dsKHELckQkVpgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"tsQ/ptQ3H5FYfON8lL4MxRk/8kFyE0A+tGPXmVP967cT/gzLHYxIejIYSxp4JmIeFHVP78g/F2FE1mUUTbDtrg==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Serialization.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.1\",\n        \"contentHash\": \"HZ6Du5QrTG8MNJbf4e4qMO3JRAkIboGT5Fk804uZtg3Gq516S7hAqTm2UZKUHa7/6HUGdVy3AqMQKbns06G/cg==\",\n        \"dependencies\": {\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Security.Claims\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"4Jlp0OgJLS/Voj1kyFP6MJlIYp3crgfH8kNQk2p7+4JYfc1aAmh9PZyAMMbDhuoolGNtux9HqSOazsioRiDvCw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.IO\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Security.Principal\": \"4.0.1\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Principal\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"On+SKhXY5rzxh/S8wlH1Rm0ogBlu7zyHNxeNBiXauNrhHRXAe9EuX8Yl5IOzLPGU5Z4kLWHMvORDOCG8iu9hww==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Security.Principal.Windows\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"iFx15AF3RMEPZn3COh8+Bb2Thv2zsmLd93RchS1b8Mj5SNYeGqbYNCSn5AES1+gq56p4ujGZPrl0xN7ngkXOHg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.0.1\",\n          \"Microsoft.Win32.Primitives\": \"4.0.1\",\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.Handles\": \"4.0.1\",\n          \"System.Runtime.InteropServices\": \"4.1.0\",\n          \"System.Security.Claims\": \"4.0.1\",\n          \"System.Security.Principal\": \"4.0.1\",\n          \"System.Text.Encoding\": \"4.0.11\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encodings.Web\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"TWZnuiJgPDAEEUfobD7njXvSVR2Toz+jvKWds6yL4oSztmKQfnWzucczjzA+6Dv1bktBdY71sZW1YN0X6m9chQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.IO\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Thread\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"gIdJqDXlOr5W9zeqFErLw3dsOsiShSCYtF9SEHitACycmvNvY8odf9kiKvp6V7aibc8C4HzzNBkWXjyfn7plbQ==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Threading.ThreadPool\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.10\",\n        \"contentHash\": \"IMXgB5Vf/5Qw1kpoVgJMOvUO1l32aC+qC3OaIZjWJOjvcxuxNWOK2ZTWWYXfij22NHxT2j1yWX5vlAeQWld9vA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Handles\": \"4.0.1\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"NETStandard.Library\": \"[1.6.1, )\",\n          \"Newtonsoft.Json\": \"[9.0.1, )\",\n          \"System.Threading.Thread\": \"[4.0.0, )\",\n          \"System.Threading.ThreadPool\": \"[4.0.10, )\"\n        }\n      },\n      \"hangfire.netcore\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Hangfire.Core\": \"[1.0.0, )\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"[1.0.0, )\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"[1.0.0, )\",\n          \"NETStandard.Library\": \"[1.6.1, )\"\n        }\n      }\n    },\n    \".NETStandard,Version=v2.0\": {\n      \"Microsoft.AspNetCore.Antiforgery\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.0, )\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"BFdjKs38tu7UEHhe1eyZ340+oVfusWhtYGGrOKB/JmjAO8nfaF3NrT6oGUVyXGaZzWxTsdJr9BhsEEN/GoQxkQ==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.DataProtection\": \"2.0.0\",\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.0.0\",\n          \"Microsoft.AspNetCore.Http.Extensions\": \"2.0.0\",\n          \"Microsoft.AspNetCore.WebUtilities\": \"2.0.0\",\n          \"Microsoft.Extensions.ObjectPool\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.0, )\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"pblZLY7IfNqhQ5wwGQ0vNq2mG6W5YgZI1fk7suEuwZsGxGEADNBAyNlTALM9L8nMXdvbp6aHP/t4wHrFpcL3Sw==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"2.0.0\",\n          \"System.Text.Encodings.Web\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.3, )\",\n        \"resolved\": \"2.0.3\",\n        \"contentHash\": \"st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.AspNetCore.Cryptography.Internal\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"SY6GQyZZ5o09rqFmy3nhyJzx3lkFDBl0wO2Kb7EoLCPyH6dC7KB+QXysHfa9P5jHPiYB9VEkcQ9H7kQKcXQ1sw==\"\n      },\n      \"Microsoft.AspNetCore.DataProtection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"CjRLA26BpKrzBqpw1g9F3rGYNGisPd+zsnYdpJbHsjH4iIbi/OHfgKzGdHZCwmfQWrlL4e8Q0SpS+DMvgf6Jpg==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Cryptography.Internal\": \"2.0.0\",\n          \"Microsoft.AspNetCore.DataProtection.Abstractions\": \"2.0.0\",\n          \"Microsoft.AspNetCore.Hosting.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Options\": \"2.0.0\",\n          \"Microsoft.Win32.Registry\": \"4.4.0\",\n          \"System.Security.Cryptography.Xml\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.DataProtection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"BiFPWLZTKw253oQ5lAXcCkFkNFSRNi8fDCUB2yOTQyuYVMR8pnBAhVJ37o/E6bnuFYrE6eFCU4iDYrShmBIBYA==\"\n      },\n      \"Microsoft.AspNetCore.Hosting.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"IR2zlm3d/CmYbkw+cMM7M6mUAi+xsFUPfWqGYqzZVC5o6jX3xD2Z4Uf44UBaWKMBf5Z7q9dodIdXxwFPF2Hxhg==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Hosting.Server.Abstractions\": \"2.0.0\",\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Hosting.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Hosting.Server.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"v2H65ix/O11HKoxhKQpljtozsD5/1tqeXr3TYnrLgfAPIsp6kTFxIcTSENoxtew7h9X14ENqUf2lBCkyCNRUuQ==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Features\": \"2.0.0\",\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"lA7Bwvur19MhXrlW0w+WBXONJMSFYY5kNazflz4MNwMZMtzwHxNA6fC5sQsssYd/XvA0gMyKwp52s68uuKLR1w==\",\n        \"dependencies\": {\n          \"Microsoft.AspNetCore.Http.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"2.0.0\",\n          \"Microsoft.Net.Http.Headers\": \"2.0.0\",\n          \"System.Buffers\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.Http.Features\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"yk62muzFTZTKCQuo3nmVPkPvGBlM2qbdSxbX62TufuONuKQrTGQ/SwhwBbYutk5/YY7u4HETu0n9BKOn7mMgmA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.AspNetCore.WebUtilities\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"RqDEwy7jdHJ0NunWydSzJrpODnsF7NPdB0KaRdG60H1bMEt4DbjcWkUb+XxjZ15uWCMi7clTQClpPuIFLwD1yQ==\",\n        \"dependencies\": {\n          \"Microsoft.Net.Http.Headers\": \"2.0.0\",\n          \"System.Text.Encodings.Web\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"vvVR/B08YVghQ4jHEloxqw2ZWzEGE1AOA5E0DioUM3ujbXz6FD3AfB/0Jl2ohJPd0nXYGwmPe1En6HTsSriq1A==\"\n      },\n      \"Microsoft.Extensions.Configuration.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"rHFrXqMIvQNq51H8RYTO4IWmDOYh8NUzyqGlh0xHWTP6XYnKk7Ryinys2uDs+Vu88b3AMlM3gBBSs78m6OQpYQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"eUdJ0Q/GfVyUJc0Jal5L1QZLceL78pvEM9wEKcHeI24KorqMDoVX+gWsMGLulQMfOwsUaPtkpQM2pFERTzSfSg==\"\n      },\n      \"Microsoft.Extensions.FileProviders.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"Z0AK+hmLO33WAXQ5P1uPzhH7z5yjDHX/XnUefXxE//SyvCb9x4cVjND24dT5566t/yzGp8/WLD7EG9KQKZZklQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Hosting.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"qPG6Ip/AdHxMJ7j3z8FkkpCbV8yjtiFpf/aOpN3TwfJWbtYpN+BKV8Q+pqPMgk7XZivcju9yARaEVCS++hWopA==\"\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==\"\n      },\n      \"Microsoft.Extensions.ObjectPool\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"drOmgNZCJiNEqFM/TvyqwtogS8wqoWGQCW5KB/CVGKL6VXHw8OOMdaHyspp8HPstP9UDnrnuq+8eaCaAcQg6tA==\"\n      },\n      \"Microsoft.Extensions.Options\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"sAKBgjl2gWsECBLLR9K54T7/uZaP2n9GhMYHay/oOLfvpvX0+iNAlQ2NJgVE352C9Fs5CDV3VbNTK8T2aNKQFA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"2.0.0\",\n          \"Microsoft.Extensions.Primitives\": \"2.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"ukg53qNlqTrK38WA30b5qhw0GD7y3jdI9PHHASjdKyTcBHTevFM2o23tyk3pWCgAV27Bbkm+CPQ2zUe1ZOuYSA==\",\n        \"dependencies\": {\n          \"System.Runtime.CompilerServices.Unsafe\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.Net.Http.Headers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"Rm9zeNCWyNrGnysHdRXJpNfeDVlPzzFuidSuRLRNvOrnw71vgNPlR4H9wHo2hG/oSaruukqNjK06MDQqb+eXhA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"2.0.0\",\n          \"System.Buffers\": \"4.4.0\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Microsoft.Win32.Registry\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"dA36TlNVn/XfrZtmf0fiI/z1nd3Wfp2QVzTdj26pqgP9LFWq0i1hYEUAW50xUjGFYn1+/cP3KGuxT2Yn1OUNBQ==\",\n        \"dependencies\": {\n          \"System.Security.AccessControl\": \"4.4.0\",\n          \"System.Security.Principal.Windows\": \"4.4.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.0.1\",\n        \"contentHash\": \"pNN4l+J6LlpIvHOeNdXlwxv39NPJ2B5klz+Rd2UQZIx30Squ5oND1Yy3wEAUoKn0GPUj6Yxt9lxlYWQqfZcvKg==\"\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==\"\n      },\n      \"System.Runtime.CompilerServices.Unsafe\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"9dLLuBxr5GNmOfl2jSMcsHuteEg32BEfUotmmUkmZjpR3RpVHE8YQwt0ow3p6prwA1ME8WqDVZqrr8z6H8G+Kw==\"\n      },\n      \"System.Security.AccessControl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"2NRFPX/V81ucKQmqNgGBZrKGH/5ejsvivSGMRum0SMgPnJxwhuNkzVS1+7gC3R2X0f57CtwrPrXPPSe6nOp82g==\",\n        \"dependencies\": {\n          \"System.Security.Principal.Windows\": \"4.4.0\"\n        }\n      },\n      \"System.Security.Cryptography.Xml\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"1Xubvo4i+K+DO6YzVh6vBKmCl5xx/cAoiJEze6VQ+XwVQU25KQC9pPrmniz2EbbJnmoQ5Rm2FFjHsfQAi0Rs+Q==\"\n      },\n      \"System.Security.Principal.Windows\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"pP+AOzt1o3jESOuLmf52YQTF7H3Ng9hTnrOESQiqsnl2IbBh1HInsAMHYtoh75iUYV0OIkHmjvveraYB6zM97w==\"\n      },\n      \"System.Text.Encodings.Web\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"l/tYeikqMHX2MD2jzrHDfR9ejrpTTF7wvAEbR51AMvzip1wSJgiURbDik4iv/w7ZgytmTD/hlwpplEhF9bmFNw==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.CSharp\": \"[4.4.0, )\",\n          \"Newtonsoft.Json\": \"[11.0.1, )\"\n        }\n      },\n      \"hangfire.netcore\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Hangfire.Core\": \"[1.0.0, )\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"[2.0.0, )\",\n          \"Microsoft.Extensions.Hosting.Abstractions\": \"[2.0.0, )\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"[2.0.0, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/Hangfire.Core/AppBuilderExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Dashboard;\nusing Hangfire.Dashboard.Owin;\nusing Hangfire.Logging;\nusing Hangfire.Server;\nusing Owin;\nusing Microsoft.Owin;\nusing Microsoft.Owin.Infrastructure;\n\nnamespace Hangfire\n{\n    using BuildFunc = Action<\n        Func<\n            IDictionary<string, object>,\n            Func<\n                Func<IDictionary<string, object>, Task>,\n                Func<IDictionary<string, object>, Task>\n        >>>;\n\n    /// <summary>\n    /// Provides extension methods for the <c>IAppBuilder</c> interface\n    /// defined in the <see href=\"https://www.nuget.org/packages/Owin/\">Owin</see> \n    /// NuGet package to simplify the integration with OWIN applications.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// \n    /// <para>This class simplifies Hangfire configuration in OWIN applications,\n    /// please read <see href=\"http://www.asp.net/aspnet/overview/owin-and-katana/getting-started-with-owin-and-katana\">\n    /// Getting Started with OWIN and Katana</see> if you aren't familiar with OWIN\n    /// and/or don't know what is the <c>Startup</c> class.\n    /// </para>\n    /// \n    /// <para>The methods of this class should be called from OWIN's <c>Startup</c> \n    /// class.</para>\n    /// \n    /// <h3>UseHangfireDashboard</h3>\n    /// <para>Dashboard UI contains pages that allow you to monitor almost every\n    /// aspect of background processing. It is exposed as an OWIN middleware that \n    /// intercepts requests to the given path.</para>\n    /// <para>OWIN implementation of Dashboard UI allows to use it outside of web\n    /// applications, including console applications and Windows Services.</para>\n    /// <note type=\"important\">\n    /// By default, an access to the Dashboard UI is restricted <b>only to local\n    /// requests</b> for security reasons. Before publishing a project to\n    /// production, make sure you still have access to the Dashboard UI by using the\n    /// <see href=\"https://www.nuget.org/packages/Hangfire.Dashboard.Authorization/\">\n    /// Hangfire.Dashboard.Authorization</see> package.</note>\n    /// \n    /// <h3>UseHangfireServer</h3>\n    /// <para>In addition to creation of a new instance of the <see cref=\"BackgroundJobServer\"/> \n    /// class, these methods also register the call to its <see cref=\"BackgroundJobServer.Dispose\"/> \n    /// method on application shutdown. This is done via registering a callback on the corresponding \n    /// <see cref=\"CancellationToken\"/> from OWIN environment (<c>\"host.OnAppDisposing\"</c> or \n    /// <c>\"server.OnDispose\"</c> keys).</para>\n    /// <para>This enables <i>graceful shutdown</i> feature for background jobs and background processes\n    /// without any additional configuration.</para>\n    /// <para>Please see <see cref=\"BackgroundJobServer\"/> for more details regarding\n    /// background processing.</para>\n    /// </remarks>\n    /// \n    /// <example>\n    /// <h3>Basic Configuration</h3> \n    /// <para>Basic setup in an OWIN application looks like the following example. Please note\n    /// that job storage should be configured before using the methods of this class.</para>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\AppBuilderExtensions.cs\" region=\"Basic Setup\" />\n    /// \n    /// <h3>Adding Dashboard Only</h3>\n    /// <para>If you want to install dashboard without starting a background job server, for example,\n    /// to process background jobs outside of your web application, call only the\n    /// <see cref=\"O:Hangfire.AppBuilderExtensions.UseHangfireDashboard\"/>.</para>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\AppBuilderExtensions.cs\" region=\"Dashboard Only\" />\n    /// \n    /// <h3>Change Dashboard Path</h3>\n    /// <para>By default, you can access Dashboard UI by hitting the <i>http(s)://&lt;app&gt;/hangfire</i>\n    /// URL, however you can change it as in the following example.</para>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\AppBuilderExtensions.cs\" region=\"Change Dashboard Path\" />\n    /// \n    /// <h3>Configuring Authorization</h3>\n    /// <para>The following example demonstrates how to change default local-requests-only\n    /// authorization for Dashboard UI.</para>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\AppBuilderExtensions.cs\" region=\"Configuring Authorization\" />\n    /// \n    /// <h3>Changing Application Path</h3>\n    /// <para>Have you seen the <i>Back to site</i> button in the Dashboard? By default it leads\n    /// you to the root of your site, but you can configure the behavior.</para>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\AppBuilderExtensions.cs\" region=\"Change Application Path\" />\n    /// \n    /// <h3>Multiple Dashboards</h3>\n    /// <para>The following example demonstrates adding multiple Dashboard UI endpoints. This may\n    /// be useful when you are using multiple shards for your background processing needs.</para>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\AppBuilderExtensions.cs\" region=\"Multiple Dashboards\" />\n    /// \n    /// </example>\n    /// \n    /// <seealso cref=\"BackgroundJobServer\"/>\n    /// <seealso cref=\"Hangfire.Dashboard\"/>\n    /// <seealso href=\"https://www.nuget.org/packages/Hangfire.Dashboard.Authorization/\">\n    /// Hangfire.Dashboard.Authorization Package\n    /// </seealso>\n    /// \n    /// <threadsafety static=\"true\" instance=\"false\" />\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public static class AppBuilderExtensions\n    {\n        // Prevent GC to collect background processing servers in hosts that do\n        // not support shutdown notifications. Dictionary is used as a Set.\n        private static readonly ConcurrentDictionary<IBackgroundProcessingServer, object> Servers\n            = new ConcurrentDictionary<IBackgroundProcessingServer, object>();\n\n        /// <summary>\n        /// Creates a new instance of the <see cref=\"BackgroundJobServer\"/> class\n        /// with default options and <see cref=\"JobStorage.Current\"/> storage and\n        /// registers its disposal on application shutdown.\n        /// </summary>\n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"InvalidOperationException\">\n        /// OWIN environment does not contain the application shutdown cancellation token.\n        /// </exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireServer([NotNull] this IAppBuilder builder)\n        {\n            return builder.UseHangfireServer(new BackgroundJobServerOptions());\n        }\n\n        /// <summary>\n        /// Creates a new instance of the <see cref=\"BackgroundJobServer\"/> class \n        /// with the given collection of additional background processes and \n        /// <see cref=\"JobStorage.Current\"/> storage, and registers its disposal\n        /// on application shutdown.\n        /// </summary>\n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// <param name=\"additionalProcesses\">Collection of additional background processes.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"additionalProcesses\"/> is null.</exception>\n        /// <exception cref=\"InvalidOperationException\">\n        /// OWIN environment does not contain the application shutdown cancellation token.\n        /// </exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireServer(\n            [NotNull] this IAppBuilder builder, \n            [NotNull] params IBackgroundProcess[] additionalProcesses)\n        {\n            return builder.UseHangfireServer(JobStorage.Current, new BackgroundJobServerOptions(), additionalProcesses);\n        }\n\n        /// <summary>\n        /// Creates a new instance of the <see cref=\"BackgroundJobServer\"/> class\n        /// with the specified options and <see cref=\"JobStorage.Current\"/> storage,\n        /// and registers its disposal on application shutdown.\n        /// </summary>\n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// <param name=\"options\">Options for background job server.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"options\"/> is null.</exception>\n        /// <exception cref=\"InvalidOperationException\">\n        /// OWIN environment does not contain the application shutdown cancellation token.\n        /// </exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireServer(\n            [NotNull] this IAppBuilder builder,\n            [NotNull] BackgroundJobServerOptions options)\n        {\n            return builder.UseHangfireServer(options, JobStorage.Current);\n        }\n\n        /// <summary>\n        /// Creates a new instance of the <see cref=\"BackgroundJobServer\"/> class\n        /// with the specified options, given collection of background processes\n        /// and <see cref=\"JobStorage.Current\"/> storage, and registers its\n        /// disposal on application shutdown.\n        /// </summary>\n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// <param name=\"options\">Options for background job server.</param>\n        /// <param name=\"additionalProcesses\">Collection of additional background processes.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"options\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"additionalProcesses\"/> is null.</exception>\n        /// <exception cref=\"InvalidOperationException\">\n        /// OWIN environment does not contain the application shutdown cancellation token.\n        /// </exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireServer(\n            [NotNull] this IAppBuilder builder,\n            [NotNull] BackgroundJobServerOptions options,\n            [NotNull] params IBackgroundProcess[] additionalProcesses)\n        {\n            return builder.UseHangfireServer(JobStorage.Current, options, additionalProcesses);\n        }\n\n        /// <summary>\n        /// Creates a new instance of the <see cref=\"BackgroundJobServer\"/> class\n        /// with the given options and specified storage, and registers its disposal\n        /// on application shutdown.\n        /// </summary>\n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// <param name=\"options\">Options for background job server.</param>\n        /// <param name=\"storage\">Storage to use by background job server.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"storage\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"options\"/> is null.</exception>\n        /// <exception cref=\"InvalidOperationException\">\n        /// OWIN environment does not contain the application shutdown cancellation token.\n        /// </exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireServer(\n            [NotNull] this IAppBuilder builder,\n            [NotNull] BackgroundJobServerOptions options,\n            [NotNull] JobStorage storage)\n        {\n            return builder.UseHangfireServer(storage, options);\n        }\n\n        /// <summary>\n        /// Starts a new instance of the <see cref=\"BackgroundJobServer\"/> class with\n        /// the given arguments, and registers its disposal on application shutdown.\n        /// </summary>\n        /// \n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// <param name=\"storage\">Storage to use by background job server.</param>\n        /// <param name=\"options\">Options for background job server.</param>\n        /// <param name=\"additionalProcesses\">Collection of additional background processes.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"storage\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"options\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"additionalProcesses\"/> is null.</exception>\n        /// <exception cref=\"InvalidOperationException\">\n        /// OWIN environment does not contain the application shutdown cancellation token.\n        /// </exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireServer(\n            [NotNull] this IAppBuilder builder,\n            [NotNull] JobStorage storage,\n            [NotNull] BackgroundJobServerOptions options, \n            [NotNull] params IBackgroundProcess[] additionalProcesses)\n        {\n            if (builder == null) throw new ArgumentNullException(nameof(builder));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n            if (additionalProcesses == null) throw new ArgumentNullException(nameof(additionalProcesses));\n\n            return UseHangfireServer(builder, new BackgroundJobServer(options, storage, additionalProcesses));\n        }\n\n        /// <summary>\n        /// Registers the given custom instance of the <see cref=\"IBackgroundProcessingServer\"/>\n        /// interface for disposal on application shutdown.\n        /// </summary>\n        /// \n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// <param name=\"server\">Custom background processing server instance.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"server\"/> is null.</exception>\n        /// <exception cref=\"InvalidOperationException\">\n        /// OWIN environment does not contain the application shutdown cancellation token.\n        /// </exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireServer(\n            [NotNull] this IAppBuilder builder,\n            [NotNull] IBackgroundProcessingServer server)\n        {\n            if (builder == null) throw new ArgumentNullException(nameof(builder));\n            if (server == null) throw new ArgumentNullException(nameof(server));\n\n            Servers.TryAdd(server, null);\n\n            var context = new OwinContext(builder.Properties);\n            var token = context.Get<CancellationToken>(\"host.OnAppDisposing\");\n            if (token == default(CancellationToken))\n            {\n                // https://github.com/owin/owin/issues/27\n                token = context.Get<CancellationToken>(\"server.OnDispose\");\n            }\n\n            if (token == default(CancellationToken))\n            {\n                throw new InvalidOperationException(\n                    \"Current OWIN environment does not contain an instance of the `CancellationToken` class neither under `host.OnAppDisposing`, nor `server.OnDispose` key.\\r\\n\"\n                    + \"Please use another OWIN host or create an instance of the `BackgroundJobServer` class manually.\");\n            }\n\n            token.Register(OnAppDisposing, server);\n            return builder;\n        }\n\n        private static void OnAppDisposing(object state)\n        {\n            var logger = LogProvider.GetLogger(typeof(AppBuilderExtensions));\n            logger.Info(\"Web application is shutting down via OWIN's host.OnAppDisposing callback.\");\n\n            ((IDisposable) state).Dispose();\n\n            if (state is IBackgroundProcessingServer server)\n                Servers.TryRemove(server, out _);\n        }\n\n        /// <summary>\n        /// Adds Dashboard UI middleware to the OWIN request processing pipeline under \n        /// the <c>/hangfire</c> path, for the <see cref=\"JobStorage.Current\"/> storage.\n        /// </summary>\n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireDashboard([NotNull] this IAppBuilder builder)\n        {\n            return builder.UseHangfireDashboard(\"/hangfire\");\n        }\n\n        /// <summary>\n        /// Adds Dashboard UI middleware to the OWIN request processing pipeline under\n        /// the given path, for the <see cref=\"JobStorage.Current\"/> storage.\n        /// </summary>\n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// <param name=\"pathMatch\">Path prefix for middleware to use, e.g. \"/hangfire\".</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"pathMatch\"/> is null.</exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireDashboard(\n            [NotNull] this IAppBuilder builder,\n            [NotNull] string pathMatch)\n        {\n            return builder.UseHangfireDashboard(pathMatch, new DashboardOptions());\n        }\n\n        /// <summary>\n        /// Adds Dashboard UI middleware to the OWIN request processing pipeline under\n        /// the specified path and the given options, for the <see cref=\"JobStorage.Current\"/>\n        /// storage.\n        /// </summary>\n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// <param name=\"pathMatch\">Path prefix for middleware to use, e.g. \"/hangfire\".</param>\n        /// <param name=\"options\">Options for Dashboard UI.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"pathMatch\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"options\"/> is null.</exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireDashboard(\n            [NotNull] this IAppBuilder builder,\n            [NotNull] string pathMatch,\n            [NotNull] DashboardOptions options)\n        {\n            return builder.UseHangfireDashboard(pathMatch, options, JobStorage.Current);\n        }\n\n        /// <summary>\n        /// Adds Dashboard UI middleware to the OWIN request processing pipeline with the\n        /// specified parameters.\n        /// </summary>\n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// <param name=\"pathMatch\">Path prefix for middleware to use, e.g. \"/hangfire\".</param>\n        /// <param name=\"options\">Options for Dashboard UI.</param>\n        /// <param name=\"storage\">Job storage to use by Dashboard IO.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"pathMatch\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"options\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"storage\"/> is null.</exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireDashboard(\n            [NotNull] this IAppBuilder builder,\n            [NotNull] string pathMatch,\n            [NotNull] DashboardOptions options,\n            [NotNull] JobStorage storage)\n        {\n            return builder.UseHangfireDashboard(pathMatch, options, storage, null);\n        }\n\n        /// <summary>\n        /// Adds Dashboard UI middleware to the OWIN request processing pipeline with the\n        /// specified parameters and antiforgery service.\n        /// </summary>\n        /// <param name=\"builder\">OWIN application builder.</param>\n        /// <param name=\"pathMatch\">Path prefix for middleware to use, e.g. \"/hangfire\".</param>\n        /// <param name=\"options\">Options for Dashboard UI.</param>\n        /// <param name=\"storage\">Job storage to use by Dashboard IO.</param>\n        /// <param name=\"antiforgery\">Antiforgery service.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"builder\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"pathMatch\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"options\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"storage\"/> is null.</exception>\n        /// \n        /// <remarks>\n        /// Please see <see cref=\"AppBuilderExtensions\"/> for details and examples.\n        /// </remarks>\n        public static IAppBuilder UseHangfireDashboard(\n                [NotNull] this IAppBuilder builder,\n                [NotNull] string pathMatch,\n                [NotNull] DashboardOptions options,\n                [NotNull] JobStorage storage,\n                [CanBeNull] IOwinDashboardAntiforgery antiforgery)\n        {\n            if (builder == null) throw new ArgumentNullException(nameof(builder));\n            if (pathMatch == null) throw new ArgumentNullException(nameof(pathMatch));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n\n            SignatureConversions.AddConversions(builder);\n\n            builder.Map(pathMatch, subApp => subApp\n                .UseOwin()\n                .UseHangfireDashboard(options, storage, DashboardRoutes.Routes, antiforgery));\n\n            return builder;\n        }\n\n        private static BuildFunc UseOwin(this IAppBuilder builder)\n        {\n            return middleware => builder.Use(middleware(builder.Properties));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/App_Packages/LibLog.1.4/LibLog.cs",
    "content": "//===============================================================================\n// LibLog\n//\n// https://github.com/damianh/LibLog\n//===============================================================================\n// Copyright © 2011-2014 Damian Hickey.  All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//===============================================================================\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing System.Threading;\nusing Hangfire.Logging.LogProviders;\n\n// ReSharper disable All\n\nnamespace Hangfire.Logging\n{\n    using System.Collections.Generic;\n    using System;\n    using System.Diagnostics;\n    using System.Globalization;\n\n    /// <summary>\n    /// Simple interface that represent a logger.\n    /// </summary>\n    public interface ILog\n    {\n        /// <summary>\n        /// Log a message the specified log level.\n        /// </summary>\n        /// <param name=\"logLevel\">The log level.</param>\n        /// <param name=\"messageFunc\">The message function.</param>\n        /// <param name=\"exception\">An optional exception.</param>\n        /// <returns>true if the message was logged. Otherwise false.</returns>\n        /// <remarks>\n        /// Note to implementers: the message func should not be called if the loglevel is not enabled\n        /// so as not to incur performance penalties.\n        /// \n        /// To check IsEnabled call Log with only LogLevel and check the return value, no event will be written\n        /// </remarks>\n        bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception = null);\n    }\n\n    /// <summary>\n    /// The log level.\n    /// </summary>\n    public enum LogLevel\n    {\n        Trace,\n        Debug,\n        Info,\n        Warn,\n        Error,\n        Fatal\n    }\n\n    public static class LogExtensions\n    {\n        public static bool IsDebugEnabled(this ILog logger)\n        {\n            GuardAgainstNullLogger(logger);\n            return logger.Log(LogLevel.Debug, null);\n        }\n\n        public static bool IsErrorEnabled(this ILog logger)\n        {\n            GuardAgainstNullLogger(logger);\n            return logger.Log(LogLevel.Error, null);\n        }\n\n        public static bool IsFatalEnabled(this ILog logger)\n        {\n            GuardAgainstNullLogger(logger);\n            return logger.Log(LogLevel.Fatal, null);\n        }\n\n        public static bool IsInfoEnabled(this ILog logger)\n        {\n            GuardAgainstNullLogger(logger);\n            return logger.Log(LogLevel.Info, null);\n        }\n\n        public static bool IsTraceEnabled(this ILog logger)\n        {\n            GuardAgainstNullLogger(logger);\n            return logger.Log(LogLevel.Trace, null);\n        }\n\n        public static bool IsWarnEnabled(this ILog logger)\n        {\n            GuardAgainstNullLogger(logger);\n            return logger.Log(LogLevel.Warn, null);\n        }\n\n        public static void Debug(this ILog logger, Func<string> messageFunc)\n        {\n            GuardAgainstNullLogger(logger);\n            logger.Log(LogLevel.Debug, messageFunc);\n        }\n\n        public static void Debug(this ILog logger, string message)\n        {\n            if (logger.IsDebugEnabled())\n            {\n                logger.Log(LogLevel.Debug, message.AsFunc());\n            }\n        }\n\n        public static void DebugFormat(this ILog logger, string message, params object[] args)\n        {\n            if (logger.IsDebugEnabled())\n            {\n                logger.LogFormat(LogLevel.Debug, message, args);\n            }\n        }\n\n        public static void DebugException(this ILog logger, string message, Exception exception)\n        {\n            if (logger.IsDebugEnabled())\n            {\n                logger.Log(LogLevel.Debug, message.AsFunc(), exception);\n            }\n        }\n\n        public static void Error(this ILog logger, Func<string> messageFunc)\n        {\n            logger.Log(LogLevel.Error, messageFunc);\n        }\n\n        public static void Error(this ILog logger, string message)\n        {\n            if (logger.IsErrorEnabled())\n            {\n                logger.Log(LogLevel.Error, message.AsFunc());\n            }\n        }\n\n        public static void ErrorFormat(this ILog logger, string message, params object[] args)\n        {\n            if (logger.IsErrorEnabled())\n            {\n                logger.LogFormat(LogLevel.Error, message, args);\n            }\n        }\n\n        public static void ErrorException(this ILog logger, string message, Exception exception)\n        {\n            if (logger.IsErrorEnabled())\n            {\n                logger.Log(LogLevel.Error, message.AsFunc(), exception);\n            }\n        }\n\n        public static void Fatal(this ILog logger, Func<string> messageFunc)\n        {\n            logger.Log(LogLevel.Fatal, messageFunc);\n        }\n\n        public static void Fatal(this ILog logger, string message)\n        {\n            if (logger.IsFatalEnabled())\n            {\n                logger.Log(LogLevel.Fatal, message.AsFunc());\n            }\n        }\n\n        public static void FatalFormat(this ILog logger, string message, params object[] args)\n        {\n            if (logger.IsFatalEnabled())\n            {\n                logger.LogFormat(LogLevel.Fatal, message, args);\n            }\n        }\n\n        public static void FatalException(this ILog logger, string message, Exception exception)\n        {\n            if (logger.IsFatalEnabled())\n            {\n                logger.Log(LogLevel.Fatal, message.AsFunc(), exception);\n            }\n        }\n\n        public static void Info(this ILog logger, Func<string> messageFunc)\n        {\n            GuardAgainstNullLogger(logger);\n            logger.Log(LogLevel.Info, messageFunc);\n        }\n\n        public static void Info(this ILog logger, string message)\n        {\n            if (logger.IsInfoEnabled())\n            {\n                logger.Log(LogLevel.Info, message.AsFunc());\n            }\n        }\n\n        public static void InfoFormat(this ILog logger, string message, params object[] args)\n        {\n            if (logger.IsInfoEnabled())\n            {\n                logger.LogFormat(LogLevel.Info, message, args);\n            }\n        }\n\n        public static void InfoException(this ILog logger, string message, Exception exception)\n        {\n            if (logger.IsInfoEnabled())\n            {\n                logger.Log(LogLevel.Info, message.AsFunc(), exception);\n            }\n        }\n\n        public static void Trace(this ILog logger, Func<string> messageFunc)\n        {\n            GuardAgainstNullLogger(logger);\n            logger.Log(LogLevel.Trace, messageFunc);\n        }\n\n        public static void Trace(this ILog logger, string message)\n        {\n            if (logger.IsTraceEnabled())\n            {\n                logger.Log(LogLevel.Trace, message.AsFunc());\n            }\n        }\n\n        public static void TraceFormat(this ILog logger, string message, params object[] args)\n        {\n            if (logger.IsTraceEnabled())\n            {\n                logger.LogFormat(LogLevel.Trace, message, args);\n            }\n        }\n\n        public static void TraceException(this ILog logger, string message, Exception exception)\n        {\n            if (logger.IsTraceEnabled())\n            {\n                logger.Log(LogLevel.Trace, message.AsFunc(), exception);\n            }\n        }\n\n        public static void Warn(this ILog logger, Func<string> messageFunc)\n        {\n            GuardAgainstNullLogger(logger);\n            logger.Log(LogLevel.Warn, messageFunc);\n        }\n\n        public static void Warn(this ILog logger, string message)\n        {\n            if (logger.IsWarnEnabled())\n            {\n                logger.Log(LogLevel.Warn, message.AsFunc());\n            }\n        }\n\n        public static void WarnFormat(this ILog logger, string message, params object[] args)\n        {\n            if (logger.IsWarnEnabled())\n            {\n                logger.LogFormat(LogLevel.Warn, message, args);\n            }\n        }\n\n        public static void WarnException(this ILog logger, string message, Exception exception)\n        {\n            if (logger.IsWarnEnabled())\n            {\n                logger.Log(LogLevel.Warn, message.AsFunc(), exception);\n            }\n        }\n\n        private static void GuardAgainstNullLogger(ILog logger)\n        {\n            if (logger == null)\n            {\n                throw new ArgumentNullException(nameof(logger));\n            }\n        }\n\n        private static void LogFormat(this ILog logger, LogLevel logLevel, string message, params object[] args)\n        {\n            var result = string.Format(CultureInfo.InvariantCulture, message, args);\n            logger.Log(logLevel, result.AsFunc());\n        }\n\n        // Avoid the closure allocation, see https://gist.github.com/AArnott/d285feef75c18f6ecd2b\n        private static Func<T> AsFunc<T>(this T value) where T : class\n        {\n            return value.Return;\n        }\n\n        private static T Return<T>(this T value)\n        {\n            return value;\n        }\n    }\n\n    /// <summary>\n    /// Represents a way to get a <see cref=\"ILog\"/>\n    /// </summary>\n    public interface ILogProvider\n    {\n        ILog GetLogger(string name);\n    }\n\n\n    /// <summary>\n    /// Provides a mechanism to create instances of <see cref=\"ILog\" /> objects.\n    /// </summary>\n    public static class LogProvider\n    {\n        private static ILogProvider _currentLogProvider;\n\n        /// <summary>\n        /// Gets a logger for the specified type.\n        /// </summary>\n        /// <typeparam name=\"T\">The type whose name will be used for the logger.</typeparam>\n        /// <returns>An instance of <see cref=\"ILog\"/></returns>\n        public static ILog For<T>()\n        {\n            return GetLogger(typeof(T));\n        }\n\n#if !NETSTANDARD1_3\n        /// <summary>\n        /// Gets a logger for the current class.\n        /// </summary>\n        /// <returns>An instance of <see cref=\"ILog\"/></returns>\n        public static ILog GetCurrentClassLogger()\n        {\n            var stackFrame = new StackFrame(1, false);\n            return GetLogger(stackFrame.GetMethod().DeclaringType);\n        }\n#endif\n\n        /// <summary>\n        /// Gets a logger for the specified type.\n        /// </summary>\n        /// <param name=\"type\">The type whose name will be used for the logger.</param>\n        /// <returns>An instance of <see cref=\"ILog\"/></returns>\n        public static ILog GetLogger(Type type)\n        {\n            return GetLogger(type.FullName);\n        }\n\n        /// <summary>\n        /// Gets a logger with the specified name.\n        /// </summary>\n        /// <param name=\"name\">The name.</param>\n        /// <returns>An instance of <see cref=\"ILog\"/></returns>\n        public static ILog GetLogger(string name)\n        {\n            ILogProvider logProvider = Volatile.Read(ref _currentLogProvider) ?? ResolveLogProvider();\n            return logProvider == null ? new NoOpLogger() : (ILog)new LoggerExecutionWrapper(logProvider.GetLogger(name));\n        }\n\n        /// <summary>\n        /// Sets the current log provider.\n        /// </summary>\n        /// <param name=\"logProvider\">The log provider.</param>\n        public static void SetCurrentLogProvider(ILogProvider logProvider)\n        {\n            Volatile.Write(ref _currentLogProvider, logProvider);\n        }\n\n        internal delegate bool IsLoggerAvailable();\n\n        internal delegate ILogProvider CreateLogProvider();\n\n        internal static readonly List<Tuple<IsLoggerAvailable, CreateLogProvider>> LogProviderResolvers =\n            new List<Tuple<IsLoggerAvailable, CreateLogProvider>>\n        {\n            new Tuple<IsLoggerAvailable, CreateLogProvider>(SerilogLogProvider.IsLoggerAvailable, static () => new SerilogLogProvider()),\n            new Tuple<IsLoggerAvailable, CreateLogProvider>(NLogLogProvider.IsLoggerAvailable, static () => new NLogLogProvider()),\n            new Tuple<IsLoggerAvailable, CreateLogProvider>(Log4NetLogProvider.IsLoggerAvailable, static () => new Log4NetLogProvider()),\n#if !NETSTANDARD1_3\n            new Tuple<IsLoggerAvailable, CreateLogProvider>(EntLibLogProvider.IsLoggerAvailable, static () => new EntLibLogProvider()),\n            new Tuple<IsLoggerAvailable, CreateLogProvider>(LoupeLogProvider.IsLoggerAvailable, static () => new LoupeLogProvider()),\n            new Tuple<IsLoggerAvailable, CreateLogProvider>(ElmahLogProvider.IsLoggerAvailable, static () => new ElmahLogProvider()),\n#endif\n        };\n\n        private static ILogProvider ResolveLogProvider()\n        {\n            try\n            {\n                foreach (var providerResolver in LogProviderResolvers)\n                {\n                    if (providerResolver.Item1())\n                    {\n                        return providerResolver.Item2();\n                    }\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                Console.WriteLine(\n                    \"Exception occured resolving a log provider. Logging for this assembly {0} is disabled. {1}\",\n                    typeof(LogProvider).GetTypeInfo().Assembly.FullName,\n                    ex);\n            }\n            return null;\n        }\n\n        internal sealed class NoOpLogProvider : ILogProvider\n        {\n            public static readonly NoOpLogProvider Instance = new NoOpLogProvider();\n\n            public ILog GetLogger(string name)\n            {\n                return NoOpLogger.Instance;\n            }\n        }\n\n        internal sealed class NoOpLogger : ILog\n        {\n            public static readonly NoOpLogger Instance = new NoOpLogger();\n\n            public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                return false;\n            }\n        }\n    }\n\n    internal sealed class LoggerExecutionWrapper : ILog\n    {\n        private readonly ILog _logger;\n        public const string FailedToGenerateLogMessage = \"Failed to generate log message\";\n\n        public ILog WrappedLogger\n        {\n            get { return _logger; }\n        }\n\n        public LoggerExecutionWrapper(ILog logger)\n        {\n            _logger = logger;\n        }\n\n        public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception = null)\n        {\n            if (messageFunc == null)\n            {\n                return _logger.Log(logLevel, null);\n            }\n\n            Func<string> wrappedMessageFunc = () =>\n            {\n                try\n                {\n                    return messageFunc();\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    Log(LogLevel.Error, static () => FailedToGenerateLogMessage, ex);\n                }\n                return null;\n            };\n            return _logger.Log(logLevel, wrappedMessageFunc, exception);\n        }\n    }\n}\n\nnamespace Hangfire.Logging.LogProviders\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Diagnostics;\n    using System.Globalization;\n    using System.Linq.Expressions;\n    using System.Reflection;\n    using System.Text;\n\n    public class NLogLogProvider : ILogProvider\n    {\n        private readonly Func<string, object> _getLoggerByNameDelegate;\n        private static bool _providerIsAvailableOverride = true;\n\n        public NLogLogProvider()\n        {\n            if (!IsLoggerAvailable())\n            {\n                throw new InvalidOperationException(\"NLog.LogManager not found\");\n            }\n            _getLoggerByNameDelegate = GetGetLoggerMethodCall();\n        }\n\n        public static bool ProviderIsAvailableOverride\n        {\n            get { return _providerIsAvailableOverride; }\n            set { _providerIsAvailableOverride = value; }\n        }\n\n        public ILog GetLogger(string name)\n        {\n            return new NLogLogger(_getLoggerByNameDelegate(name));\n        }\n\n        public static bool IsLoggerAvailable()\n        {\n            return ProviderIsAvailableOverride && GetLogManagerType() != null;\n        }\n\n        private static Type GetLogManagerType()\n        {\n            return Type.GetType(\"NLog.LogManager, NLog\");\n        }\n\n        private static Func<string, object> GetGetLoggerMethodCall()\n        {\n            Type logManagerType = GetLogManagerType();\n            MethodInfo method = logManagerType.GetRuntimeMethod(\"GetLogger\", new[] { typeof(string) });\n            ParameterExpression nameParam = Expression.Parameter(typeof(string), \"name\");\n            MethodCallExpression methodCall = Expression.Call(null, method, new Expression[] { nameParam });\n            return Expression.Lambda<Func<string, object>>(methodCall, new[] { nameParam }).Compile();\n        }\n\n        internal sealed class NLogLogger : ILog\n        {\n            private readonly dynamic _logger;\n\n            internal NLogLogger(dynamic logger)\n            {\n                _logger = logger;\n            }\n\n            public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                if (messageFunc == null)\n                {\n                    return IsLogLevelEnable(logLevel);\n                }\n                if (exception != null)\n                {\n                    return LogException(logLevel, messageFunc, exception);\n                }\n                switch (logLevel)\n                {\n                    case LogLevel.Debug:\n                        if (_logger.IsDebugEnabled)\n                        {\n                            _logger.Debug(messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Info:\n                        if (_logger.IsInfoEnabled)\n                        {\n                            _logger.Info(messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Warn:\n                        if (_logger.IsWarnEnabled)\n                        {\n                            _logger.Warn(messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Error:\n                        if (_logger.IsErrorEnabled)\n                        {\n                            _logger.Error(messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Fatal:\n                        if (_logger.IsFatalEnabled)\n                        {\n                            _logger.Fatal(messageFunc());\n                            return true;\n                        }\n                        break;\n                    default:\n                        if (_logger.IsTraceEnabled)\n                        {\n                            _logger.Trace(messageFunc());\n                            return true;\n                        }\n                        break;\n                }\n                return false;\n            }\n\n            private bool LogException(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                switch (logLevel)\n                {\n                    case LogLevel.Debug:\n                        if (_logger.IsDebugEnabled)\n                        {\n                            _logger.DebugException(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Info:\n                        if (_logger.IsInfoEnabled)\n                        {\n                            _logger.InfoException(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Warn:\n                        if (_logger.IsWarnEnabled)\n                        {\n                            _logger.WarnException(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Error:\n                        if (_logger.IsErrorEnabled)\n                        {\n                            _logger.ErrorException(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Fatal:\n                        if (_logger.IsFatalEnabled)\n                        {\n                            _logger.FatalException(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                    default:\n                        if (_logger.IsTraceEnabled)\n                        {\n                            _logger.TraceException(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                }\n                return false;\n            }\n\n            private bool IsLogLevelEnable(LogLevel logLevel)\n            {\n                switch (logLevel)\n                {\n                    case LogLevel.Debug:\n                        return _logger.IsDebugEnabled;\n                    case LogLevel.Info:\n                        return _logger.IsInfoEnabled;\n                    case LogLevel.Warn:\n                        return _logger.IsWarnEnabled;\n                    case LogLevel.Error:\n                        return _logger.IsErrorEnabled;\n                    case LogLevel.Fatal:\n                        return _logger.IsFatalEnabled;\n                    default:\n                        return _logger.IsTraceEnabled;\n                }\n            }\n        }\n    }\n\n    public class Log4NetLogProvider : ILogProvider\n    {\n        private readonly Func<string, object> _getLoggerByNameDelegate;\n        private static bool _providerIsAvailableOverride = true;\n\n        public Log4NetLogProvider()\n        {\n            if (!IsLoggerAvailable())\n            {\n                throw new InvalidOperationException(\"log4net.LogManager not found\");\n            }\n            _getLoggerByNameDelegate = GetGetLoggerMethodCall();\n        }\n\n        public static bool ProviderIsAvailableOverride\n        {\n            get { return _providerIsAvailableOverride; }\n            set { _providerIsAvailableOverride = value; }\n        }\n\n        public ILog GetLogger(string name)\n        {\n            return new Log4NetLogger(_getLoggerByNameDelegate(name));\n        }\n\n        public static bool IsLoggerAvailable()\n        {\n            return ProviderIsAvailableOverride && GetLogManagerType() != null;\n        }\n\n        private static Type GetLogManagerType()\n        {\n            return Type.GetType(\"log4net.LogManager, log4net\");\n        }\n\n        private static Func<string, object> GetGetLoggerMethodCall()\n        {\n            Type logManagerType = GetLogManagerType();\n            MethodInfo method = logManagerType.GetRuntimeMethod(\"GetLogger\", new[] { typeof(Assembly), typeof(string) });\n            ParameterExpression nameParam = Expression.Parameter(typeof(string), \"name\");\n            MethodCallExpression methodCall = Expression.Call(null, method, new Expression[] { Expression.Constant(typeof(Log4NetLogProvider).GetTypeInfo().Assembly), nameParam });\n            return Expression.Lambda<Func<string, object>>(methodCall, new[] { nameParam }).Compile();\n        }\n\n        internal sealed class Log4NetLogger : ILog\n        {\n            private readonly dynamic _logger;\n\n            internal Log4NetLogger(dynamic logger)\n            {\n                _logger = logger;\n            }\n\n            public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                if (messageFunc == null)\n                {\n                    return IsLogLevelEnable(logLevel);\n                }\n                if (exception != null)\n                {\n                    return LogException(logLevel, messageFunc, exception);\n                }\n                switch (logLevel)\n                {\n                    case LogLevel.Info:\n                        if (_logger.IsInfoEnabled)\n                        {\n                            _logger.Info(messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Warn:\n                        if (_logger.IsWarnEnabled)\n                        {\n                            _logger.Warn(messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Error:\n                        if (_logger.IsErrorEnabled)\n                        {\n                            _logger.Error(messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Fatal:\n                        if (_logger.IsFatalEnabled)\n                        {\n                            _logger.Fatal(messageFunc());\n                            return true;\n                        }\n                        break;\n                    default:\n                        if (_logger.IsDebugEnabled)\n                        {\n                            _logger.Debug(messageFunc()); // Log4Net doesn't have a 'Trace' level, so all Trace messages are written as 'Debug'\n                            return true;\n                        }\n                        break;\n                }\n                return false;\n            }\n\n            private bool LogException(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                switch (logLevel)\n                {\n                    case LogLevel.Info:\n                        if (_logger.IsDebugEnabled)\n                        {\n                            _logger.Info(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Warn:\n                        if (_logger.IsWarnEnabled)\n                        {\n                            _logger.Warn(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Error:\n                        if (_logger.IsErrorEnabled)\n                        {\n                            _logger.Error(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Fatal:\n                        if (_logger.IsFatalEnabled)\n                        {\n                            _logger.Fatal(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                    default:\n                        if (_logger.IsDebugEnabled)\n                        {\n                            _logger.Debug(messageFunc(), exception);\n                            return true;\n                        }\n                        break;\n                }\n                return false;\n            }\n\n            private bool IsLogLevelEnable(LogLevel logLevel)\n            {\n                switch (logLevel)\n                {\n                    case LogLevel.Debug:\n                        return _logger.IsDebugEnabled;\n                    case LogLevel.Info:\n                        return _logger.IsInfoEnabled;\n                    case LogLevel.Warn:\n                        return _logger.IsWarnEnabled;\n                    case LogLevel.Error:\n                        return _logger.IsErrorEnabled;\n                    case LogLevel.Fatal:\n                        return _logger.IsFatalEnabled;\n                    default:\n                        return _logger.IsDebugEnabled;\n                }\n            }\n        }\n    }\n\n#if !NETSTANDARD1_3\n    public class EntLibLogProvider : ILogProvider\n    {\n        private const string TypeTemplate = \"Microsoft.Practices.EnterpriseLibrary.Logging.{0}, Microsoft.Practices.EnterpriseLibrary.Logging\";\n        private static bool _providerIsAvailableOverride = true;\n        private static readonly Type LogEntryType;\n        private static readonly Type LoggerType;\n        private readonly Action<string, string, TraceEventType> WriteLogEntry;\n        private Func<string, TraceEventType, bool> ShouldLogEntry;\n\n        static EntLibLogProvider()\n        {\n            LogEntryType = Type.GetType(string.Format(CultureInfo.InvariantCulture, TypeTemplate, \"LogEntry\"));\n            LoggerType = Type.GetType(string.Format(CultureInfo.InvariantCulture, TypeTemplate, \"Logger\"));\n        }\n\n        public EntLibLogProvider()\n        {\n            if (!IsLoggerAvailable())\n            {\n                throw new InvalidOperationException(\"Microsoft.Practices.EnterpriseLibrary.Logging.Logger not found\");\n            }\n\n            WriteLogEntry = GetWriteLogEntry();\n            ShouldLogEntry = GetShouldLogEntry();\n        }\n\n        public static bool ProviderIsAvailableOverride\n        {\n            get { return _providerIsAvailableOverride; }\n            set { _providerIsAvailableOverride = value; }\n        }\n\n        public ILog GetLogger(string name)\n        {\n            return new EntLibLogger(name, WriteLogEntry, ShouldLogEntry);\n        }\n\n        public static bool IsLoggerAvailable()\n        {\n            return ProviderIsAvailableOverride && LogEntryType != null;\n        }\n\n        private static Action<string, string, TraceEventType> GetWriteLogEntry()\n        {\n            // new LogEntry(...)\n            var logNameParameter = Expression.Parameter(typeof(string), \"logName\");\n            var messageParameter = Expression.Parameter(typeof(string), \"message\");\n            var severityParameter = Expression.Parameter(typeof(TraceEventType), \"severity\");\n\n            MemberInitExpression memberInit = GetWriteLogExpression(messageParameter, severityParameter, logNameParameter);\n\n            //Logger.Write(new LogEntry(....));\n            MethodInfo writeLogEntryMethod = LoggerType.GetMethod(\"Write\", new[] { LogEntryType });\n            var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit);\n\n            return Expression.Lambda<Action<string, string, TraceEventType>>(\n                writeLogEntryExpression,\n                logNameParameter,\n                messageParameter,\n                severityParameter).Compile();\n        }\n\n        private static Func<string, TraceEventType, bool> GetShouldLogEntry()\n        {\n            // new LogEntry(...)\n            var logNameParameter = Expression.Parameter(typeof(string), \"logName\");\n            var severityParameter = Expression.Parameter(typeof(TraceEventType), \"severity\");\n\n            MemberInitExpression memberInit = GetWriteLogExpression(Expression.Constant(\"***dummy***\"), severityParameter, logNameParameter);\n\n            //Logger.Write(new LogEntry(....));\n            MethodInfo writeLogEntryMethod = LoggerType.GetMethod(\"ShouldLog\", new[] { LogEntryType });\n            var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit);\n\n            return Expression.Lambda<Func<string, TraceEventType, bool>>(\n                writeLogEntryExpression,\n                logNameParameter,\n                severityParameter).Compile();\n        }\n\n        private static MemberInitExpression GetWriteLogExpression(Expression message,\n            ParameterExpression severityParameter, ParameterExpression logNameParameter)\n        {\n            var entryType = LogEntryType;\n            MemberInitExpression memberInit = Expression.MemberInit(Expression.New(entryType), new MemberBinding[]\n            {\n                Expression.Bind(entryType.GetProperty(\"Message\"), message),\n                Expression.Bind(entryType.GetProperty(\"Severity\"), severityParameter),\n                Expression.Bind(entryType.GetProperty(\"TimeStamp\"),\n                    Expression.Property(null, typeof (DateTime).GetProperty(\"UtcNow\"))),\n                Expression.Bind(entryType.GetProperty(\"Categories\"),\n                    Expression.ListInit(\n                        Expression.New(typeof (List<string>)),\n                        typeof (List<string>).GetMethod(\"Add\", new[] {typeof (string)}),\n                        logNameParameter))\n            });\n            return memberInit;\n        }\n\n        internal sealed class EntLibLogger : ILog\n        {\n            private readonly string _loggerName;\n            private readonly Action<string, string, TraceEventType> _writeLog;\n            private readonly Func<string, TraceEventType, bool> _shouldLog;\n\n            internal EntLibLogger(string loggerName, Action<string, string, TraceEventType> writeLog, Func<string, TraceEventType, bool> shouldLog)\n            {\n                _loggerName = loggerName;\n                _writeLog = writeLog;\n                _shouldLog = shouldLog;\n            }\n\n            public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                var severity = MapSeverity(logLevel);\n                if (messageFunc == null)\n                {\n                    return _shouldLog(_loggerName, severity);\n                }\n                if (exception != null)\n                {\n                    return LogException(logLevel, messageFunc, exception);\n                }\n                _writeLog(_loggerName, messageFunc(), severity);\n                return true;\n            }\n\n            public bool LogException(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                var severity = MapSeverity(logLevel);\n                var message = messageFunc() + Environment.NewLine + exception;\n                _writeLog(_loggerName, message, severity);\n                return true;\n            }\n\n            private static TraceEventType MapSeverity(LogLevel logLevel)\n            {\n                switch (logLevel)\n                {\n                    case LogLevel.Fatal:\n                        return TraceEventType.Critical;\n                    case LogLevel.Error:\n                        return TraceEventType.Error;\n                    case LogLevel.Warn:\n                        return TraceEventType.Warning;\n                    case LogLevel.Info:\n                        return TraceEventType.Information;\n                    default:\n                        return TraceEventType.Verbose;\n                }\n            }\n        }\n    }\n#endif\n\n    public class SerilogLogProvider : ILogProvider\n    {\n        private readonly Func<string, object> _getLoggerByNameDelegate;\n        private readonly SerilogCallbacks _callbacks;\n        private static bool _providerIsAvailableOverride = true;\n\n        public SerilogLogProvider()\n        {\n            if (!IsLoggerAvailable())\n            {\n                throw new InvalidOperationException(\"Serilog.Log not found\");\n            }\n            _getLoggerByNameDelegate = GetForContextMethodCall();\n            _callbacks = new SerilogCallbacks();\n        }\n\n        public static bool ProviderIsAvailableOverride\n        {\n            get { return _providerIsAvailableOverride; }\n            set { _providerIsAvailableOverride = value; }\n        }\n\n        public ILog GetLogger(string name)\n        {\n            return new SerilogLogger(_callbacks, _getLoggerByNameDelegate(name));\n        }\n\n        public static bool IsLoggerAvailable()\n        {\n            return ProviderIsAvailableOverride && GetLogManagerType() != null;\n        }\n\n        private static Type GetLogManagerType()\n        {\n            return Type.GetType(\"Serilog.Log, Serilog\");\n        }\n\n        private static Func<string, object> GetForContextMethodCall()\n        {\n            Type logManagerType = GetLogManagerType();\n            MethodInfo method = logManagerType.GetRuntimeMethod(\"ForContext\", new[] { typeof(string), typeof(object), typeof(bool) });\n            ParameterExpression propertyNameParam = Expression.Parameter(typeof(string), \"propertyName\");\n            ParameterExpression valueParam = Expression.Parameter(typeof(object), \"value\");\n            ParameterExpression destructureObjectsParam = Expression.Parameter(typeof(bool), \"destructureObjects\");\n            MethodCallExpression methodCall = Expression.Call(null, method, new Expression[]\n            {\n                propertyNameParam, \n                valueParam,\n                destructureObjectsParam\n            });\n            var func = Expression.Lambda<Func<string, object, bool, object>>(methodCall, new[]\n            {\n                propertyNameParam,\n                valueParam,\n                destructureObjectsParam\n            }).Compile();\n            return name => func(\"SourceContext\", name, false);\n        }\n\n        internal sealed class SerilogCallbacks\n        {\n            private static object[] EmptyArray = [];\n            public readonly object DebugLevel;\n            public readonly object ErrorLevel;\n            public readonly object FatalLevel;\n            public readonly object InformationLevel;\n            public readonly object VerboseLevel;\n            public readonly object WarningLevel;\n            public readonly Func<object, object, bool> IsEnabled;\n            public readonly Action<object, object, string> Write;\n            public readonly Action<object, object, Exception, string> WriteException;\n\n            public SerilogCallbacks()\n            {\n                var logEventTypeType = Type.GetType(\"Serilog.Events.LogEventLevel, Serilog\");\n                DebugLevel = Enum.Parse(logEventTypeType, \"Debug\");\n                ErrorLevel = Enum.Parse(logEventTypeType, \"Error\");\n                FatalLevel = Enum.Parse(logEventTypeType, \"Fatal\");\n                InformationLevel = Enum.Parse(logEventTypeType, \"Information\");\n                VerboseLevel = Enum.Parse(logEventTypeType, \"Verbose\");\n                WarningLevel = Enum.Parse(logEventTypeType, \"Warning\");\n\n                // Func<object, object, bool> isEnabled = (logger, level) => { return ((SeriLog.ILogger)logger).IsEnabled(level); }\n                var loggerType = Type.GetType(\"Serilog.ILogger, Serilog\");\n                MethodInfo isEnabledMethodInfo = loggerType.GetRuntimeMethod(\"IsEnabled\", new Type[] { logEventTypeType });\n                ParameterExpression instanceParam = Expression.Parameter(typeof(object));\n                UnaryExpression instanceCast = Expression.Convert(instanceParam, loggerType);\n                ParameterExpression levelParam = Expression.Parameter(typeof(object));\n                UnaryExpression levelCast = Expression.Convert(levelParam, logEventTypeType);\n                MethodCallExpression isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast);\n                IsEnabled = Expression.Lambda<Func<object, object, bool>>(isEnabledMethodCall, new[]\n                {\n                    instanceParam,\n                    levelParam\n                }).Compile();\n\n                // Action<object, object, string> Write =\n                // (logger, level, message) => { ((SeriLog.ILoggerILogger)logger).Write(level, message, new object[]); }\n                MethodInfo writeMethodInfo = loggerType.GetRuntimeMethod(\"Write\", new[] { logEventTypeType, typeof(string), typeof(object[]) });\n                ParameterExpression messageParam = Expression.Parameter(typeof(string));\n                ConstantExpression propertyValuesParam = Expression.Constant(EmptyArray);\n                MethodCallExpression writeMethodExp = Expression.Call(instanceCast, writeMethodInfo, levelCast, messageParam, propertyValuesParam);\n                Write = Expression.Lambda<Action<object, object, string>>(writeMethodExp, new[]\n                {\n                    instanceParam,\n                    levelParam,\n                    messageParam\n                }).Compile();\n\n                // Action<object, object, string, Exception> WriteException =\n                // (logger, level, exception, message) => { ((ILogger)logger).Write(level, exception, message, new object[]); }\n                MethodInfo writeExceptionMethodInfo = loggerType.GetRuntimeMethod(\"Write\", new[]\n                {\n                    logEventTypeType,\n                    typeof(Exception), \n                    typeof(string),\n                    typeof(object[])\n                });\n                ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception));\n                writeMethodExp = Expression.Call(\n                    instanceCast,\n                    writeExceptionMethodInfo,\n                    levelCast,\n                    exceptionParam,\n                    messageParam,\n                    propertyValuesParam);\n                WriteException = Expression.Lambda<Action<object, object, Exception, string>>(writeMethodExp, new[]\n                {\n                    instanceParam,\n                    levelParam,\n                    exceptionParam,\n                    messageParam,\n                }).Compile();\n            }\n        }\n\n        internal sealed class SerilogLogger : ILog\n        {\n            private readonly SerilogCallbacks _callbacks;\n            private readonly object _logger;\n\n            internal SerilogLogger(SerilogCallbacks callbacks, object logger)\n            {\n                _callbacks = callbacks;\n                _logger = logger;\n            }\n\n            public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                if (messageFunc == null)\n                {\n                    return _callbacks.IsEnabled(_logger, logLevel);\n                }\n                if (exception != null)\n                {\n                    return LogException(logLevel, messageFunc, exception);\n                }\n\n                switch (logLevel)\n                {\n                    case LogLevel.Debug:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.DebugLevel))\n                        {\n                            _callbacks.Write(_logger, _callbacks.DebugLevel, messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Info:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.InformationLevel))\n                        {\n                            _callbacks.Write(_logger, _callbacks.InformationLevel, messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Warn:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.WarningLevel))\n                        {\n                            _callbacks.Write(_logger, _callbacks.WarningLevel, messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Error:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.ErrorLevel))\n                        {\n                            _callbacks.Write(_logger, _callbacks.ErrorLevel, messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Fatal:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.FatalLevel))\n                        {\n                            _callbacks.Write(_logger, _callbacks.FatalLevel, messageFunc());\n                            return true;\n                        }\n                        break;\n                    default:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.VerboseLevel))\n                        {\n                            _callbacks.Write(_logger, _callbacks.VerboseLevel, messageFunc());\n                            return true;\n                        }\n                        break;\n                }\n                return false;\n            }\n\n            private bool LogException(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                switch (logLevel)\n                {\n                    case LogLevel.Debug:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.DebugLevel))\n                        {\n                            _callbacks.WriteException(_logger, _callbacks.DebugLevel, exception, messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Info:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.InformationLevel))\n                        {\n                            _callbacks.WriteException(_logger, _callbacks.InformationLevel, exception, messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Warn:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.WarningLevel))\n                        {\n                            _callbacks.WriteException(_logger, _callbacks.WarningLevel, exception, messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Error:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.ErrorLevel))\n                        {\n                            _callbacks.WriteException(_logger, _callbacks.ErrorLevel, exception, messageFunc());\n                            return true;\n                        }\n                        break;\n                    case LogLevel.Fatal:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.FatalLevel))\n                        {\n                            _callbacks.WriteException(_logger, _callbacks.FatalLevel, exception, messageFunc());\n                            return true;\n                        }\n                        break;\n                    default:\n                        if (_callbacks.IsEnabled(_logger, _callbacks.VerboseLevel))\n                        {\n                            _callbacks.WriteException(_logger, _callbacks.VerboseLevel, exception, messageFunc());\n                            return true;\n                        }\n                        break;\n                }\n                return false;\n            }\n        }\n    }\n\n#if !NETSTANDARD1_3\n    public class LoupeLogProvider : ILogProvider\n    {\n        private static bool _providerIsAvailableOverride = true;\n        private readonly WriteDelegate _logWriteDelegate;\n\n        public LoupeLogProvider()\n        {\n            if (!IsLoggerAvailable())\n            {\n                throw new InvalidOperationException(\"Gibraltar.Agent.Log (Loupe) not found\");\n            }\n\n            _logWriteDelegate = GetLogWriteDelegate();\n        }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether [provider is available override]. Used in tests.\n        /// </summary>\n        /// <value>\n        /// <c>true</c> if [provider is available override]; otherwise, <c>false</c>.\n        /// </value>\n        public static bool ProviderIsAvailableOverride\n        {\n            get { return _providerIsAvailableOverride; }\n            set { _providerIsAvailableOverride = value; }\n        }\n\n        public ILog GetLogger(string name)\n        {\n            return new LoupeLogger(name, _logWriteDelegate);\n        }\n\n        public static bool IsLoggerAvailable()\n        {\n            return ProviderIsAvailableOverride && GetLogManagerType() != null;\n        }\n\n        private static Type GetLogManagerType()\n        {\n            return Type.GetType(\"Gibraltar.Agent.Log, Gibraltar.Agent\");\n        }\n\n        private static WriteDelegate GetLogWriteDelegate()\n        {\n            Type logManagerType = GetLogManagerType();\n            Type logMessageSeverityType = Type.GetType(\"Gibraltar.Agent.LogMessageSeverity, Gibraltar.Agent\");\n            Type logWriteModeType = Type.GetType(\"Gibraltar.Agent.LogWriteMode, Gibraltar.Agent\");\n\n            MethodInfo method = logManagerType.GetMethod(\"Write\", new[]\n                                                                  {\n                                                                      logMessageSeverityType, typeof(string), typeof(int), typeof(Exception), typeof(bool), \n                                                                      logWriteModeType, typeof(string), typeof(string), typeof(string), typeof(string), typeof(object[])\n                                                                  });\n\n            return (WriteDelegate) method.CreateDelegate(typeof (WriteDelegate));\n        }\n\n        internal sealed class LoupeLogger : ILog\n        {\n            private const string LogSystem = \"LibLog\";\n\n            private readonly string _category;\n            private readonly WriteDelegate _logWriteDelegate;\n            private readonly int _skipLevel;\n\n            internal LoupeLogger(string category, WriteDelegate logWriteDelegate)\n            {\n                _category = category;\n                _logWriteDelegate = logWriteDelegate;\n                _skipLevel = 1;\n            }\n\n            public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                if (messageFunc == null)\n                {\n                    //nothing to log..\n                    return true;\n                }\n\n                _logWriteDelegate((int)LoupeLogger.ToLogMessageSeverity(logLevel), LogSystem, _skipLevel, exception, true, 0, null,\n                    _category, null, messageFunc.Invoke());\n\n                return true;\n            }\n\n            private static TraceEventType ToLogMessageSeverity(LogLevel logLevel)\n            {\n                switch (logLevel)\n                {\n                    case LogLevel.Trace:\n                        return TraceEventType.Verbose;\n                    case LogLevel.Debug:\n                        return TraceEventType.Verbose;\n                    case LogLevel.Info:\n                        return TraceEventType.Information;\n                    case LogLevel.Warn:\n                        return TraceEventType.Warning;\n                    case LogLevel.Error:\n                        return TraceEventType.Error;\n                    case LogLevel.Fatal:\n                        return TraceEventType.Critical;\n                    default:\n                        throw new ArgumentOutOfRangeException(nameof(logLevel));\n                }\n            }\n        }\n\n        /// <summary>\n        /// The form of the Loupe Log.Write method we're using\n        /// </summary>\n        internal delegate void WriteDelegate(\n            int severity,\n            string logSystem,\n            int skipFrames,\n            Exception exception,\n            bool attributeToException,\n            int writeMode,\n            string detailsXml,\n            string category,\n            string caption,\n            string description,\n            params object[] args\n            );\n    }\n#endif\n\n    public class ColouredConsoleLogProvider : ILogProvider\n    {\n        private readonly LogLevel _minLevel;\n\n        static ColouredConsoleLogProvider()\n        {\n            MessageFormatter = DefaultMessageFormatter;\n            Colors = new Dictionary<LogLevel, ConsoleColor> {\n                        { LogLevel.Fatal, ConsoleColor.Red },\n                        { LogLevel.Error, ConsoleColor.Yellow },\n                        { LogLevel.Warn, ConsoleColor.Magenta },\n                        { LogLevel.Info, ConsoleColor.White },\n                        { LogLevel.Debug, ConsoleColor.Gray },\n                        { LogLevel.Trace, ConsoleColor.DarkGray },\n                    };\n        }\n\n        public ColouredConsoleLogProvider()\n            : this(LogLevel.Info)\n        {\n        }\n\n        public ColouredConsoleLogProvider(LogLevel minLevel)\n        {\n            _minLevel = minLevel;\n        }\n\n        public ILog GetLogger(string name)\n        {\n            return new ColouredConsoleLogger(name, _minLevel);\n        }\n\n        /// <summary>\n        /// A delegate returning a formatted log message\n        /// </summary>\n        /// <param name=\"loggerName\">The name of the Logger</param>\n        /// <param name=\"level\">The Log Level</param>\n        /// <param name=\"message\">The Log Message</param>\n        /// <param name=\"e\">The Exception, if there is one</param>\n        /// <returns>A formatted Log Message string.</returns>\n        [SuppressMessage(\"Naming\", \"CA1711:Identifiers should not have incorrect suffix\", Justification = \"Public API, can not change in minor versions.\")]\n        public delegate string MessageFormatterDelegate(\n            string loggerName,\n            LogLevel level,\n            object message,\n            Exception e);\n\n        public static Dictionary<LogLevel, ConsoleColor> Colors { get; set; }\n\n        public static MessageFormatterDelegate MessageFormatter { get; set; }\n\n        protected static string DefaultMessageFormatter(string loggerName, LogLevel level, object message, Exception e)\n        {\n            var stringBuilder = new StringBuilder();\n\n            stringBuilder.Append(DateTime.Now.ToString(\"yyyy-MM-dd hh:mm:ss\", CultureInfo.InvariantCulture));\n\n            stringBuilder.Append(' ');\n\n            // Append a readable representation of the log level\n#pragma warning disable CA1311\n            stringBuilder.Append((\"[\" + level.ToString().ToUpper(\n#if !NETSTANDARD1_3\n                CultureInfo.InvariantCulture\n#endif\n                ) + \"]\").PadRight(8));\n#pragma warning restore CA1311\n\n            stringBuilder.Append(\"(\" + loggerName + \") \");\n\n            // Append the message\n            stringBuilder.Append(message);\n\n            // Append stack trace if there is an exception\n            if (e != null)\n            {\n                stringBuilder.Append(Environment.NewLine).Append(e);\n            }\n\n            return stringBuilder.ToString();\n        }\n\n        internal sealed class ColouredConsoleLogger : ILog\n        {\n            private static readonly object Lock = new object();\n            private readonly string _name;\n            private readonly LogLevel _minLevel;\n\n            public ColouredConsoleLogger(string name, LogLevel minLevel)\n            {\n                _name = name;\n                _minLevel = minLevel;\n            }\n\n            public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                if (logLevel < _minLevel)\n                {\n                    return false;\n                }\n\n                if (messageFunc == null)\n                {\n                    return true;\n                }\n\n                Write(logLevel, messageFunc(), exception);\n                return true;\n            }\n\n            private void Write(LogLevel logLevel, string message, Exception e = null)\n            {\n                var formattedMessage = MessageFormatter(_name, logLevel, message, e);\n                ConsoleColor color;\n\n                if (Colors.TryGetValue(logLevel, out color))\n                {\n                    lock (Lock)\n                    {\n                        var originalColor = Console.ForegroundColor;\n                        try\n                        {\n                            Console.ForegroundColor = color;\n                            Console.Out.WriteLine(formattedMessage);\n                        }\n                        finally\n                        {\n                            Console.ForegroundColor = originalColor;\n                        }\n                    }\n                }\n                else\n                {\n                    Console.Out.WriteLine(formattedMessage);\n                }\n            }\n        }\n    }\n\n#if !NETSTANDARD1_3\n    public class ElmahLogProvider : ILogProvider\n    {\n        private static bool _providerIsAvailableOverride = true;\n\n        private const LogLevel DefaultMinLevel = LogLevel.Error;\n        private readonly Type _errorType;\n\n        private readonly LogLevel _minLevel;\n        private readonly Func<object> _getErrorLogDelegate;\n\n        public ElmahLogProvider()\n            : this(DefaultMinLevel)\n        {\n        }\n\n        public ElmahLogProvider(LogLevel minLevel)\n        {\n            if (!IsLoggerAvailable())\n            {\n                throw new InvalidOperationException(\"`Elmah.ErrorLog` or `Elmah.Error` type not found\");\n            }\n\n            _minLevel = minLevel;\n\n            _errorType = GetErrorType();\n            _getErrorLogDelegate = GetGetErrorLogMethodCall();\n        }\n\n        public static bool ProviderIsAvailableOverride\n        {\n            get { return _providerIsAvailableOverride; }\n            set { _providerIsAvailableOverride = value; }\n        }\n\n        public ILog GetLogger(string name)\n        {\n            return new ElmahLog(_minLevel, _getErrorLogDelegate(), _errorType);\n        }\n\n        public static bool IsLoggerAvailable()\n        {\n            return ProviderIsAvailableOverride && GetLogManagerType() != null && GetErrorType() != null;\n        }\n\n        private static Type GetLogManagerType()\n        {\n            return Type.GetType(\"Elmah.ErrorLog, Elmah\");\n        }\n\n        private static Type GetHttpContextType()\n        {\n            return Type.GetType(\n                $\"System.Web.HttpContext, System.Web, Version={Environment.Version}, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\");\n        }\n\n        private static Type GetErrorType()\n        {\n            return Type.GetType(\"Elmah.Error, Elmah\");\n        }\n\n        private static Func<object> GetGetErrorLogMethodCall()\n        {\n            Type logManagerType = GetLogManagerType();\n            Type httpContextType = GetHttpContextType();\n            MethodInfo method = logManagerType.GetMethod(\"GetDefault\", new[] { httpContextType });\n            ConstantExpression contextValue = Expression.Constant(null, httpContextType);\n            MethodCallExpression methodCall = Expression.Call(null, method, new Expression[] { contextValue });\n            return Expression.Lambda<Func<object>>(methodCall).Compile();\n        }\n\n        internal sealed class ElmahLog : ILog\n        {\n            private readonly LogLevel _minLevel;\n            private readonly Type _errorType;\n            private readonly dynamic _errorLog;\n\n            public ElmahLog(LogLevel minLevel, dynamic errorLog, Type errorType)\n            {\n                _minLevel = minLevel;\n                _errorType = errorType;\n                _errorLog = errorLog;\n            }\n\n            public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception)\n            {\n                if (messageFunc == null) return logLevel >= _minLevel;\n\n                var message = messageFunc();\n\n                dynamic error = exception == null\n                    ? Activator.CreateInstance(_errorType)\n                    : Activator.CreateInstance(_errorType, exception);\n\n                error.Message = message;\n                error.Type = logLevel.ToString();\n                error.Time = DateTime.Now;\n                error.ApplicationName = \"Hangfire\";\n\n                try\n                {\n                    _errorLog.Log(error);\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    Debug.Print(\"Error: {0}\\n{1}\", ex.Message, ex.StackTrace);\n                }\n\n                return true;\n            }\n        }\n    }\n#endif\n}\n"
  },
  {
    "path": "src/Hangfire.Core/App_Packages/StackTraceFormatter/StackTraceFormatter.cs",
    "content": "#region Copyright (c) 2011 Atif Aziz. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#endregion\n\n// ReSharper disable All\n\nnamespace Hangfire\n{\n    using System.Collections.Generic;\n    using System.Diagnostics;\n    using System.Linq;\n    using System.Net;\n    using MoreLinq;\n\n    partial class StackTraceHtmlFragments : IStackTraceFormatter<string>\n    {\n        public string BeforeType          { get; set; }\n        public string AfterType           { get; set; }\n        public string BeforeMethod        { get; set; }\n        public string AfterMethod         { get; set; }\n        public string BeforeParameterType { get; set; }\n        public string AfterParameterType  { get; set; }\n        public string BeforeParameterName { get; set; }\n        public string AfterParameterName  { get; set; }\n        public string BeforeFile          { get; set; }\n        public string AfterFile           { get; set; }\n        public string BeforeLine          { get; set; }\n        public string AfterLine           { get; set; }\n        public string BeforeFrame         { get; set; }\n        public string AfterFrame          { get; set; }\n        public string BeforeParameters    { get; set; }\n        public string AfterParameters     { get; set; }\n\n        string IStackTraceFormatter<string>.Text(string text)            => string.IsNullOrEmpty(text) ? string.Empty : WebUtility.HtmlEncode(text);\n        string IStackTraceFormatter<string>.Type(string markup)          => BeforeType          + markup + AfterType;\n        string IStackTraceFormatter<string>.Method(string markup)        => BeforeMethod        + markup + AfterMethod;\n        string IStackTraceFormatter<string>.ParameterType(string markup) => BeforeParameterType + markup + AfterParameterType;\n        string IStackTraceFormatter<string>.ParameterName(string markup) => BeforeParameterName + markup + AfterParameterName;\n        string IStackTraceFormatter<string>.File(string markup)          => BeforeFile          + markup + AfterFile;\n        string IStackTraceFormatter<string>.Line(string markup)          => BeforeLine          + markup + AfterLine;\n        string IStackTraceFormatter<string>.BeforeFrame                  => BeforeFrame      ?? string.Empty;\n        string IStackTraceFormatter<string>.AfterFrame                   => AfterFrame       ?? string.Empty;\n        string IStackTraceFormatter<string>.BeforeParameters             => BeforeParameters ?? string.Empty;\n        string IStackTraceFormatter<string>.AfterParameters              => AfterParameters  ?? string.Empty;\n    }\n\n    partial interface IStackTraceFormatter<T>\n    {\n        T Text             (string text);\n        T Type             (T markup);\n        T Method           (T markup);\n        T ParameterType    (T markup);\n        T ParameterName    (T markup);\n        T File             (T markup);\n        T Line             (T markup);\n        T BeforeFrame      { get; }\n        T AfterFrame       { get; }\n        T BeforeParameters { get; }\n        T AfterParameters  { get; }\n    }\n\n    static partial class StackTraceFormatter\n    {\n        static readonly StackTraceHtmlFragments DefaultStackTraceHtmlFragments = new StackTraceHtmlFragments();\n\n        public static string FormatHtml(string text, IStackTraceFormatter<string> formatter)\n        {\n            return string.Concat(Format(text, formatter ?? DefaultStackTraceHtmlFragments));\n        }\n\n        public static IEnumerable<T> Format<T>(string text, IStackTraceFormatter<T> formatter)\n        {\n            Debug.Assert(text != null);\n\n            var frames = StackTraceParser.Parse\n                (\n                    text,\n                    (idx, len, txt) => new\n                    {\n                        Index   = idx,\n                        End     = idx + len,\n                        Text    = txt,\n                        Markup  = formatter.Text(txt),\n                    },\n                    (t, m) => new\n                    {\n                        Type   = new { t.Index, t.End, Markup = formatter.Type(t.Markup)   },\n                        Method = new { m.Index, m.End, Markup = formatter.Method(m.Markup) }\n                    },\n                    (t, n) => new\n                    {\n                        Type = new { t.Index, t.End, Markup = formatter.ParameterType(t.Markup) },\n                        Name = new { n.Index, n.End, Markup = formatter.ParameterName(n.Markup) }\n                    },\n                    (p, ps) => new { List = p, Parameters = ps.ToArray() },\n                    (f, l) => new\n                    {\n                        File = f.Text.Length > 0\n                             ? new { f.Index, f.End, Markup = formatter.File(f.Markup) }\n                             : null,\n                        Line = l.Text.Length > 0\n                             ? new { l.Index, l.End, Markup = formatter.Line(l.Markup) }\n                             : null,\n                    },\n                    (f, tm, p, fl) =>\n                        from tokens in new[]\n                        {\n                            new[]\n                            {\n                                new { f.Index, End = f.Index, Markup = formatter.BeforeFrame },\n                                tm.Type,\n                                tm.Method,\n                                new { p.List.Index, End = p.List.Index, Markup = formatter.BeforeParameters },\n                            },\n                            from pe in p.Parameters\n                            from e in new[] { pe.Type, pe.Name }\n                            select e,\n                            new[]\n                            {\n                                new { Index = p.List.End, p.List.End, Markup = formatter.AfterParameters },\n                                fl.File,\n                                fl.Line,\n                                new { Index = f.End, f.End, Markup = formatter.AfterFrame },\n                            },\n                        }\n                        from token in tokens\n                        where token != null\n                        select token\n                );\n\n            return\n                from token in Enumerable.Repeat(new { Index = 0, End = 0, Markup = default(T) }, 1)\n                                        .Concat(from tokens in frames from token in tokens select token)\n                                        .Pairwise((prev, curr) => new { Previous = prev, Current = curr })\n                from m in new[]\n                {\n                    formatter.Text(text.Substring(token.Previous.End, token.Current.Index - token.Previous.End)),\n                    token.Current.Markup,\n                }\n                select m;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/App_Packages/StackTraceParser/StackTraceParser.cs",
    "content": "#region Copyright (c) 2011 Atif Aziz. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#endregion\n\n// ReSharper disable once CheckNamespace\n\nnamespace Hangfire\n{\n    #region Imports\n\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text.RegularExpressions;\n\n    #endregion\n\n    // ReSharper disable once PartialTypeWithSinglePart\n\n    partial class StackTraceParser\n    {\n        const string Space = @\"[\\x20\\t]\";\n        const string NotSpace = @\"[^\\x20\\t]\";\n\n        static readonly Regex Regex = new Regex(@\"\n            ^\n            \" + Space + @\"*\n            \\w+ \" + Space + @\"+\n            (?<frame>\n                (?<type> \" + NotSpace + @\"+ ) \\.\n                (?<method> \" + NotSpace + @\"+? ) \" + Space + @\"*\n                (?<params>  \\( ( \" + Space + @\"* \\)\n                               |                    (?<pt> .+?) \" + Space + @\"+ (?<pn> .+?)\n                                 (, \" + Space + @\"* (?<pt> .+?) \" + Space + @\"+ (?<pn> .+?) )* \\) ) )\n                ( \" + Space + @\"+\n                    ( # Microsoft .NET stack traces\n                    \\w+ \" + Space + @\"+\n                    (?<file> ( [a-z] \\: # Windows rooted path starting with a drive letter\n                             | / )      # *nix rooted path starting with a forward-slash\n                             .+? )\n                    \\: \\w+ \" + Space + @\"+\n                    (?<line> [0-9]+ ) \\p{P}?\n                    | # Mono stack traces\n                    \\[0x[0-9a-f]+\\] \" + Space + @\"+ \\w+ \" + Space + @\"+\n                    <(?<file> [^>]+ )>\n                    :(?<line> [0-9]+ )\n                    )\n                )?\n            )\n            \\s*\n            $\",\n            RegexOptions.IgnoreCase\n            | RegexOptions.Multiline\n            | RegexOptions.ExplicitCapture\n            | RegexOptions.CultureInvariant\n            | RegexOptions.IgnorePatternWhitespace\n            | RegexOptions.Compiled,\n            // Cap the evaluation time to make it obvious should the expression\n            // fall into the \"catastrophic backtracking\" trap due to over\n            // generalization.\n            // https://github.com/atifaziz/StackTraceParser/issues/4\n            TimeSpan.FromMilliseconds(100));\n\n        public static IEnumerable<T> Parse<T>(\n            string text,\n            Func<string, string, string, string, IEnumerable<KeyValuePair<string, string>>, string, string, T> selector)\n        {\n            if (selector == null) throw new ArgumentNullException(nameof(selector));\n\n            return Parse(text, (idx, len, txt) => txt,\n                               (t, m) => new { Type = t, Method = m },\n                               (pt, pn) => new KeyValuePair<string, string>(pt, pn),\n                               // ReSharper disable once PossibleMultipleEnumeration\n                               (pl, ps) => new { List = pl, Items = ps },\n                               (fn, ln) => new { File = fn, Line = ln },\n                               (f, tm, p, fl) => selector(f, tm.Type, tm.Method, p.List, p.Items, fl.File, fl.Line));\n        }\n\n        public static IEnumerable<TFrame> Parse<TToken, TMethod, TParameters, TParameter, TSourceLocation, TFrame>(\n            string text,\n            Func<int, int, string, TToken> tokenSelector,\n            Func<TToken, TToken, TMethod> methodSelector,\n            Func<TToken, TToken, TParameter> parameterSelector,\n            Func<TToken, IEnumerable<TParameter>, TParameters> parametersSelector,\n            Func<TToken, TToken, TSourceLocation> sourceLocationSelector,\n            Func<TToken, TMethod, TParameters, TSourceLocation, TFrame> selector)\n        {\n            if (tokenSelector == null) throw new ArgumentNullException(nameof(tokenSelector));\n            if (methodSelector == null) throw new ArgumentNullException(nameof(methodSelector));\n            if (parameterSelector == null) throw new ArgumentNullException(nameof(parameterSelector));\n            if (parametersSelector == null) throw new ArgumentNullException(nameof(parametersSelector));\n            if (sourceLocationSelector == null) throw new ArgumentNullException(nameof(sourceLocationSelector));\n            if (selector == null) throw new ArgumentNullException(nameof(selector));\n\n            return from Match m in Regex.Matches(text)\n                   select m.Groups into groups\n                   let pt = groups[\"pt\"].Captures\n                   let pn = groups[\"pn\"].Captures\n                   select selector(Token(groups[\"frame\"], tokenSelector),\n                                   methodSelector(\n                                       Token(groups[\"type\"], tokenSelector),\n                                       Token(groups[\"method\"], tokenSelector)),\n                                   parametersSelector(\n                                       Token(groups[\"params\"], tokenSelector),\n                                       from i in Enumerable.Range(0, pt.Count)\n                                       select parameterSelector(Token(pt[i], tokenSelector),\n                                                                Token(pn[i], tokenSelector))),\n                                   sourceLocationSelector(Token(groups[\"file\"], tokenSelector),\n                                                          Token(groups[\"line\"], tokenSelector)));\n        }\n\n        static T Token<T>(Capture capture, Func<int, int, string, T> tokenSelector)\n        {\n            return tokenSelector(capture.Index, capture.Length, capture.Value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/AttemptsExceededAction.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Specifies a candidate state for a background job that will be chosen\n    /// by the <see cref=\"AutomaticRetryAttribute\"/> filter after exceeding\n    /// the number of retry attempts.\n    /// </summary>\n    public enum AttemptsExceededAction\n    {\n        /// <summary>\n        /// Background job will be moved to the <see cref=\"States.FailedState\"/>.\n        /// </summary>\n        Fail = 0,\n\n        /// <summary>\n        /// Background job will be moved to the <see cref=\"States.DeletedState\"/>.\n        /// </summary>\n        Delete\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/AutomaticRetryAttribute.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Reflection;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Newtonsoft.Json;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Represents a job filter that performs <i>automatic retries</i> for \n    /// background jobs whose processing was failed due to an exception, with \n    /// a limited number of attempts.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// <para>Filter is added to the global <see cref=\"GlobalJobFilters.Filters\"/> \n    /// collection by default. Intervals between attempts are based on increasing \n    /// exponential back-off multiplier in seconds.</para>\n    /// \n    /// <para>This filter works in a <i>state election</i> phase by changing the\n    /// candidate state from <see cref=\"FailedState\"/> to the <see cref=\"ScheduledState\"/>\n    /// when another retry should be attempted, or other state based on the value\n    /// of the <see cref=\"OnAttemptsExceeded\"/> property when attempts exceeded.\n    /// </para>\n    /// </remarks>\n    /// \n    /// <example>\n    /// <h3>Disabling Automatic Retries</h3>\n    /// <para>The following example shows how to disable automatic retries for\n    /// a specific job method by applying an attribute to a method.</para>\n    /// \n    /// <note>Even if you disable <see cref=\"AutomaticRetryAttribute\"/> filter, \n    /// your background jobs can still be executed several times, due to re-queue \n    /// on shutdown and other compensation logic that guarantees the <i>at least\n    /// once</i> processing.</note>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\AutomaticRetry.cs\" region=\"Disable Retries\" />\n    /// \n    /// <h3>Overriding Defaults</h3>\n    /// <para>The following example shows how to override the default number of\n    /// retry attempts for all of the background jobs by modifying the global\n    /// <see cref=\"GlobalJobFilters.Filters\"/> collection.</para>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\AutomaticRetry.cs\" region=\"Override Default\" />\n    /// \n    /// <h3>Specifying Attempts Exceeded Action</h3>\n    /// <para>The following example shows how to ignore a background job when\n    /// number of retry attempts exceed using the <see cref=\"OnAttemptsExceeded\"/>\n    /// property.</para>\n    /// \n    /// <note type=\"tip\">Choose <see cref=\"AttemptsExceededAction.Delete\"/> action \n    /// when you aren't interested in processing background job that failed several\n    /// times.</note>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\AutomaticRetry.cs\" region=\"Attempts Exceeded\" />\n    /// </example>\n    /// \n    /// <threadsafety static=\"true\" instance=\"true\" />\n    public sealed class AutomaticRetryAttribute : JobFilterAttribute, IElectStateFilter, IApplyStateFilter\n    {\n        /// <summary>\n        /// Represents the default number of retry attempts. This field is read-only.\n        /// </summary>\n        /// <remarks>\n        /// The value of this field is <c>10</c>.\n        /// </remarks>\n        public static readonly int DefaultRetryAttempts = 10;\n\n        private static readonly Func<long, int> DefaultDelayInSecondsByAttemptFunc = attempt =>\n        {\n            var random = new Random();\n            return (int)Math.Round(\n                Math.Pow(attempt - 1, 4) + 15 + random.Next(30) * attempt);\n        };\n        \n        private readonly ILog _logger = LogProvider.For<AutomaticRetryAttribute>();\n        \n        private readonly object _lockObject = new object();\n        private int _attempts;\n        private int[]  _delaysInSeconds;\n        private Func<long, int> _delayInSecondsByAttemptFunc;\n        private AttemptsExceededAction _onAttemptsExceeded;\n        private bool _logEvents;\n        private Type[] _onlyOn;\n        private Type[] _exceptOn;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"AutomaticRetryAttribute\"/>\n        /// class with <see cref=\"DefaultRetryAttempts\"/> number.\n        /// </summary>\n        public AutomaticRetryAttribute()\n        {\n            Attempts = DefaultRetryAttempts;\n            DelayInSecondsByAttemptFunc = DefaultDelayInSecondsByAttemptFunc;\n            LogEvents = true;\n            OnAttemptsExceeded = AttemptsExceededAction.Fail;\n            Order = 20;\n        }\n\n        /// <summary>\n        /// Gets or sets the maximum number of automatic retry attempts.\n        /// </summary>\n        /// <value>Any non-negative number.</value>\n        /// <exception cref=\"ArgumentOutOfRangeException\">The value in a set operation is less than zero.</exception>\n        public int Attempts\n        {\n            get { lock (_lockObject) { return _attempts; } }\n            set\n            {\n                if (value < 0)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), @\"Attempts value must be equal or greater than zero.\");\n                }\n\n                lock (_lockObject)\n                {\n                    _attempts = value;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the delays between attempts.\n        /// </summary>\n        /// <value>An array of non-negative numbers.</value>\n        /// <exception cref=\"ArgumentNullException\">The value in a set operation is null.</exception>\n        /// <exception cref=\"ArgumentException\">The value contain one or more negative numbers.</exception>\n        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]\n        public int[] DelaysInSeconds\n        {\n            get { lock (_lockObject) { return _delaysInSeconds; } }\n            set\n            {\n                if (value != null)\n                {\n                    if (value.Length == 0) throw new ArgumentNullException(nameof(value));\n                    if (value.Any(static delay => delay < 0))\n                        throw new ArgumentException(\n                            $@\"{nameof(DelaysInSeconds)} value must be an array of non-negative numbers.\",\n                            nameof(value));\n                }\n\n                lock (_lockObject) { _delaysInSeconds = value; }\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets a function using to get a delay by an attempt number.\n        /// </summary>\n        /// <exception cref=\"ArgumentNullException\">The value in a set operation is null.</exception>\n        [JsonIgnore]\n        public Func<long, int> DelayInSecondsByAttemptFunc\n        {\n            get { lock (_lockObject) { return _delayInSecondsByAttemptFunc;} }\n            set\n            {\n                if (value == null) throw new ArgumentNullException(nameof(value));\n                lock (_lockObject) { _delayInSecondsByAttemptFunc = value; }\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets a candidate state for a background job that \n        /// will be chosen when number of retry attempts exceeded.\n        /// </summary>\n        [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]\n        public AttemptsExceededAction OnAttemptsExceeded\n        {\n            get { lock (_lockObject) { return _onAttemptsExceeded; } }\n            set { lock (_lockObject) { _onAttemptsExceeded = value; } }\n        }\n\n        /// <summary>\n        /// Gets or sets whether to produce log messages on retry attempts.\n        /// </summary>\n        [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]\n        [DefaultValue(true)]\n        public bool LogEvents\n        {\n            get { lock (_lockObject) { return _logEvents; } }\n            set { lock (_lockObject) { _logEvents = value; } }\n        }\n\n        /// <summary>\n        /// Gets a sets an array of exception types that will be used to determine whether\n        /// automatic retry logic should be attempted to run. By default it will be run on\n        /// any exception, but this property allow to reduce it only to some specific\n        /// exception types and their subtypes.\n        /// </summary>\n        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]\n        public Type[] OnlyOn\n        {\n            get { lock (_lockObject) { return _onlyOn; } }\n            set { lock (_lockObject) { _onlyOn = value; } }\n        }\n\n        /// <summary>\n        /// Gets or sets the array of exception types on which the automatic retry mechanism\n        /// should not be applied.\n        /// </summary>\n        /// <value>\n        /// An array of <see cref=\"System.Type\"/> objects representing the exception types to\n        /// be excluded from automatic retries.\n        /// </value>\n        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]\n        public Type[] ExceptOn\n        {\n            get { lock (_lockObject) { return _exceptOn; } }\n            set { lock (_lockObject) { _exceptOn = value; } }\n        }\n\n        /// <inheritdoc />\n        public void OnStateElection(ElectStateContext context)\n        {\n            var failedState = context.CandidateState as FailedState;\n            if (failedState == null)\n            {\n                // This filter accepts only failed job state.\n                return;\n            }\n\n            if (_onlyOn != null && _onlyOn.Length > 0)\n            {\n                var exceptionType = failedState.Exception.GetType();\n                var satisfied = false;\n\n                foreach (var onlyOn in _onlyOn)\n                {\n                    if (onlyOn.GetTypeInfo().IsAssignableFrom(exceptionType.GetTypeInfo()))\n                    {\n                        satisfied = true;\n                        break;\n                    }\n                }\n\n                if (!satisfied) return;\n            }\n\n            if (_exceptOn != null && _exceptOn.Length > 0)\n            {\n                var exceptionType = failedState.Exception.GetType();\n                var satisfied = true;\n\n                foreach (var exceptOn in _exceptOn)\n                {\n                    if (exceptOn.GetTypeInfo().IsAssignableFrom(exceptionType.GetTypeInfo()))\n                    {\n                        satisfied = false;\n                        break;\n                    }\n                }\n\n                if (!satisfied) return;\n            }\n\n            var retryAttempt = context.GetJobParameter<int>(\"RetryCount\", allowStale: true) + 1;\n\n            if (retryAttempt <= Attempts)\n            {\n                ScheduleAgainLater(context, retryAttempt, failedState);\n            }\n            else if (retryAttempt > Attempts && OnAttemptsExceeded == AttemptsExceededAction.Delete)\n            {\n                TransitionToDeleted(context, failedState);\n            }\n            else\n            {\n                if (LogEvents)\n                {\n                    _logger.ErrorException(\n                        $\"Failed to process the job '{context.BackgroundJob.Id}': an exception occurred.\",\n                        failedState.Exception);\n                }\n            }\n        }\n\n        /// <inheritdoc />\n        public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)\n        {\n            if (context.NewState is ScheduledState &&\n                context.NewState.Reason != null &&\n                context.NewState.Reason.StartsWith(\"Retry attempt\", StringComparison.OrdinalIgnoreCase))\n            {\n                transaction.AddToSet(\"retries\", context.BackgroundJob.Id);\n            }\n        }\n\n        /// <inheritdoc />\n        public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)\n        {\n            if (ScheduledState.StateName.Equals(context.OldStateName, StringComparison.OrdinalIgnoreCase) ||\n                FailedState.StateName.Equals(context.OldStateName, StringComparison.OrdinalIgnoreCase))\n            {\n                transaction.RemoveFromSet(\"retries\", context.BackgroundJob.Id);\n            }\n        }\n\n        /// <summary>\n        /// Schedules the job to run again later. See <see cref=\"DelayInSecondsByAttemptFunc\"/>.\n        /// </summary>\n        /// <param name=\"context\">The state context.</param>\n        /// <param name=\"retryAttempt\">The count of retry attempts made so far.</param>\n        /// <param name=\"failedState\">Object which contains details about the current failed state.</param>\n        private void ScheduleAgainLater(ElectStateContext context, int retryAttempt, FailedState failedState)\n        {\n            context.SetJobParameter(\"RetryCount\", retryAttempt);\n\n            int delayInSeconds;\n            \n            if (_delaysInSeconds != null)\n            {\n                delayInSeconds = retryAttempt <= _delaysInSeconds.Length\n                    ? _delaysInSeconds[retryAttempt - 1]\n                    : _delaysInSeconds.Last();\n            }\n            else\n            {\n                delayInSeconds = DelayInSecondsByAttemptFunc(retryAttempt);                \n            }\n\n            var delay = TimeSpan.FromSeconds(delayInSeconds);          \n\n            const int maxMessageLength = 50;\n            var exceptionMessage = failedState.Exception.Message.Length > maxMessageLength\n                ? failedState.Exception.Message.Substring(0, maxMessageLength - 1) + \"…\" \n                : failedState.Exception.Message;\n\n            // If attempt number is less than max attempts, we should\n            // schedule the job to run again later.\n            \n            var reason = $\"Retry attempt {retryAttempt} of {Attempts}: {exceptionMessage}\";\n\n            context.CandidateState = delay == TimeSpan.Zero\n                ? (IState)new EnqueuedState { Reason = reason }\n                : new ScheduledState(delay) { Reason = reason };\n\n            if (LogEvents)\n            {\n                _logger.WarnException(\n                    $\"Failed to process the job '{context.BackgroundJob.Id}': an exception occurred. Retry attempt {retryAttempt} of {Attempts} will be performed in {delay}.\",\n                    failedState.Exception);\n            }\n        }\n\n        /// <summary>\n        /// Transition the candidate state to the deleted state.\n        /// </summary>\n        /// <param name=\"context\">The state context.</param>\n        /// <param name=\"failedState\">Object which contains details about the current failed state.</param>\n        private void TransitionToDeleted(ElectStateContext context, FailedState failedState)\n        {\n            context.CandidateState = new DeletedState(new ExceptionInfo(failedState.Exception))\n            {\n                Reason = Attempts > 0\n                    ? \"Exceeded the maximum number of retry attempts.\"\n                    : \"Retries were disabled for this job.\"\n            };\n\n            if (LogEvents)\n            {\n                _logger.WarnException(\n                    $\"Failed to process the job '{context.BackgroundJob.Id}': an exception occured. Job was automatically deleted because the retry attempt count exceeded {Attempts}.\",\n                    failedState.Exception);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/BackgroundJob.Instance.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\n\nnamespace Hangfire\n{\n    partial class BackgroundJob\n    {\n        /// <exclude />\n        public BackgroundJob([NotNull] string id, [CanBeNull] Job job, DateTime createdAt)\n            : this(id, job, createdAt, null)\n        {\n        }\n\n        /// <exclude />\n        public BackgroundJob([NotNull] string id, [CanBeNull] Job job, DateTime createdAt, [CanBeNull] IReadOnlyDictionary<string, string> parametersSnapshot)\n        {\n            if (id == null) throw new ArgumentNullException(nameof(id));\n\n            Id = id;\n            Job = job;\n            CreatedAt = createdAt;\n            ParametersSnapshot = parametersSnapshot;\n        }\n\n        /// <exclude />\n        [NotNull]\n        public string Id { get; }\n\n        /// <exclude />\n        [CanBeNull]\n        public Job Job { get; }\n\n        /// <exclude />\n        public DateTime CreatedAt { get; }\n\n        /// <exclude />\n        [CanBeNull]\n        public IReadOnlyDictionary<string, string> ParametersSnapshot { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/BackgroundJob.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Linq.Expressions;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Provides static methods for creating <i>fire-and-forget</i>, <i>delayed</i>\n    /// jobs and <i>continuations</i> as well as re-queue and delete existing\n    /// background jobs.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// <para>This class is a wrapper for the <see cref=\"IBackgroundJobClient\"/> \n    /// interface and its default implementation, <see cref=\"BackgroundJobClient\"/>\n    /// class, that was created for the most simple scenarios. Please consider \n    /// using the types above in real world applications.</para>\n    /// <para>This class also contains undocumented constructor and instance \n    /// members. They are hidden to not to confuse new users. You can freely \n    /// use them in low-level API.</para>\n    /// </remarks>\n    /// \n    /// <seealso cref=\"IBackgroundJobClient\"/>\n    /// <seealso cref=\"BackgroundJobClient\"/>\n    /// \n    /// <threadsafety static=\"true\" instance=\"false\" />\n    public partial class BackgroundJob\n    {\n        private static readonly Func<IBackgroundJobClient> DefaultFactory = static () => new BackgroundJobClient();\n        private static readonly object ClientFactoryLock = new object();\n\n        private static Func<IBackgroundJobClient> _clientFactory;\n\n        internal static Func<IBackgroundJobClient> ClientFactory\n        {\n            get\n            {\n                lock (ClientFactoryLock)\n                {\n                    return _clientFactory ?? DefaultFactory;\n                }\n            }\n            set\n            {\n                lock (ClientFactoryLock)\n                {\n                    _clientFactory = value;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Creates a new fire-and-forget job based on a given method call expression.\n        /// </summary>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a background job.</returns>\n        /// \n        /// <exception cref=\"ArgumentNullException\">\n        /// <paramref name=\"methodCall\"/> is <see langword=\"null\"/>.\n        /// </exception>\n        /// \n        /// <seealso cref=\"EnqueuedState\"/>\n        /// <seealso cref=\"O:Hangfire.IBackgroundJobClient.Enqueue\"/>\n        public static string Enqueue([NotNull, InstantHandle] Expression<Action> methodCall)\n        {\n            return ClientFactory().Enqueue(methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new fire-and-forget job based on a given method call expression and places it\n        /// to the specified queue.\n        /// </summary>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a background job.</returns>\n        ///\n        /// <exception cref=\"ArgumentNullException\">\n        /// <paramref name=\"methodCall\"/> is <see langword=\"null\"/>.\n        /// </exception>\n        ///\n        /// <seealso cref=\"EnqueuedState\"/>\n        /// <seealso cref=\"O:Hangfire.IBackgroundJobClient.Enqueue\"/>\n        public static string Enqueue([NotNull] string queue, [NotNull, InstantHandle] Expression<Action> methodCall)\n        {\n            return ClientFactory().Enqueue(queue, methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new fire-and-forget job based on a given method call expression.\n        /// </summary>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a background job.</returns>\n        /// \n        /// <exception cref=\"ArgumentNullException\">\n        /// <paramref name=\"methodCall\"/> is <see langword=\"null\"/>.\n        /// </exception>\n        /// \n        /// <seealso cref=\"EnqueuedState\"/>\n        /// <seealso cref=\"O:Hangfire.IBackgroundJobClient.Enqueue\"/>\n        public static string Enqueue([NotNull, InstantHandle] Expression<Func<Task>> methodCall)\n        {\n            return ClientFactory().Enqueue(methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new fire-and-forget job based on a given method call expression and places it\n        /// to the specified queue.\n        /// </summary>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a background job.</returns>\n        ///\n        /// <exception cref=\"ArgumentNullException\">\n        /// <paramref name=\"methodCall\"/> is <see langword=\"null\"/>.\n        /// </exception>\n        ///\n        /// <seealso cref=\"EnqueuedState\"/>\n        /// <seealso cref=\"O:Hangfire.IBackgroundJobClient.Enqueue\"/>\n        public static string Enqueue([NotNull] string queue, [NotNull, InstantHandle] Expression<Func<Task>> methodCall)\n        {\n            return ClientFactory().Enqueue(queue, methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new fire-and-forget job based on a given method call expression.\n        /// </summary>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a background job.</returns>\n        /// \n        /// <exception cref=\"ArgumentNullException\">\n        /// <paramref name=\"methodCall\"/> is <see langword=\"null\"/>.\n        /// </exception>\n        /// \n        /// <seealso cref=\"EnqueuedState\"/>\n        /// <seealso cref=\"O:Hangfire.IBackgroundJobClient.Enqueue\"/>\n        public static string Enqueue<T>([NotNull, InstantHandle] Expression<Action<T>> methodCall)\n        {\n            return ClientFactory().Enqueue(methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new fire-and-forget job based on a given method call expression and places it\n        /// to the specified queue.\n        /// </summary>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a background job.</returns>\n        ///\n        /// <exception cref=\"ArgumentNullException\">\n        /// <paramref name=\"methodCall\"/> is <see langword=\"null\"/>.\n        /// </exception>\n        ///\n        /// <seealso cref=\"EnqueuedState\"/>\n        /// <seealso cref=\"O:Hangfire.IBackgroundJobClient.Enqueue\"/>\n        public static string Enqueue<T>([NotNull] string queue, [NotNull, InstantHandle] Expression<Action<T>> methodCall)\n        {\n            return ClientFactory().Enqueue(queue, methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new fire-and-forget job based on a given method call expression.\n        /// </summary>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a background job.</returns>\n        /// \n        /// <exception cref=\"ArgumentNullException\">\n        /// <paramref name=\"methodCall\"/> is <see langword=\"null\"/>.\n        /// </exception>\n        /// \n        /// <seealso cref=\"EnqueuedState\"/>\n        /// <seealso cref=\"O:Hangfire.IBackgroundJobClient.Enqueue\"/>\n        public static string Enqueue<T>([NotNull, InstantHandle] Expression<Func<T, Task>> methodCall)\n        {\n            return ClientFactory().Enqueue(methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new fire-and-forget job based on a given method call expression and places it\n        /// to the specified queue.\n        /// </summary>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a background job.</returns>\n        ///\n        /// <exception cref=\"ArgumentNullException\">\n        /// <paramref name=\"methodCall\"/> is <see langword=\"null\"/>.\n        /// </exception>\n        ///\n        /// <seealso cref=\"EnqueuedState\"/>\n        /// <seealso cref=\"O:Hangfire.IBackgroundJobClient.Enqueue\"/>\n        public static string Enqueue<T>([NotNull] string queue, [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall)\n        {\n            return ClientFactory().Enqueue(queue, methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method\n        /// call expression and schedules it to be enqueued after a given delay.\n        /// </summary>\n        /// \n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule(\n            [NotNull, InstantHandle] Expression<Action> methodCall, \n            TimeSpan delay)\n        {\n            return ClientFactory().Schedule(methodCall, delay);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method call expression and schedules it\n        /// to be enqueued to the specified queue after a given delay.\n        /// </summary>\n        ///\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule(\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            TimeSpan delay)\n        {\n            return ClientFactory().Schedule(queue, methodCall, delay);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method\n        /// call expression and schedules it to be enqueued after a given delay.\n        /// </summary>\n        /// \n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule(\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            TimeSpan delay)\n        {\n            return ClientFactory().Schedule(methodCall, delay);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method call expression and schedules it\n        /// to be enqueued to the specified queue after a given delay.\n        /// </summary>\n        ///\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule(\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            TimeSpan delay)\n        {\n            return ClientFactory().Schedule(queue, methodCall, delay);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method call expression\n        /// and schedules it to be enqueued at the given moment of time.\n        /// </summary>\n        /// \n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">The moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule(\n            [NotNull, InstantHandle] Expression<Action> methodCall, \n            DateTimeOffset enqueueAt)\n        {\n            return ClientFactory().Schedule(methodCall, enqueueAt);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method call expression and schedules it\n        /// to be enqueued to the specified queue at the given moment of time.\n        /// </summary>\n        ///\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">The moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule(\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            return ClientFactory().Schedule(queue, methodCall, enqueueAt);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method call expression\n        /// and schedules it to be enqueued at the given moment of time.\n        /// </summary>\n        /// \n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">The moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule(\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            return ClientFactory().Schedule(methodCall, enqueueAt);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method call expression and schedules it\n        /// to be enqueued to the specified queue at the given moment of time.\n        /// </summary>\n        ///\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">The moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule(\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            return ClientFactory().Schedule(queue, methodCall, enqueueAt);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method\n        /// call expression and schedules it to be enqueued after a given delay.\n        /// </summary>\n        /// \n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule<T>(\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            TimeSpan delay)\n        {\n            return ClientFactory().Schedule(methodCall, delay);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method call expression and schedules\n        /// it to be enqueued to the specified queue after a given delay.\n        /// </summary>\n        ///\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            TimeSpan delay)\n        {\n            return ClientFactory().Schedule(queue, methodCall, delay);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method\n        /// call expression and schedules it to be enqueued after a given delay.\n        /// </summary>\n        /// \n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule<T>(\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            TimeSpan delay)\n        {\n            return ClientFactory().Schedule(methodCall, delay);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method call expression and schedules\n        /// it to be enqueued to the specified queue after a given delay.\n        /// </summary>\n        ///\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            TimeSpan delay)\n        {\n            return ClientFactory().Schedule(queue, methodCall, delay);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method call expression\n        /// and schedules it to be enqueued at the given moment of time.\n        /// </summary>\n        /// \n        /// <typeparam name=\"T\">The type whose method will be invoked during the job processing.</typeparam>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">The moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule<T>(\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall, \n            DateTimeOffset enqueueAt)\n        {\n            return ClientFactory().Schedule(methodCall, enqueueAt);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method call expression and schedules it\n        /// to be enqueued to the specified queue at the given moment of time.\n        /// </summary>\n        ///\n        /// <typeparam name=\"T\">The type whose method will be invoked during the job processing.</typeparam>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">The moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            return ClientFactory().Schedule(queue, methodCall, enqueueAt);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method call expression\n        /// and schedules it to be enqueued at the given moment of time.\n        /// </summary>\n        /// \n        /// <typeparam name=\"T\">The type whose method will be invoked during the job processing.</typeparam>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">The moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule<T>(\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            return ClientFactory().Schedule(methodCall, enqueueAt);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified method call expression and schedules it\n        /// to be enqueued to the specified queue at the given moment of time.\n        /// </summary>\n        ///\n        /// <typeparam name=\"T\">The type whose method will be invoked during the job processing.</typeparam>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">The moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            return ClientFactory().Schedule(queue, methodCall, enqueueAt);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"DeletedState\"/>. \n        /// <seealso cref=\"BackgroundJobClientExtensions.Delete(IBackgroundJobClient, string)\"/>\n        /// </summary>\n        /// \n        /// <param name=\"jobId\">An identifier, that will be used to find a job.</param>\n        /// <returns>True on a successful state transition, false otherwise.</returns>\n        public static bool Delete([NotNull] string jobId)\n        {\n            return ClientFactory().Delete(jobId);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"DeletedState\"/>. State change is only performed\n        /// if current job state is equal to the <paramref name=\"fromState\"/> value.\n        /// <seealso cref=\"BackgroundJobClientExtensions.Delete(IBackgroundJobClient, string, string)\"/>\n        /// </summary>\n        /// \n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"fromState\">Current state assertion, or null if unneeded.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Delete([NotNull] string jobId, [CanBeNull] string fromState)\n        {\n            return ClientFactory().Delete(jobId, fromState);\n        }\n        \n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// \n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Requeue([NotNull] string jobId)\n        {\n            return ClientFactory().Requeue(jobId);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"EnqueuedState\"/>. If <paramref name=\"fromState\"/> value \n        /// is not null, state change will be performed only if the current state name \n        /// of a job equal to the given value.\n        /// </summary>\n        /// \n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"fromState\">Current state assertion, or null if unneeded.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Requeue([NotNull] string jobId, [CanBeNull] string fromState)\n        {\n            return ClientFactory().Requeue(jobId, fromState);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"ScheduledState\"/>.\n        /// </summary>\n        ///\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"delay\">Delay, after which the job will be scheduled.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Reschedule([NotNull] string jobId, TimeSpan delay)\n        {\n            return ClientFactory().Reschedule(jobId, delay);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"ScheduledState\"/>. If <paramref name=\"fromState\"/> value\n        /// is not null, state change will be performed only if the current state name\n        /// of a job equal to the given value.\n        /// </summary>\n        ///\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"delay\">Delay, after which the job will be scheduled.</param>\n        /// <param name=\"fromState\">Current state assertion, or null if unneeded.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Reschedule([NotNull] string jobId, TimeSpan delay, [CanBeNull] string fromState)\n        {\n            return ClientFactory().Reschedule(jobId, delay, fromState);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"ScheduledState\"/>.\n        /// </summary>\n        ///\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"enqueueAt\">The moment of time at which the job will be rescheduled.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Reschedule([NotNull] string jobId, DateTimeOffset enqueueAt)\n        {\n            return ClientFactory().Reschedule(jobId, enqueueAt);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"ScheduledState\"/>. If <paramref name=\"fromState\"/> value\n        /// is not null, state change will be performed only if the current state name\n        /// of a job equal to the given value.\n        /// </summary>\n        ///\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"enqueueAt\">The moment of time at which the job will be rescheduled.</param>\n        /// <param name=\"fromState\">Current state assertion, or null if unneeded.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Reschedule([NotNull] string jobId, DateTimeOffset enqueueAt, [CanBeNull] string fromState)\n        {\n            return ClientFactory().Reschedule(jobId, enqueueAt, fromState);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion\n        /// of another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith(\n            [NotNull] string parentId, \n            [NotNull, InstantHandle] Expression<Action> methodCall)\n        {\n            return ContinueJobWith(parentId, methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion \n        /// of another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action> methodCall)\n        {\n            return ClientFactory().ContinueJobWith(parentId, methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion \n        /// of another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith<T>(\n            [NotNull] string parentId, \n            [NotNull, InstantHandle] Expression<Action<T>> methodCall)\n        {\n            return ContinueJobWith(parentId, methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion \n        /// of another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall)\n        {\n            return ClientFactory().ContinueJobWith(parentId, methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith(\n            [NotNull] string parentId, \n            [NotNull, InstantHandle] Expression<Action> methodCall, \n            JobContinuationOptions options)\n        {\n            return ContinueJobWith(parentId, methodCall, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            JobContinuationOptions options)\n        {\n            return ClientFactory().ContinueJobWith(parentId, methodCall, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued to the\n        /// specified queue.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"queue\">Default queue for the continuation.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] string parentId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            return ClientFactory().ContinueJobWith(parentId, queue, methodCall, options: options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options. By default, \n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith(\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            return ContinueJobWith(parentId, methodCall, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options. By default, \n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            return ClientFactory().ContinueJobWith(parentId, methodCall, options: options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued to the\n        /// specified queue.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"queue\">Default queue for the continuation.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options. By default,\n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] string parentId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            return ClientFactory().ContinueJobWith(parentId, queue, methodCall, options: options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith<T>(\n            [NotNull] string parentId, \n            [NotNull, InstantHandle] Expression<Action<T>> methodCall, \n            JobContinuationOptions options)\n        {\n            return ContinueJobWith(parentId, methodCall, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            JobContinuationOptions options)\n        {\n            return ClientFactory().ContinueJobWith(parentId, methodCall, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued to the\n        /// specified queue.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"queue\">Default queue for the continuation.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] string parentId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            return ClientFactory().ContinueJobWith(parentId, queue, methodCall, options: options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options. By default, \n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith<T>(\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            return ContinueJobWith(parentId, methodCall, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options. By default, \n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            return ClientFactory().ContinueJobWith(parentId, methodCall, options: options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued to the\n        /// specified queue.\n        /// </summary>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"queue\">Default queue for the continuation.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options. By default,\n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] string parentId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            return ClientFactory().ContinueJobWith(parentId, queue, methodCall, options: options);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/BackgroundJobClient.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Provides methods for creating background jobs and changing their states.\n    /// Represents a default implementation of the <see cref=\"IBackgroundJobClient\"/>\n    /// interface.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// <para>This class uses the <see cref=\"IBackgroundJobFactory\"/> interface \n    /// for creating background jobs and the <see cref=\"IBackgroundJobStateChanger\"/> \n    /// interface for changing their states. Please see documentation for those \n    /// types and their implementations to learn the details.</para>\n    /// \n    /// <note type=\"warning\">\n    /// Despite the fact that instance methods of this class are thread-safe,\n    /// most implementations of the <see cref=\"IState\"/> interface are <b>neither\n    /// thread-safe, nor immutable</b>. Please create a new instance of a state \n    /// class for each operation to avoid race conditions and unexpected side \n    /// effects.\n    /// </note>\n    /// </remarks>\n    /// \n    /// <threadsafety static=\"true\" instance=\"true\" />\n    public class BackgroundJobClient : IBackgroundJobClient, IBackgroundJobClientV2\n    {\n        private readonly JobStorage _storage;\n        private readonly IBackgroundJobFactory _factory;\n        private readonly IBackgroundJobStateChanger _stateChanger;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundJobClient\"/>\n        /// class with the storage from a global configuration.\n        /// </summary>\n        /// \n        /// <remarks>\n        /// Please see the <see cref=\"GlobalConfiguration\"/> class for the\n        /// details regarding the global configuration.\n        /// </remarks>\n        public BackgroundJobClient()\n            : this(JobStorage.Current)\n        {\n        }\n        \n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundJobClient\"/>\n        /// class with the specified storage.\n        /// </summary>\n        /// \n        /// <param name=\"storage\">Job storage to use for background jobs.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"storage\"/> is null.</exception>\n        public BackgroundJobClient([NotNull] JobStorage storage)\n            : this(storage, JobFilterProviders.Providers)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundJobClient\"/> class\n        /// with the specified storage and filter provider.\n        /// </summary>\n        /// <param name=\"storage\">Job storage to use for background jobs.</param>\n        /// <param name=\"filterProvider\">Filter provider responsible to locate job filters.</param>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"storage\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"filterProvider\"/> is null.</exception>\n        public BackgroundJobClient([NotNull] JobStorage storage, [NotNull] IJobFilterProvider filterProvider)\n            : this(storage, new BackgroundJobFactory(filterProvider), new BackgroundJobStateChanger(filterProvider))\n        {\n        }\n        \n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundJobClient\"/> class\n        /// with the specified storage, background job factory and state changer.\n        /// </summary>\n        /// \n        /// <param name=\"storage\">Job storage to use for background jobs.</param>\n        /// <param name=\"factory\">Factory to create background jobs.</param>\n        /// <param name=\"stateChanger\">State changer to change states of background jobs.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"storage\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"factory\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"stateChanger\"/> is null.</exception>\n        public BackgroundJobClient(\n            [NotNull] JobStorage storage,\n            [NotNull] IBackgroundJobFactory factory,\n            [NotNull] IBackgroundJobStateChanger stateChanger)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (factory == null) throw new ArgumentNullException(nameof(factory));\n            if (stateChanger == null) throw new ArgumentNullException(nameof(stateChanger));\n            \n            _storage = storage;\n            _stateChanger = stateChanger;\n            _factory = factory;\n        }\n\n        /// <inheritdoc />\n        public JobStorage Storage => _storage;\n\n        public int RetryAttempts\n        {\n            get\n            {\n                if (_factory is BackgroundJobFactory factory)\n                {\n                    return factory.RetryAttempts;\n                }\n\n                return 0;\n            }\n            set\n            {\n                if (_factory is BackgroundJobFactory factory)\n                {\n                    factory.RetryAttempts = value;\n                }\n            }\n        }\n\n        /// <inheritdoc />\n        public string Create(Job job, IState state) => Create(job, state, null);\n\n        /// <inheritdoc />\n        public string Create(Job job, IState state, IDictionary<string, object> parameters)\n        {\n            if (job == null) throw new ArgumentNullException(nameof(job));\n            if (state == null) throw new ArgumentNullException(nameof(state));\n\n            try\n            {\n                using (var connection = _storage.GetConnection())\n                {\n                    var context = new CreateContext(_storage, connection, job, state, parameters);\n                    var backgroundJob = _factory.Create(context);\n\n                    return backgroundJob?.Id;\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new BackgroundJobClientException(\"Background job creation failed. See inner exception for details.\", ex);\n            }\n        }\n\n        /// <inheritdoc />\n        public bool ChangeState(string jobId, IState state, string expectedState)\n        {\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n            if (state == null) throw new ArgumentNullException(nameof(state));\n\n            try\n            {\n                using (var connection = _storage.GetConnection())\n                {\n                    var appliedState = _stateChanger.ChangeState(new StateChangeContext(\n                        _storage,\n                        connection,\n                        jobId,\n                        state,\n                        expectedState != null ? new[] { expectedState } : null));\n\n                    return appliedState != null && appliedState.Name.Equals(state.Name, StringComparison.OrdinalIgnoreCase);\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new BackgroundJobClientException(\"State change of a background job failed. See inner exception for details\", ex);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/BackgroundJobClientException.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Runtime.Serialization;\nusing Hangfire.Client;\n\n#pragma warning disable 618 // Obsolete member\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// The exception that is thrown when an instance of the class that \n    /// implements the <see cref=\"IBackgroundJobClient\"/> interface is unable\n    /// to perform an operation due to an error.\n    /// </summary>\n#if !NETSTANDARD1_3\n    [Serializable]\n#endif\n    public class BackgroundJobClientException : CreateJobFailedException\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundJobClientException\"/>\n        /// class with a specified error message and a reference to the inner exception\n        /// that is the cause of this exception.\n        /// </summary>\n        /// <param name=\"message\">The error message that explains the reason for the exception.</param>\n        /// <param name=\"inner\">The exception that is the cause of this exception, not null.</param>\n        public BackgroundJobClientException(string message, Exception inner) : base(message, inner)\n        {\n        }\n\n#if !NETSTANDARD1_3\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundJobClientException\"/> class\n        /// with serialized data.\n        /// </summary>\n        /// <param name=\"info\">The <see cref=\"SerializationInfo\"/> that holds the serialized object data about the exception being thrown.</param>\n        /// <param name=\"context\">The <see cref=\"StreamingContext\"/> that contains contextual information about the source or destination.</param>\n        protected BackgroundJobClientException(SerializationInfo info, StreamingContext context)\n            : base(info, context)\n        {\n        }\n#endif\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/BackgroundJobClientExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Linq.Expressions;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Provides extension methods for the <see cref=\"IBackgroundJobClient\"/>\n    /// interface to simplify the creation of fire-and-forget jobs, delayed \n    /// jobs, continuations and other background jobs in well-known states.\n    /// Also allows to re-queue and delete existing background jobs.\n    /// </summary>\n    public static class BackgroundJobClientExtensions\n    {\n        /// <summary>\n        /// Creates a background job based on a specified lambda expression \n        /// and places it into its actual queue. \n        /// Please, see the <see cref=\"QueueAttribute\"/> to learn how to \n        /// place the job on a non-default queue.\n        /// </summary>\n        /// \n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Static method call expression that will be marshalled to the Server.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Enqueue(\n            [NotNull] this IBackgroundJobClient client, \n            [NotNull, InstantHandle] Expression<Action> methodCall)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new EnqueuedState());\n        }\n\n        /// <summary>\n        /// Creates a background job based on a specified lambda expression and places it into\n        /// the specified queue.\n        /// Please, see the <see cref=\"QueueAttribute\"/> to learn how to\n        /// place the job on a non-default queue.\n        /// </summary>\n        ///\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Static method call expression that will be marshalled to the Server.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Enqueue(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new EnqueuedState());\n        }\n\n        /// <summary>\n        /// Creates a background job based on a specified lambda expression \n        /// and places it into its actual queue. \n        /// Please, see the <see cref=\"QueueAttribute\"/> to learn how to \n        /// place the job on a non-default queue.\n        /// </summary>\n        /// \n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Static method call expression that will be marshalled to the Server.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Enqueue(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new EnqueuedState());\n        }\n\n        /// <summary>\n        /// Creates a background job based on a specified lambda expression and places it into\n        /// the specified queue.\n        /// Please, see the <see cref=\"QueueAttribute\"/> to learn how to\n        /// place the job on a non-default queue.\n        /// </summary>\n        ///\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Static method call expression that will be marshalled to the Server.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Enqueue(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new EnqueuedState());\n        }\n\n        /// <summary>\n        /// Creates a background job based on a specified lambda expression \n        /// and places it into its actual queue. \n        /// Please, see the <see cref=\"QueueAttribute\"/> to learn how to \n        /// place the job on a non-default queue.\n        /// </summary>\n        /// \n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Enqueue<T>(\n            [NotNull] this IBackgroundJobClient client, \n            [NotNull, InstantHandle] Expression<Action<T>> methodCall)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new EnqueuedState());\n        }\n\n        /// <summary>\n        /// Creates a background job based on a specified lambda expression and places it into\n        /// the specified queue.\n        /// Please, see the <see cref=\"QueueAttribute\"/> to learn how to\n        /// place the job on a non-default queue.\n        /// </summary>\n        ///\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Enqueue<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new EnqueuedState());\n        }\n\n        /// <summary>\n        /// Creates a background job based on a specified lambda expression \n        /// and places it into its actual queue. \n        /// Please, see the <see cref=\"QueueAttribute\"/> to learn how to \n        /// place the job on a non-default queue.\n        /// </summary>\n        /// \n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Enqueue<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new EnqueuedState());\n        }\n\n        /// <summary>\n        /// Creates a background job based on a specified lambda expression and places it into\n        /// the specified queue.\n        /// Please, see the <see cref=\"QueueAttribute\"/> to learn how to\n        /// place the job on a non-default queue.\n        /// </summary>\n        ///\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Enqueue<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new EnqueuedState());\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression \n        /// and schedules it to be enqueued after a given delay.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule(\n            [NotNull] this IBackgroundJobClient client, \n            [NotNull, InstantHandle] Expression<Action> methodCall, \n            TimeSpan delay)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new ScheduledState(delay));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression and schedules it\n        /// to be enqueued to the specified queue after a given delay.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            TimeSpan delay)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new ScheduledState(delay));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression \n        /// and schedules it to be enqueued after a given delay.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            TimeSpan delay)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new ScheduledState(delay));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression and schedules it\n        /// to be enqueued to the specified queue after a given delay.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            TimeSpan delay)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new ScheduledState(delay));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression\n        /// and schedules it to be enqueued at the specified moment of time.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">Moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier or a created job.</returns>\n        public static string Schedule(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new ScheduledState(enqueueAt.UtcDateTime));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression and schedules it\n        /// to be enqueued to the specified queue at the specified moment of time.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">Moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier or a created job.</returns>\n        public static string Schedule(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new ScheduledState(enqueueAt.UtcDateTime));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression\n        /// and schedules it to be enqueued at the specified moment of time.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">Moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier or a created job.</returns>\n        public static string Schedule(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new ScheduledState(enqueueAt.UtcDateTime));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression and schedules it\n        /// to be enqueued to the specified queue at the specified moment of time.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">Moment of time at which the job will be enqueued.</param>\n        /// <returns>Unique identifier or a created job.</returns>\n        public static string Schedule(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new ScheduledState(enqueueAt.UtcDateTime));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method\n        /// call expression and schedules it to be enqueued after a given delay.\n        /// </summary>\n        /// \n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] this IBackgroundJobClient client, \n            [NotNull, InstantHandle] Expression<Action<T>> methodCall, \n            TimeSpan delay)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new ScheduledState(delay));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method call expression and\n        /// schedules it to be enqueued to the specified queue after a given delay.\n        /// </summary>\n        ///\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            TimeSpan delay)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new ScheduledState(delay));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method\n        /// call expression and schedules it to be enqueued after a given delay.\n        /// </summary>\n        /// \n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            TimeSpan delay)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new ScheduledState(delay));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method call expression and\n        /// schedules it to be enqueued to the specified queue after a given delay.\n        /// </summary>\n        ///\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"delay\">Delay, after which the job will be enqueued.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            TimeSpan delay)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new ScheduledState(delay));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression and schedules\n        /// it to be enqueued at the specified moment.\n        /// </summary>\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">Moment at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new ScheduledState(enqueueAt.UtcDateTime));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression and schedules\n        /// it to be enqueued to the specified queue at the specified moment.\n        /// </summary>\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">Moment at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new ScheduledState(enqueueAt.UtcDateTime));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression and schedules\n        /// it to be enqueued at the specified moment.\n        /// </summary>\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">Moment at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(methodCall, new ScheduledState(enqueueAt.UtcDateTime));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression and schedules\n        /// it to be enqueued to the specified queue at the specified moment.\n        /// </summary>\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for the background job.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"enqueueAt\">Moment at which the job will be enqueued.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string Schedule<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            DateTimeOffset enqueueAt)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.Create(queue, methodCall, new ScheduledState(enqueueAt.UtcDateTime));\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression in a given state.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Static method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"state\">Initial state of a job.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Create(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] IState state)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            return client.Create(Job.FromExpression(methodCall), state);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression in a given state with\n        /// the specified default queue.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for a job.</param>\n        /// <param name=\"methodCall\">Static method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"state\">Initial state of a job.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Create(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] IState state)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            return client.Create(Job.FromExpression(methodCall, queue), state);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression in a given state.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Static method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"state\">Initial state of a job.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Create(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] IState state)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            return client.Create(Job.FromExpression(methodCall), state);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified lambda expression in a given state with\n        /// the specified default queue.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for a job.</param>\n        /// <param name=\"methodCall\">Static method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"state\">Initial state of a job.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Create(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] IState state)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            return client.Create(Job.FromExpression(methodCall, queue), state);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method in a given state.\n        /// </summary> \n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"state\">Initial state of a job.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Create<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] IState state)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            return client.Create(Job.FromExpression(methodCall), state);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method in a given state with\n        /// the specified default queue.\n        /// </summary>\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for a job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"state\">Initial state of a job.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Create<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] IState state)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            return client.Create(Job.FromExpression(methodCall, queue), state);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method in a given state.\n        /// </summary>\n        /// \n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"state\">Initial state of a job.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Create<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] IState state)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            return client.Create(Job.FromExpression(methodCall), state);\n        }\n\n        /// <summary>\n        /// Creates a new background job based on a specified instance method in a given state with\n        /// the specified default queue.\n        /// </summary>\n        ///\n        /// <typeparam name=\"T\">Type whose method will be invoked during job processing.</typeparam>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"queue\">Default queue for a job.</param>\n        /// <param name=\"methodCall\">Instance method call expression that will be marshalled to the Server.</param>\n        /// <param name=\"state\">Initial state of a job.</param>\n        /// <returns>Unique identifier of the created job.</returns>\n        public static string Create<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] IState state)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            return client.Create(Job.FromExpression(methodCall, queue), state);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the given <paramref name=\"jobId\"/> to\n        /// the specified one. \n        /// </summary>\n        /// \n        /// <param name=\"client\">An instance of <see cref=\"IBackgroundJobClient\"/> implementation.</param>\n        /// <param name=\"jobId\">A job, whose state is being changed.</param>\n        /// <param name=\"state\">New state for a job.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool ChangeState(\n            [NotNull] this IBackgroundJobClient client, \n            [NotNull] string jobId, \n            [NotNull] IState state)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n            return client.ChangeState(jobId, state, null);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"DeletedState\"/>.\n        /// </summary>\n        /// \n        /// <remarks>\n        /// The job is not actually being deleted, this method changes only\n        /// its state.\n        /// \n        /// This operation does not provide guarantee that the job will not be \n        /// performed. If you are deleting a job that is performing right now, it \n        /// will be performed anyway, despite of this call.\n        /// \n        /// The method returns result of a state transition. It can be false\n        /// if a job was expired, its method does not exist or there was an\n        /// exception during the state change process.\n        /// </remarks>\n        /// \n        /// <param name=\"client\">An instance of <see cref=\"IBackgroundJobClient\"/> implementation.</param>\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Delete([NotNull] this IBackgroundJobClient client, [NotNull] string jobId)\n        {\n            return Delete(client, jobId, null);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"DeletedState\"/>. If <paramref name=\"fromState\"/> value \n        /// is not null, state change will be performed only if the current state name \n        /// of a job equal to the given value.\n        /// </summary>\n        /// \n        /// <remarks>\n        /// The job is not actually being deleted, this method changes only\n        /// its state.\n        /// \n        /// This operation does not provide guarantee that the job will not be \n        /// performed. If you are deleting a job that is performing right now, it \n        /// will be performed anyway, despite of this call.\n        /// \n        /// The method returns result of a state transition. It can be false\n        /// if a job was expired, its method does not exist or there was an\n        /// exception during the state change process.\n        /// </remarks>\n        /// \n        /// <param name=\"client\">An instance of <see cref=\"IBackgroundJobClient\"/> implementation.</param>\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"fromState\">Current state assertion, or null if unneeded.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Delete(\n            [NotNull] this IBackgroundJobClient client, \n            [NotNull] string jobId, \n            [CanBeNull] string fromState)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new DeletedState();\n            return client.ChangeState(jobId, state, fromState);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// \n        /// <param name=\"client\">An instance of <see cref=\"IBackgroundJobClient\"/> implementation.</param>\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Requeue([NotNull] this IBackgroundJobClient client, [NotNull] string jobId)\n        {\n            return Requeue(client, jobId, null);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"ScheduledState\"/>. If <paramref name=\"fromState\"/> value\n        /// is not null, state change will be performed only if the current state name\n        /// of a job equal to the given value.\n        /// </summary>\n        ///\n        /// <param name=\"client\">An instance of <see cref=\"IBackgroundJobClient\"/> implementation.</param>\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"delay\">Delay, after which the job will be rescheduled.</param>\n        /// <param name=\"fromState\">Current state assertion, or null if unneeded.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Reschedule(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string jobId,\n            TimeSpan delay,\n            [CanBeNull] string fromState)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new ScheduledState(delay);\n            return client.ChangeState(jobId, state, fromState);\n        }\n\n        /// <summary>\n        ///   Changes state of a job with the specified <paramref name=\"jobId\" />\n        ///   to the <see cref=\"ScheduledState\" />.\n        /// </summary>\n        /// \n        /// <param name=\"client\">An instance of <see cref=\"IBackgroundJobClient\" /> implementation.</param>\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"delay\">Delay, after which the job will be rescheduled.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Reschedule([NotNull] this IBackgroundJobClient client, [NotNull] string jobId, TimeSpan delay)\n        {\n            return Reschedule(client, jobId, delay, null);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"ScheduledState\"/>. If <paramref name=\"fromState\"/> value\n        /// is not null, state change will be performed only if the current state name\n        /// of a job equal to the given value.\n        /// </summary>\n        ///\n        /// <param name=\"client\">An instance of <see cref=\"IBackgroundJobClient\"/> implementation.</param>\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"enqueueAt\">Moment of time at which the job will be rescheduled.</param>\n        /// <param name=\"fromState\">Current state assertion, or null if unneeded.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Reschedule(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string jobId,\n            DateTimeOffset enqueueAt,\n            [CanBeNull] string fromState)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new ScheduledState(enqueueAt.UtcDateTime);\n            return client.ChangeState(jobId, state, fromState);\n        }\n\n        /// <summary>\n        ///   Changes state of a job with the specified <paramref name=\"jobId\" />\n        ///   to the <see cref=\"ScheduledState\" />.\n        /// </summary>\n        /// \n        /// <param name=\"client\">An instance of <see cref=\"IBackgroundJobClient\" /> implementation.</param>\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"enqueueAt\">Moment of time at which the job will be rescheduled.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Reschedule([NotNull] this IBackgroundJobClient client, [NotNull] string jobId, DateTimeOffset enqueueAt)\n        {\n            return Reschedule(client, jobId, enqueueAt, null);\n        }\n\n        /// <summary>\n        /// Changes state of a job with the specified <paramref name=\"jobId\"/>\n        /// to the <see cref=\"EnqueuedState\"/>. If <paramref name=\"fromState\"/> value\n        /// is not null, state change will be performed only if the current state name\n        /// of a job equal to the given value.\n        /// </summary>\n        ///\n        /// <param name=\"client\">An instance of <see cref=\"IBackgroundJobClient\"/> implementation.</param>\n        /// <param name=\"jobId\">Identifier of job, whose state is being changed.</param>\n        /// <param name=\"fromState\">Current state assertion, or null if unneeded.</param>\n        /// <returns>True, if state change succeeded, otherwise false.</returns>\n        public static bool Requeue(\n            [NotNull] this IBackgroundJobClient client, \n            [NotNull] string jobId, \n            [CanBeNull] string fromState)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new EnqueuedState();\n            return client.ChangeState(jobId, state, fromState);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion \n        /// of another background job to be triggered in the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith(\n            [NotNull] this IBackgroundJobClient client, \n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action> methodCall)\n        {\n            return ContinueJobWith(client, parentId, methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion \n        /// of another background job to be triggered in the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action> methodCall)\n        {\n            return ContinueJobWith(client, parentId, methodCall, new EnqueuedState());\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion \n        /// of another background job to be triggered in the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith<T>(\n            [NotNull] this IBackgroundJobClient client, \n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall)\n        {\n            return ContinueJobWith(client, parentId, methodCall);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion \n        /// of another background job to be triggered in the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall)\n        {\n            return ContinueJobWith(client, parentId, methodCall, new EnqueuedState());\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion \n        /// of another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered. \n        /// If null, then <see cref=\"EnqueuedState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] IState nextState)\n        {\n            return ContinueJobWith(client, parentId, methodCall, nextState);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion \n        /// of another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered. \n        /// If null, then <see cref=\"EnqueuedState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] IState nextState)\n        {\n            return ContinueJobWith(client, parentId, methodCall, nextState, JobContinuationOptions.OnlyOnSucceededState);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion\n        /// of another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered. \n        /// If null, then <see cref=\"EnqueuedState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] IState nextState)\n        {\n            return ContinueJobWith(client, parentId, methodCall, nextState);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for a successful completion\n        /// of another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered. \n        /// If null, then <see cref=\"EnqueuedState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] IState nextState)\n        {\n            return ContinueJobWith(client, parentId, methodCall, nextState, JobContinuationOptions.OnlyOnSucceededState);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered\n        /// in the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            JobContinuationOptions options)\n        {\n            return ContinueJobWith(client, parentId, methodCall, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered\n        /// in the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            JobContinuationOptions options)\n        {\n            return ContinueJobWith(client, parentId, methodCall, new EnqueuedState(), options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered\n        /// in the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param> \n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            JobContinuationOptions options)\n        {\n            return ContinueJobWith(client, parentId, methodCall, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered\n        /// in the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param> \n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            JobContinuationOptions options)\n        {\n            return ContinueJobWith(client, parentId, methodCall, new EnqueuedState(), options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [InstantHandle] Expression<Action> methodCall,\n            [NotNull] IState nextState,\n            JobContinuationOptions options)\n        {\n            return ContinueJobWith(client, parentId, methodCall, nextState, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [InstantHandle] Expression<Action> methodCall,\n            [NotNull] IState nextState,\n            JobContinuationOptions options)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new AwaitingState(parentId, nextState, options);\n            return client.Create(methodCall, state);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued\n        /// to the specified queue.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"queue\">Default queue for the continuation.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [CanBeNull] IState nextState = null,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new AwaitingState(parentId, nextState ?? new EnqueuedState(), options);\n            return client.Create(queue, methodCall, state);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered. \n        /// If null, then <see cref=\"EnqueuedState\"/> is used.</param>\n        /// <param name=\"options\">Continuation options. By default, \n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [InstantHandle] Expression<Func<Task>> methodCall,\n            [CanBeNull] IState nextState = null,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            return ContinueJobWith(client, parentId, methodCall, nextState, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered. \n        /// If null, then <see cref=\"EnqueuedState\"/> is used.</param>\n        /// <param name=\"options\">Continuation options. By default, \n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [InstantHandle] Expression<Func<Task>> methodCall,\n            [CanBeNull] IState nextState = null,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new AwaitingState(parentId, nextState ?? new EnqueuedState(), options);\n            return client.Create(methodCall, state);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued\n        /// to the specified queue.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"queue\">Default queue for the continuation.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered.\n        /// If null, then <see cref=\"EnqueuedState\"/> is used.</param>\n        /// <param name=\"options\">Continuation options. By default,\n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull] string queue,\n            [InstantHandle] Expression<Func<Task>> methodCall,\n            [CanBeNull] IState nextState = null,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new AwaitingState(parentId, nextState ?? new EnqueuedState(), options);\n            return client.Create(queue, methodCall, state);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] IState nextState,\n            JobContinuationOptions options)\n        {\n            return ContinueJobWith(client, parentId, methodCall, nextState, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] IState nextState,\n            JobContinuationOptions options)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new AwaitingState(parentId, nextState, options);\n            return client.Create(methodCall, state);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued\n        /// to the specified queue.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"queue\">Default queue for the continuation.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered.</param>\n        /// <param name=\"options\">Continuation options.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [CanBeNull] IState nextState = null,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new AwaitingState(parentId, nextState ?? new EnqueuedState(), options);\n            return client.Create(queue, methodCall, state);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered. \n        /// If null, then <see cref=\"EnqueuedState\"/> is used.</param>\n        /// <param name=\"options\">Continuation options. By default, \n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        [Obsolete(\"Deprecated for clarity, please use ContinueJobWith method with the same arguments. Will be removed in 2.0.0.\")]\n        public static string ContinueWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [CanBeNull] IState nextState = null,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            return ContinueJobWith(client, parentId, methodCall, nextState, options);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be triggered.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered. \n        /// If null, then <see cref=\"EnqueuedState\"/> is used.</param>\n        /// <param name=\"options\">Continuation options. By default, \n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [CanBeNull] IState nextState = null,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new AwaitingState(parentId, nextState ?? new EnqueuedState(), options);\n            return client.Create(methodCall, state);\n        }\n\n        /// <summary>\n        /// Creates a new background job that will wait for another background job to be enqueued\n        /// to the specified queue.\n        /// </summary>\n        /// <param name=\"client\">A job client instance.</param>\n        /// <param name=\"parentId\">Identifier of a background job to wait completion for.</param>\n        /// <param name=\"queue\">Default queue for the continuation.</param>\n        /// <param name=\"methodCall\">Method call expression that will be marshalled to a server.</param>\n        /// <param name=\"nextState\">Next state for a job, when continuation is triggered.\n        /// If null, then <see cref=\"EnqueuedState\"/> is used.</param>\n        /// <param name=\"options\">Continuation options. By default,\n        /// <see cref=\"JobContinuationOptions.OnlyOnSucceededState\"/> is used.</param>\n        /// <returns>Unique identifier of a created job.</returns>\n        public static string ContinueJobWith<T>(\n            [NotNull] this IBackgroundJobClient client,\n            [NotNull] string parentId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [CanBeNull] IState nextState = null,\n            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)\n        {\n            if (client == null) throw new ArgumentNullException(nameof(client));\n\n            var state = new AwaitingState(parentId, nextState ?? new EnqueuedState(), options);\n            return client.Create(queue, methodCall, state);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/BackgroundJobServer.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Server;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    public class BackgroundJobServer : IBackgroundProcessingServer\n    {\n        private readonly ILog _logger = LogProvider.For<BackgroundJobServer>();\n\n        private readonly BackgroundJobServerOptions _options;\n        private readonly BackgroundProcessingServer _processingServer;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundJobServer\"/> class\n        /// with default options and <see cref=\"JobStorage.Current\"/> storage.\n        /// </summary>\n        public BackgroundJobServer()\n            : this(new BackgroundJobServerOptions())\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundJobServer\"/> class\n        /// with default options and the given storage.\n        /// </summary>\n        /// <param name=\"storage\">The storage</param>\n        public BackgroundJobServer([NotNull] JobStorage storage)\n            : this(new BackgroundJobServerOptions(), storage)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundJobServer\"/> class\n        /// with the given options and <see cref=\"JobStorage.Current\"/> storage.\n        /// </summary>\n        /// <param name=\"options\">Server options</param>\n        public BackgroundJobServer([NotNull] BackgroundJobServerOptions options)\n            : this(options, JobStorage.Current)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundJobServer\"/> class\n        /// with the specified options and the given storage.\n        /// </summary>\n        /// <param name=\"options\">Server options</param>\n        /// <param name=\"storage\">The storage</param>\n        public BackgroundJobServer([NotNull] BackgroundJobServerOptions options, [NotNull] JobStorage storage)\n            : this(options, storage, Enumerable.Empty<IBackgroundProcess>())\n        {\n        }\n\n        public BackgroundJobServer(\n            [NotNull] BackgroundJobServerOptions options,\n            [NotNull] JobStorage storage,\n            [NotNull] IEnumerable<IBackgroundProcess> additionalProcesses)\n#pragma warning disable 618\n            : this(options, storage, additionalProcesses, null, null, null, null, null)\n#pragma warning restore 618\n        {\n        }\n\n        [Obsolete(\"Create your own BackgroundJobServer-like type and pass custom services to it. This constructor will be removed in 2.0.0.\")]\n        [EditorBrowsable(EditorBrowsableState.Advanced)]\n        public BackgroundJobServer(\n            [NotNull] BackgroundJobServerOptions options,\n            [NotNull] JobStorage storage,\n            [NotNull] IEnumerable<IBackgroundProcess> additionalProcesses,\n            [CanBeNull] IJobFilterProvider filterProvider,\n            [CanBeNull] JobActivator activator,\n            [CanBeNull] IBackgroundJobFactory factory,\n            [CanBeNull] IBackgroundJobPerformer performer,\n            [CanBeNull] IBackgroundJobStateChanger stateChanger)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n            if (additionalProcesses == null) throw new ArgumentNullException(nameof(additionalProcesses));\n\n            _options = options;\n\n            var processes = new List<IBackgroundProcessDispatcherBuilder>();\n            processes.AddRange(GetRequiredProcesses(filterProvider, activator, factory, performer, stateChanger));\n            processes.AddRange(additionalProcesses.Select(static x => x.UseBackgroundPool(1)));\n\n            var properties = new Dictionary<string, object>\n            {\n                { \"Queues\", options.Queues },\n                { \"WorkerCount\", options.WorkerCount }\n            };\n\n            _logger.Info($\"Starting Hangfire Server using job storage: '{storage}'\");\n\n            storage.WriteOptionsToLog(_logger);\n\n            _logger.Info(\"Using the following options for Hangfire Server:\\r\\n\" +\n                $\"    Worker count: {options.WorkerCount}\\r\\n\" +\n                $\"    Listening queues: {String.Join(\", \", options.Queues.Select(static x => \"'\" + x + \"'\"))}\\r\\n\" +\n                $\"    Shutdown timeout: {options.ShutdownTimeout}\\r\\n\" +\n                $\"    Schedule polling interval: {options.SchedulePollingInterval}\");\n\n            var wrongQueues = new HashSet<string>(StringComparer.Ordinal);\n            foreach (var queue in options.Queues)\n            {\n                if (!EnqueuedState.TryValidateQueueName(queue))\n                {\n                    wrongQueues.Add(queue);\n                }\n            }\n\n            if (wrongQueues.Count > 0)\n            {\n                _logger.Warn($\"These queues fail to match the naming format: {String.Join(\", \", wrongQueues.Select(static x => $\"'{x}'\"))}. A queue name must consist of lowercase letters, digits, underscore, and dash characters only.\");\n            }\n\n            _processingServer = new BackgroundProcessingServer(\n                storage, \n                processes, \n                properties, \n                GetProcessingServerOptions());\n        }\n\n        public void SendStop()\n        {\n            _logger.Debug(\"Hangfire Server is stopping...\");\n            _processingServer.SendStop();\n        }\n\n        public void Dispose()\n        {\n            _processingServer.Dispose();\n            GC.SuppressFinalize(this);\n        }\n\n        [Obsolete(\"This method is a stub. There is no need to call the `Start` method. Will be removed in version 2.0.0.\")]\n        public void Start()\n        {\n        }\n\n        [Obsolete(\"Please call the `Shutdown` method instead. Will be removed in version 2.0.0.\")]\n        public void Stop()\n        {\n            SendStop();\n        }\n\n        [Obsolete(\"Please call the `Shutdown` method instead. Will be removed in version 2.0.0.\")]\n        public void Stop(bool force)\n        {\n            SendStop();\n        }\n\n        public bool WaitForShutdown(TimeSpan timeout)\n        {\n            return _processingServer.WaitForShutdown(timeout);\n        }\n\n        public Task WaitForShutdownAsync(CancellationToken cancellationToken)\n        {\n            return _processingServer.WaitForShutdownAsync(cancellationToken);\n        }\n\n        private IEnumerable<IBackgroundProcessDispatcherBuilder> GetRequiredProcesses(\n            [CanBeNull] IJobFilterProvider filterProvider,\n            [CanBeNull] JobActivator activator,\n            [CanBeNull] IBackgroundJobFactory factory,\n            [CanBeNull] IBackgroundJobPerformer performer,\n            [CanBeNull] IBackgroundJobStateChanger stateChanger)\n        {\n            var processes = new List<IBackgroundProcessDispatcherBuilder>();\n            var timeZoneResolver = _options.TimeZoneResolver ?? new DefaultTimeZoneResolver();\n\n            if (factory == null && performer == null && stateChanger == null)\n            {\n                filterProvider = filterProvider ?? _options.FilterProvider ?? JobFilterProviders.Providers;\n                activator = activator ?? _options.Activator ?? JobActivator.Current;\n\n                factory = new BackgroundJobFactory(filterProvider);\n                performer = new BackgroundJobPerformer(filterProvider, activator, _options.TaskScheduler);\n                stateChanger = new BackgroundJobStateChanger(filterProvider);\n            }\n            else\n            {\n                if (factory == null) throw new ArgumentNullException(nameof(factory));\n                if (performer == null) throw new ArgumentNullException(nameof(performer));\n                if (stateChanger == null) throw new ArgumentNullException(nameof(stateChanger));\n            }\n\n            processes.Add(new Worker(_options.Queues, performer, stateChanger).UseBackgroundPool(_options.WorkerCount, _options.WorkerThreadConfigurationAction));\n\n            if (!_options.IsLightweightServer)\n            {\n                processes.Add(\n                    new DelayedJobScheduler(_options.SchedulePollingInterval, stateChanger)\n                    {\n                        TaskScheduler = _options.TaskScheduler,\n                        MaxDegreeOfParallelism = _options.MaxDegreeOfParallelismForSchedulers\n                    }\n                    .UseBackgroundPool(1));\n\n                processes.Add(\n                    new RecurringJobScheduler(factory, _options.SchedulePollingInterval, timeZoneResolver)\n                        {\n                            TaskScheduler = _options.TaskScheduler,\n                            MaxDegreeOfParallelism = _options.MaxDegreeOfParallelismForSchedulers\n                        }\n                        .UseBackgroundPool(1));\n            }\n\n            return processes;\n        }\n\n        private BackgroundProcessingServerOptions GetProcessingServerOptions()\n        {\n            return new BackgroundProcessingServerOptions\n            {\n                StopTimeout = _options.StopTimeout,\n                ShutdownTimeout = _options.ShutdownTimeout,\n                HeartbeatInterval = _options.HeartbeatInterval,\n#pragma warning disable 618\n                ServerCheckInterval = _options.ServerWatchdogOptions?.CheckInterval ?? _options.ServerCheckInterval,\n                ServerTimeout = _options.ServerWatchdogOptions?.ServerTimeout ?? _options.ServerTimeout,\n#pragma warning restore 618\n                CancellationCheckInterval = _options.CancellationCheckInterval,\n                ServerName = _options.ServerName,\n                ExcludeStorageProcesses = _options.IsLightweightServer\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/BackgroundJobServerOptions.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    public class BackgroundJobServerOptions\n    {\n        // https://github.com/HangfireIO/Hangfire/issues/246\n        private const int MaxDefaultWorkerCount = 20;\n\n        private int _workerCount;\n        private string[] _queues;\n        private TimeSpan _serverTimeout;\n        private TimeSpan _serverCheckInterval;\n        private TimeSpan _heartbeatInterval;\n        private TimeSpan _stopTimeout;\n        private TimeSpan _shutdownTimeout;\n        private TimeSpan _schedulePollingInterval;\n\n        public BackgroundJobServerOptions()\n        {\n            WorkerCount = Math.Min(Environment.ProcessorCount * 5, MaxDefaultWorkerCount);\n            Queues = new[] { EnqueuedState.DefaultQueue };\n            StopTimeout = BackgroundProcessingServerOptions.DefaultStopTimeout;\n            ShutdownTimeout = BackgroundProcessingServer.DefaultShutdownTimeout;\n            SchedulePollingInterval = DelayedJobScheduler.DefaultPollingDelay;\n            HeartbeatInterval = BackgroundProcessingServerOptions.DefaultHeartbeatInterval;\n            ServerTimeout = ServerWatchdog.DefaultServerTimeout;\n            ServerCheckInterval = ServerWatchdog.DefaultCheckInterval;\n            CancellationCheckInterval = ServerJobCancellationWatcher.DefaultCheckInterval;\n            \n            FilterProvider = null;\n            Activator = null;\n            TimeZoneResolver = null;\n            TaskScheduler = TaskScheduler.Default;\n        }\n        \n        public string ServerName { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether storage instance will include only <see cref=\"Worker\"/> and required\n        /// <see cref=\"ServerWatchdog\"/> and <see cref=\"ServerJobCancellationWatcher\"/> processes. No\n        /// storage-related processes or recurring/delayed job schedulers will be included.\n        /// </summary>\n        public bool IsLightweightServer { get; set; }\n\n        public int WorkerCount\n        {\n            get { return _workerCount; }\n            set\n            {\n                if (value <= 0) throw new ArgumentOutOfRangeException(nameof(value), \"WorkerCount property value should be positive.\");\n\n                _workerCount = value;\n            }\n        }\n\n        public string[] Queues\n        {\n            get { return _queues; }\n            set\n            {\n                if (value == null) throw new ArgumentNullException(nameof(value));\n                if (value.Length == 0) throw new ArgumentException(\"You should specify at least one queue to listen.\", nameof(value));\n\n                _queues = value;\n            }\n        }\n\n        public TimeSpan StopTimeout\n        {\n            get => _stopTimeout;\n            set\n            {\n                if ((value < TimeSpan.Zero && value != Timeout.InfiniteTimeSpan) || value.TotalMilliseconds > Int32.MaxValue)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), $\"StopTimeout must be either equal to or less than {Int32.MaxValue} milliseconds and non-negative or infinite\");\n                }\n                _stopTimeout = value;\n            }\n        }\n\n        public TimeSpan ShutdownTimeout\n        {\n            get { return _shutdownTimeout; }\n            set\n            {\n                if ((value < TimeSpan.Zero && value != Timeout.InfiniteTimeSpan) || value.TotalMilliseconds > Int32.MaxValue)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), $\"ShutdownTimeout must be either equal to or less than {Int32.MaxValue} milliseconds and non-negative or infinite\");\n                }\n                _shutdownTimeout = value;\n            }\n        }\n\n        public TimeSpan SchedulePollingInterval\n        {\n            get { return _schedulePollingInterval; }\n            set\n            {\n                if (value < TimeSpan.Zero || value.TotalMilliseconds > Int32.MaxValue)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), $\"SchedulePollingInterval must be non-negative and either equal to or less than {Int32.MaxValue} milliseconds\");\n                }\n\n                _schedulePollingInterval = value;\n            }\n        }\n\n        public TimeSpan HeartbeatInterval\n        {\n            get { return _heartbeatInterval; }\n            set\n            {\n                if (value < TimeSpan.Zero || value > ServerWatchdog.MaxHeartbeatInterval)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), $\"HeartbeatInterval must be either non-negative and equal to or less than {ServerWatchdog.MaxHeartbeatInterval.Hours} hours\");\n                }\n                _heartbeatInterval = value;\n            }\n        }\n\n        public TimeSpan ServerCheckInterval\n        {\n            get { return _serverCheckInterval; }\n            set\n            {\n                if (value < TimeSpan.Zero || value > ServerWatchdog.MaxServerCheckInterval)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), $\"ServerCheckInterval must be either non-negative and equal to or less than {ServerWatchdog.MaxServerCheckInterval.Hours} hours\");\n                }\n                _serverCheckInterval = value;\n            }\n        }\n\n        public TimeSpan ServerTimeout\n        {\n            get { return _serverTimeout; }\n            set\n            {\n                if (value < TimeSpan.Zero || value > ServerWatchdog.MaxServerTimeout)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), $\"ServerTimeout must be either non-negative and equal to or less than {ServerWatchdog.MaxServerTimeout.Hours} hours\");\n                }\n\n                _serverTimeout = value;\n\n            }\n        }\n\n        public TimeSpan CancellationCheckInterval { get; set; }\n\n        [Obsolete(\"Please use `ServerTimeout` or `ServerCheckInterval` options instead. Will be removed in 2.0.0.\")]\n        public ServerWatchdogOptions ServerWatchdogOptions { get; set; }\n\n        [CanBeNull]\n        public IJobFilterProvider FilterProvider { get; set; }\n\n        [CanBeNull]\n        public JobActivator Activator { get; set; }\n\n        [CanBeNull]\n        public ITimeZoneResolver TimeZoneResolver { get; set; }\n\n        [CanBeNull]\n        public TaskScheduler TaskScheduler { get; set; }\n        \n        [CanBeNull]\n        public Action<Thread> WorkerThreadConfigurationAction { get; set; }\n\n        /// <summary>\n        /// Experimental option for schedulers, but not for workers. Gets or sets the\n        /// maximum degree of parallelism for <see cref=\"RecurringJobScheduler\"/>\n        /// and <see cref=\"DelayedJobScheduler\"/> processes, allowing them to enable\n        /// parallel scheduling of recurring and delayed jobs when the specified value\n        /// is greater than <c>1</c>. Parallel work items are executed on the task scheduler\n        /// specified in the <see cref=\"TaskScheduler\"/> property.\n        /// </summary>\n        public int MaxDegreeOfParallelismForSchedulers { get; set; } = 1;\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/CaptureCultureAttribute.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Globalization;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Server;\n\nnamespace Hangfire\n{\n    public sealed class CaptureCultureAttribute : JobFilterAttribute, IClientFilter, IServerFilter\n    {\n        private readonly ILog _logger = LogProvider.GetLogger(typeof(CaptureCultureAttribute));\n\n        public CaptureCultureAttribute() : this(null)\n        {\n        }\n\n        public CaptureCultureAttribute([CanBeNull] string defaultCultureName, bool captureDefault = true)\n            : this(defaultCultureName, defaultCultureName, captureDefault)\n        {\n        }\n\n        public CaptureCultureAttribute(\n            [CanBeNull] string defaultCultureName,\n            [CanBeNull] string defaultUICultureName,\n            bool captureDefault = true)\n        {\n            DefaultCultureName = defaultCultureName;\n            DefaultUICultureName = defaultUICultureName;\n            CaptureDefault = captureDefault;\n\n#if !NETSTANDARD1_3\n            // For backward compatibility, the cached method does not respect user-overridden values.\n            // https://blog.codeinside.eu/2018/05/28/cultureinfo-getculture-vs-new-cultureinfo/\n            // https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.-ctor#system-globalization-cultureinfo-ctor(system-string)\n            CachedCulture = false;\n#endif\n        }\n\n        [CanBeNull]\n        public string DefaultCultureName { get; }\n\n        [CanBeNull]\n        public string DefaultUICultureName { get; }\n\n        public bool CaptureDefault { get; }\n\n#if !NETSTANDARD1_3\n        /// <summary>\n        /// Gets or sets whether to use the <see cref=\"GetCultureInfo\"/> method when getting\n        /// a culture by its name, or create a <see cref=\"CultureInfo\"/> instance using its\n        /// constructor instead. Cached method does not respect user-overridden values associated\n        /// with the current culture specified on the OS level.\n        /// </summary>\n        public bool CachedCulture { get; set; }\n#endif\n\n        public void OnCreating(CreatingContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            var currentCulture = CultureInfo.CurrentCulture;\n            var currentUICulture = CultureInfo.CurrentUICulture;\n\n            if (CaptureDefault == false && currentCulture.Name.Equals(DefaultCultureName, StringComparison.Ordinal))\n            {\n                // Don't set the 'CurrentCulture' job parameter when it's equal to the default one\n            }\n            else\n            {\n                context.SetJobParameter(\"CurrentCulture\", currentCulture.Name);\n            }\n\n            if (CaptureDefault == false && currentUICulture.Name.Equals(DefaultUICultureName, StringComparison.Ordinal))\n            {\n                // Don't set the 'CurrentUICulture' job parameter when it's equal to the default one\n            }\n            else if (GlobalConfiguration.HasCompatibilityLevel(CompatibilityLevel.Version_180) &&\n                     currentUICulture.Equals(currentCulture))\n            {\n                // Don't set the 'CurrentUICulture' when it's the same as 'CurrentCulture' under\n                // CompatibilityLevel.Version_180\n            }\n            else\n            {\n                context.SetJobParameter(\"CurrentUICulture\", currentUICulture.Name);\n            }\n        }\n\n        public void OnCreated(CreatedContext context)\n        {\n        }\n\n        public void OnPerforming(PerformingContext context)\n        {\n            var cultureName = context.GetJobParameter<string>(\"CurrentCulture\", allowStale: true);\n            var uiCultureName = context.GetJobParameter<string>(\"CurrentUICulture\", allowStale: true) ?? cultureName;\n\n            cultureName = cultureName ?? DefaultCultureName;\n            uiCultureName = uiCultureName ?? DefaultUICultureName;\n\n            try\n            {\n                if (cultureName != null)\n                {\n                    context.Items[\"PreviousCulture\"] = CultureInfo.CurrentCulture;\n                    SetCurrentCulture(GetCultureInfo(cultureName));\n                }\n            }\n            catch (CultureNotFoundException ex)\n            {\n                // TODO: Make this overridable, and start with throwing an exception\n                _logger.WarnException($\"Unable to set CurrentCulture for job {context.BackgroundJob.Id} due to an exception\", ex);\n            }\n\n            try\n            {\n                if (uiCultureName != null)\n                {\n                    context.Items[\"PreviousUICulture\"] = CultureInfo.CurrentUICulture;\n                    SetCurrentUICulture(GetCultureInfo(uiCultureName));\n                }\n            }\n            catch (CultureNotFoundException ex)\n            {\n                // TODO: Make this overridable, and start with throwing an exception\n                _logger.WarnException($\"Unable to set CurrentUICulture for job {context.BackgroundJob.Id} due to an exception\", ex);\n            }\n        }\n\n        public void OnPerformed(PerformedContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            if (context.Items.TryGetValue(\"PreviousCulture\", out var culture))\n            {\n                SetCurrentCulture((CultureInfo)culture);\n            }\n            if (context.Items.TryGetValue(\"PreviousUICulture\", out var uiCulture))\n            {\n                SetCurrentUICulture((CultureInfo)uiCulture);\n            }\n        }\n        \n        private static void SetCurrentCulture(CultureInfo value)\n        {\n#if !NETSTANDARD1_3\n            System.Threading.Thread.CurrentThread.CurrentCulture = value;\n#else\n            CultureInfo.CurrentCulture = value;\n#endif\n        }\n\n        // ReSharper disable once InconsistentNaming\n        private static void SetCurrentUICulture(CultureInfo value)\n        {\n#if !NETSTANDARD1_3\n            System.Threading.Thread.CurrentThread.CurrentUICulture = value;\n#else\n            CultureInfo.CurrentUICulture = value;\n#endif\n        }\n\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        private CultureInfo GetCultureInfo(string cultureName)\n        {\n#if !NETSTANDARD1_3\n            if (CachedCulture)\n            {\n                return CultureInfo.GetCultureInfo(cultureName);\n            }\n#endif\n\n            return new CultureInfo(cultureName);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Client/BackgroundJobFactory.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Profiling;\nusing Hangfire.States;\n\nnamespace Hangfire.Client\n{\n    public class BackgroundJobFactory : IBackgroundJobFactory\n    {\n        private readonly IJobFilterProvider _filterProvider;\n        private readonly IBackgroundJobFactory _innerFactory;\n\n        public BackgroundJobFactory()\n            : this(JobFilterProviders.Providers)\n        {\n        }\n\n        public BackgroundJobFactory([NotNull] IJobFilterProvider filterProvider)\n            : this(filterProvider, new CoreBackgroundJobFactory(new StateMachine(filterProvider)))\n        {\n        }\n\n        public int RetryAttempts\n        {\n            get\n            {\n                if (_innerFactory is CoreBackgroundJobFactory factory)\n                {\n                    return factory.RetryAttempts;\n                }\n\n                return 0;\n            }\n            set\n            {\n                if (_innerFactory is CoreBackgroundJobFactory factory)\n                {\n                    factory.RetryAttempts = value;\n                }\n            }\n        }\n\n        internal BackgroundJobFactory(\n            [NotNull] IJobFilterProvider filterProvider, \n            [NotNull] IBackgroundJobFactory innerFactory)\n        {\n            if (filterProvider == null) throw new ArgumentNullException(nameof(filterProvider));\n            if (innerFactory == null) throw new ArgumentNullException(nameof(innerFactory));\n\n            _filterProvider = filterProvider;\n            _innerFactory = innerFactory;\n        }\n\n        public IStateMachine StateMachine => _innerFactory.StateMachine;\n\n        public BackgroundJob Create(CreateContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            var filterInfo = GetFilters(context.Job);\n\n            try\n            {\n                context.Factory = this;\n\n                var createdContext = CreateWithFilters(context, filterInfo.ClientFilters);\n                return createdContext.BackgroundJob;\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                var exceptionContext = new ClientExceptionContext(context, ex);\n\n                InvokeExceptionFilters(exceptionContext, filterInfo.ClientExceptionFiltersReversed);\n                if (!exceptionContext.ExceptionHandled)\n                {\n                    throw;\n                }\n\n                return null;\n            }\n            finally\n            {\n                context.Factory = null;\n            }\n        }\n\n        private JobFilterInfo GetFilters(Job job)\n        {\n            return new JobFilterInfo(_filterProvider.GetFilters(job));\n        }\n\n        private CreatedContext CreateWithFilters(\n            CreateContext context, \n            JobFilterInfo.FilterCollection<IClientFilter> filters)\n        {\n            var preContext = new CreatingContext(context);\n            var enumerator = filters.GetEnumerator();\n\n            return InvokeNextClientFilter(ref enumerator, _innerFactory, context, preContext);\n        }\n        \n        private static CreatedContext InvokeNextClientFilter(\n            ref JobFilterInfo.FilterCollection<IClientFilter>.Enumerator enumerator,\n            IBackgroundJobFactory innerFactory,\n            CreateContext context,\n            CreatingContext preContext)\n        {\n            if (enumerator.MoveNext())\n            {\n                return InvokeClientFilter(ref enumerator, innerFactory, context, preContext);\n            }\n\n            var backgroundJob = innerFactory.Create(context);\n            return new CreatedContext(context, backgroundJob, false, null);\n        }\n\n        private static CreatedContext InvokeClientFilter(\n            ref JobFilterInfo.FilterCollection<IClientFilter>.Enumerator enumerator,\n            IBackgroundJobFactory innerFactory,\n            CreateContext context,\n            CreatingContext preContext)\n        {\n            var filter = enumerator.Current!;\n\n            preContext.Profiler.InvokeMeasured(\n                new KeyValuePair<IClientFilter, CreatingContext>(filter, preContext),\n                InvokeOnCreating,\n                static ctx => $\"OnCreating for {ctx.Value.Job.Type.FullName}.{ctx.Value.Job.Method.Name}\");\n\n            if (preContext.Canceled)\n            {\n                return new CreatedContext(preContext, null, true, null);\n            }\n\n            var wasError = false;\n            CreatedContext postContext;\n            try\n            {\n                postContext = InvokeNextClientFilter(ref enumerator, innerFactory, context, preContext);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                wasError = true;\n                postContext = new CreatedContext(preContext, null, false, ex);\n\n                postContext.Profiler.InvokeMeasured(\n                    new KeyValuePair<IClientFilter, CreatedContext>(filter, postContext),\n                    InvokeOnCreated,\n                    static ctx => $\"OnCreated for {ctx.Value.BackgroundJob?.Id ?? \"(null)\"}\");\n\n                if (!postContext.ExceptionHandled)\n                {\n                    throw;\n                }\n            }\n\n            if (!wasError)\n            {\n                postContext.Profiler.InvokeMeasured(\n                    new KeyValuePair<IClientFilter, CreatedContext>(filter, postContext),\n                    InvokeOnCreated,\n                    static ctx => $\"OnCreated for {ctx.Value.BackgroundJob?.Id ?? \"(null)\"}\");\n            }\n\n            return postContext;\n        }\n\n        private static void InvokeOnCreating(KeyValuePair<IClientFilter, CreatingContext> x)\n        {\n            try\n            {\n                x.Key.OnCreating(x.Value);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                ex.PreserveOriginalStackTrace();\n                throw;\n            }\n        }\n\n        private static void InvokeOnCreated(KeyValuePair<IClientFilter, CreatedContext> x)\n        {\n            try\n            {\n                x.Key.OnCreated(x.Value);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                ex.PreserveOriginalStackTrace();\n                throw;\n            }\n        }\n\n        private static void InvokeExceptionFilters(\n            ClientExceptionContext context, JobFilterInfo.ReversedFilterCollection<IClientExceptionFilter> filters)\n        {\n            foreach (var filter in filters)\n            {\n                context.Profiler.InvokeMeasured(\n                    new KeyValuePair<IClientExceptionFilter, ClientExceptionContext>(filter, context),\n                    InvokeOnClientException,\n                    static ctx => $\"OnClientException for {ctx.Value.Job.Type.FullName}.{ctx.Value.Job.Method.Name}\");\n            }\n        }\n\n        private static void InvokeOnClientException(KeyValuePair<IClientExceptionFilter, ClientExceptionContext> x)\n        {\n            try\n            {\n                x.Key.OnClientException(x.Value);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                ex.PreserveOriginalStackTrace();\n                throw;\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Client/ClientExceptionContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.Client\n{\n    /// <summary>\n    /// Provides the context for the <see cref=\"IClientExceptionFilter.OnClientException\"/>\n    /// method of the <see cref=\"IClientExceptionFilter\"/> interface.\n    /// </summary>\n    public class ClientExceptionContext : CreateContext\n    {\n        public ClientExceptionContext(CreateContext createContext, Exception exception)\n            : base(createContext)\n        {\n            if (exception == null) throw new ArgumentNullException(nameof(exception));\n\n            Exception = exception;\n        }\n\n        /// <summary>\n        /// Gets an exception that occurred during the creation of the job.\n        /// </summary>\n        public Exception Exception { get; }\n\n        /// <summary>\n        /// Gets or sets a value that indicates that this <see cref=\"ClientExceptionContext\"/>\n        /// object handles an exception occurred during the creation of the job.\n        /// </summary>\n        public bool ExceptionHandled { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Client/CoreBackgroundJobFactory.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.ExceptionServices;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.States;\nusing Hangfire.Storage;\n\nnamespace Hangfire.Client\n{\n    internal sealed class CoreBackgroundJobFactory : IBackgroundJobFactory\n    {\n        private readonly ILog _logger = LogProvider.GetLogger(typeof(CoreBackgroundJobFactory));\n        private readonly object _syncRoot = new object();\n        private int _retryAttempts;\n        private Func<int, TimeSpan> _retryDelayFunc;\n\n        public CoreBackgroundJobFactory([NotNull] IStateMachine stateMachine)\n        {\n            StateMachine = stateMachine ?? throw new ArgumentNullException(nameof(stateMachine));\n            RetryAttempts = 0;\n            RetryDelayFunc = GetRetryDelay;\n        }\n\n        public IStateMachine StateMachine { get; }\n\n        public int RetryAttempts\n        {\n            get { lock (_syncRoot) { return _retryAttempts; } }\n            set { lock (_syncRoot) { _retryAttempts = value; } }\n        }\n\n        public Func<int, TimeSpan> RetryDelayFunc\n        {\n            get { lock (_syncRoot) { return _retryDelayFunc; } }\n            set { lock (_syncRoot) { _retryDelayFunc = value; } }\n        }\n\n        public BackgroundJob Create(CreateContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            if (context.Job.Queue != null && !context.Storage.HasFeature(JobStorageFeatures.JobQueueProperty))\n            {\n                throw new NotSupportedException(\"Current storage doesn't support specifying queues directly for a specific job. Please use the QueueAttribute instead.\");\n            }\n\n            var parameters = context.Parameters.ToDictionary(\n                static x => x.Key,\n                static x => SerializationHelper.Serialize(x.Value, SerializationOption.User));\n\n            var createdAt = DateTime.UtcNow;\n            var expireIn = TimeSpan.FromDays(30);\n\n            return CreateBackgroundJobTwoSteps(context, parameters, createdAt, expireIn);\n        }\n\n        private BackgroundJob CreateBackgroundJobTwoSteps(CreateContext context, Dictionary<string, string> parameters, DateTime createdAt, TimeSpan expireIn)\n        {\n            var attemptsLeft = Math.Max(RetryAttempts, 0);\n\n            // Retry may cause multiple background jobs to be created, especially when there's\n            // a timeout-related exception. But initialization attempt will be performed only\n            // for the most recent job, leaving all the previous ones in a non-initialized state\n            // and making them invisible to other parts of the system, since no one knows their\n            // identifiers. Since they also will be eventually expired leaving no trace, we can\n            // consider that only one background job is created, regardless of retry attempts\n            // number.\n            var jobId = RetryOnException(\n                ref attemptsLeft,\n                static (_, ctx) => ctx.Context.Connection.CreateExpiredJob(\n                    ctx.Context.Job,\n                    ctx.Parameters,\n                    ctx.CreatedAt,\n                    ctx.ExpireIn),\n                new JobCreateContext { Context = context, Parameters = parameters, CreatedAt = createdAt, ExpireIn = expireIn });\n\n            if (String.IsNullOrEmpty(jobId))\n            {\n                return null;\n            }\n\n            var backgroundJob = new BackgroundJob(jobId, context.Job, createdAt, parameters);\n\n            if (context.InitialState != null)\n            {\n                RetryOnException(ref attemptsLeft, static (attempt, ctx) =>\n                {\n                    if (attempt > 0)\n                    {\n                        // Normally, a distributed lock should be applied when making a retry, since\n                        // it's possible to get a timeout exception, when transaction was actually\n                        // committed. But since background job can't be returned to a position where\n                        // its state is null, and since only the current thread knows the job's identifier\n                        // when its state is null, and since we shouldn't do anything when it's non-null,\n                        // there will be no any race conditions.\n                        var data = ctx.Context.Connection.GetJobData(ctx.BackgroundJob.Id);\n                        if (data == null) throw new InvalidOperationException($\"Was unable to initialize a background job '{ctx.BackgroundJob.Id}', because it doesn't exists.\");\n\n                        if (!String.IsNullOrEmpty(data.State)) return;\n                    }\n\n                    using (var transaction = ctx.Context.Connection.CreateWriteTransaction())\n                    {\n                        var applyContext = new ApplyStateContext(\n                            ctx.Context.Storage,\n                            ctx.Context.Connection,\n                            transaction,\n                            ctx.BackgroundJob,\n                            ctx.Context.InitialState!,\n                            oldStateName: null,\n                            ctx.Context.Profiler,\n                            ctx.StateMachine);\n\n                        ctx.StateMachine.ApplyState(applyContext);\n\n                        transaction.Commit();\n                    }\n                }, new JobInitializeContext { Context = context, StateMachine = StateMachine, BackgroundJob = backgroundJob });\n            }\n\n            return backgroundJob;\n        }\n\n        private void RetryOnException<TContext>(ref int attemptsLeft, Action<int, TContext> action, TContext context)\n        {\n            RetryOnException(ref attemptsLeft, static (attempt, ctx) =>\n            {\n                ctx.Key(attempt, ctx.Value);\n                return true;\n            }, new KeyValuePair<Action<int, TContext>, TContext>(action, context));\n        }\n\n        private TResult RetryOnException<TContext, TResult>(ref int attemptsLeft, Func<int, TContext, TResult> action, TContext context)\n        {\n            List<Exception> exceptions = null;\n            var attempt = 0;\n            var delay = TimeSpan.Zero;\n\n            do\n            {\n                try\n                {\n                    if (delay > TimeSpan.Zero)\n                    {\n                        Thread.Sleep(delay);\n                    }\n\n                    return action(attempt++, context);\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    (exceptions ??= new List<Exception>()).Add(ex);\n                    _logger.DebugException(\"An exception occurred while creating a background job, see inner exception for details.\", ex);\n                    delay = RetryDelayFunc(attempt);\n                }\n            } while (attemptsLeft-- > 0);\n\n            if (exceptions.Count == 1)\n            {\n                ExceptionDispatchInfo.Capture(exceptions[0]).Throw();\n            }\n\n            throw new AggregateException(exceptions);\n        }\n\n        private static TimeSpan GetRetryDelay(int retryAttempt)\n        {\n            switch (retryAttempt)\n            {\n                case 1: return TimeSpan.Zero;\n                case 2: return TimeSpan.FromMilliseconds(50);\n                case 3: return TimeSpan.FromMilliseconds(100);\n                default: return TimeSpan.FromMilliseconds(500);\n            }\n        }\n\n        private readonly record struct JobCreateContext\n        {\n            public required CreateContext Context { get; init; }\n            public required Dictionary<string, string> Parameters { get; init; }\n            public required DateTime CreatedAt { get; init; }\n            public required TimeSpan ExpireIn { get; init; }\n        }\n\n        private readonly record struct JobInitializeContext\n        {\n            public required CreateContext Context { get; init; }\n            public required IStateMachine StateMachine { get; init; }\n            public required BackgroundJob BackgroundJob { get; init; }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Client/CreateContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Profiling;\nusing Hangfire.States;\nusing Hangfire.Storage;\n\nnamespace Hangfire.Client\n{\n    /// <summary>\n    /// Provides information about the context in which the job is created.\n    /// </summary>\n    public class CreateContext\n    {\n        public CreateContext([NotNull] CreateContext context)\n            : this(context.Storage, context.Connection, context.Job, context.InitialState, context.Parameters, context.Profiler, context.Items)\n        {\n            Factory = context.Factory;\n        }\n\n        public CreateContext(\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [NotNull] Job job,\n            [CanBeNull] IState initialState)\n            : this(storage, connection, job, initialState, null)\n        {\n        }\n\n        public CreateContext(\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [NotNull] Job job,\n            [CanBeNull] IState initialState,\n            [CanBeNull] IDictionary<string, object> parameters)\n            : this(storage, connection, job, initialState, parameters, EmptyProfiler.Instance, null)\n        {\n        }\n\n        internal CreateContext(\n            [NotNull] JobStorage storage, \n            [NotNull] IStorageConnection connection, \n            [NotNull] Job job, \n            [CanBeNull] IState initialState,\n            [CanBeNull] IDictionary<string, object> parameters,\n            [NotNull] IProfiler profiler,\n            [CanBeNull] IDictionary<string, object> items)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            if (job == null) throw new ArgumentNullException(nameof(job));\n\n            Storage = storage;\n            Connection = connection;\n            Job = job;\n            InitialState = initialState;\n            Profiler = profiler;\n\n            Items = items ?? new Dictionary<string, object>();\n            Parameters = parameters ?? new Dictionary<string, object>();\n        }\n\n        [NotNull]\n        public JobStorage Storage { get; }\n\n        [NotNull]\n        public IStorageConnection Connection { get; }\n\n        /// <summary>\n        /// Gets an instance of the key-value storage. You can use it\n        /// to pass additional information between different client filters\n        /// or just between different methods.\n        /// </summary>\n        [NotNull]\n        public IDictionary<string, object> Items { get; }\n\n        [NotNull]\n        public virtual IDictionary<string, object> Parameters { get; }\n            \n        [NotNull]\n        public Job Job { get; }\n\n        /// <summary>\n        /// Gets the initial state of the creating job. Note, that\n        /// the final state of the created job could be changed after \n        /// the registered instances of the <see cref=\"IElectStateFilter\"/>\n        /// class are doing their job.\n        /// </summary>\n        [CanBeNull]\n        public IState InitialState { get; }\n\n        [NotNull]\n        internal IProfiler Profiler { get; }\n        \n        [CanBeNull]\n        public IBackgroundJobFactory Factory { get; internal set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Client/CreatedContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Client\n{\n    /// <summary>\n    /// Provides the context for the <see cref=\"IClientFilter.OnCreated\"/> \n    /// method of the <see cref=\"IClientFilter\"/> interface.\n    /// </summary>\n    public class CreatedContext : CreateContext\n    {\n        public CreatedContext(\n            [NotNull] CreateContext context, \n            [CanBeNull] BackgroundJob backgroundJob,\n            bool canceled, \n            [CanBeNull] Exception exception)\n            : base(context)\n        {\n            BackgroundJob = backgroundJob;\n            Canceled = canceled;\n            Exception = exception;\n        }\n\n        [CanBeNull]\n        [Obsolete(\"Please use `BackgroundJob` property instead. Will be removed in 2.0.0.\")]\n        public string JobId => BackgroundJob?.Id;\n\n        [CanBeNull]\n        public BackgroundJob BackgroundJob { get; }\n        \n        public override IDictionary<string, object> Parameters => new ReadOnlyDictionary<string, object>(base.Parameters);\n\n        /// <summary>\n        /// Gets an exception that occurred during the creation of the job.\n        /// </summary>\n        [CanBeNull]\n        public Exception Exception { get; }\n\n        /// <summary>\n        /// Gets a value that indicates that this <see cref=\"CreatedContext\"/>\n        /// object was canceled.\n        /// </summary>\n        public bool Canceled { get; }\n\n        /// <summary>\n        /// Gets or sets a value that indicates that this <see cref=\"CreatedContext\"/>\n        /// object handles an exception occurred during the creation of the job.\n        /// </summary>\n        public bool ExceptionHandled { get; set; }\n\n        [Obsolete(\"This method only throws InvalidOperationException, will be removed in 2.0.0.\")]\n        public void SetJobParameter([NotNull] string name, object value)\n        {\n            if (String.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));\n\n            throw new InvalidOperationException(\"Could not set parameter for a created job.\");\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Client/CreatingContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.Client\n{\n    /// <summary>\n    /// Provides the context for the <see cref=\"IClientFilter.OnCreating\"/>\n    /// method of the <see cref=\"IClientFilter\"/> interface.\n    /// </summary>\n    public class CreatingContext : CreateContext\n    {\n        public CreatingContext(CreateContext context)\n            : base(context)\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets a value that indicates that this <see cref=\"CreatingContext\"/>\n        /// object was canceled.\n        /// </summary>\n        public bool Canceled { get; set; }\n\n        /// <summary>\n        /// Sets the job parameter of the specified <paramref name=\"name\"/>\n        /// to the corresponding <paramref name=\"value\"/>. The value of the\n        /// parameter is serialized to a JSON string.\n        /// </summary>\n        /// \n        /// <param name=\"name\">The name of the parameter.</param>\n        /// <param name=\"value\">The value of the parameter.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\">The <paramref name=\"name\"/> is null or empty.</exception>\n        public void SetJobParameter(string name, object value)\n        {\n            if (String.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));\n            Parameters[name] = value;\n        }\n\n        /// <summary>\n        /// Gets the job parameter of the specified <paramref name=\"name\"/>\n        /// if it exists. The parameter is deserialized from a JSON \n        /// string value to the given type <typeparamref name=\"T\"/>.\n        /// </summary>\n        /// \n        /// <typeparam name=\"T\">The type of the parameter.</typeparam>\n        /// <param name=\"name\">The name of the parameter.</param>\n        /// <returns>The value of the given parameter if it exists or null otherwise.</returns>\n        /// \n        /// <exception cref=\"ArgumentNullException\">The <paramref name=\"name\"/> is null or empty.</exception>\n        /// <exception cref=\"InvalidOperationException\">Could not deserialize the parameter value to the type <typeparamref name=\"T\"/>.</exception>\n        public T GetJobParameter<T>(string name)\n        {\n            if (String.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));\n\n            try\n            {\n                return Parameters.TryGetValue(name, out var parameter)\n                    ? (T)parameter\n                    : default(T);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new InvalidOperationException(\n                    $\"Could not get a value of the job parameter `{name}`. See inner exception for details.\", ex);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Client/IBackgroundJobFactory.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing Hangfire.Annotations;\nusing Hangfire.States;\n\nnamespace Hangfire.Client\n{\n    /// <summary>\n    /// This interface acts as extensibility point for the process\n    /// of job creation. See the default implementation in the\n    /// <see cref=\"BackgroundJobFactory\"/> class.\n    /// </summary>\n    public interface IBackgroundJobFactory\n    {\n        /// <summary>\n        /// Gets a state machine that's responsible for initial state change.\n        /// </summary>\n        [NotNull]\n        IStateMachine StateMachine { get; }\n\n        /// <summary>\n        /// Runs the process of job creation with the specified context.\n        /// </summary>\n        [CanBeNull]\n        BackgroundJob Create([NotNull] CreateContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Client/IClientExceptionFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Client\n{\n    /// <summary>\n    /// Defines methods that are required for the client exception filter.\n    /// </summary>\n    public interface IClientExceptionFilter\n    {\n        /// <summary>\n        /// Called when an exception occurred during the creation of the job.\n        /// </summary>\n        /// <param name=\"filterContext\">The filter context.</param>\n        void OnClientException(ClientExceptionContext filterContext);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Client/IClientFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Client\n{\n    /// <summary>\n    /// Defines methods that are required for a client filter.\n    /// </summary>\n    public interface IClientFilter\n    {\n        /// <summary>\n        /// Called before the creation of the job. \n        /// </summary>\n        /// <param name=\"context\">The filter context.</param>\n        void OnCreating(CreatingContext context);\n\n        /// <summary>\n        /// Called after the creation of the job.\n        /// </summary>\n        /// <param name=\"context\">The filter context.</param>\n        void OnCreated(CreatedContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Common/CachedExpressionCompiler.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// The caching expression tree compiler was copied from MVC core to MVC Futures so that Futures code could benefit\n    /// from it and so that it could be exposed as a public API. This is the only public entry point into the system.\n    /// See the comments in the ExpressionUtil namespace for more information.\n    ///\n    /// The unit tests for the ExpressionUtil.* types are in the System.Web.Mvc.Test project.\n    /// </summary>\n    [ExcludeFromCodeCoverage]\n    internal static class CachedExpressionCompiler\n    {\n        private static readonly ParameterExpression UnusedParameterExpr = Expression.Parameter(typeof(object), \"_unused\");\n\n        /// <summary>\n        /// Evaluates an expression (not a LambdaExpression), e.g. 2 + 2.\n        /// </summary>\n        /// <param name=\"arg\"></param>\n        /// <returns>Expression result.</returns>\n        public static object Evaluate(Expression arg)\n        {\n            if (arg == null)\n            {\n                throw new ArgumentNullException(nameof(arg));\n            }\n\n            Func<object, object> func = Wrap(arg);\n            return func(null);\n        }\n\n        private static Func<object, object> Wrap(Expression arg)\n        {\n            var lambdaExpr = Expression.Lambda<Func<object, object>>(Expression.Convert(arg, typeof(object)), UnusedParameterExpr);\n            return ExpressionUtil.CachedExpressionCompiler.Process(lambdaExpr);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/CancellationTokenExtentions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2018 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Diagnostics;\nusing System.Threading;\nusing Hangfire.Logging;\n\nnamespace Hangfire.Common\n{\n    public static class CancellationTokenExtentions\n    {\n        /// <summary>\n        /// Returns a class that contains a <see cref=\"EventWaitHandle\"/> that is set, when\n        /// the given <paramref name=\"cancellationToken\"/> is canceled. This method is based\n        /// on cancellation token registration and avoids using the <see cref=\"CancellationToken.WaitHandle\"/>\n        /// property as it may lead to high CPU issues.\n        /// </summary>\n        public static CancellationEvent GetCancellationEvent(this CancellationToken cancellationToken)\n        {\n            return new CancellationEvent(cancellationToken);\n        }\n\n        /// <summary>\n        /// Performs a wait until the specified <paramref name=\"timeout\"/> is elapsed or the\n        /// given cancellation token is canceled and throw <see cref=\"OperationCanceledException\"/>\n        /// exception if wait succeeded. The wait is performed on a dedicated event\n        /// wait handle to avoid using the <see cref=\"CancellationToken.WaitHandle\"/> property\n        /// that may lead to high CPU issues.\n        /// </summary>\n        public static void WaitOrThrow(this CancellationToken cancellationToken, TimeSpan timeout)\n        {\n            if (Wait(cancellationToken, timeout))\n            {\n                throw new OperationCanceledException(cancellationToken);\n            }\n        }\n\n        /// <summary>\n        /// Performs a wait until the specified <paramref name=\"timeout\"/> is elapsed or the\n        /// given cancellation token is canceled. The wait is performed on a dedicated event\n        /// wait handle to avoid using the <see cref=\"CancellationToken.WaitHandle\"/> property\n        /// that may lead to high CPU issues.\n        /// </summary>\n        public static bool Wait(this CancellationToken cancellationToken, TimeSpan timeout)\n        {\n            using var cancellationEvent = GetCancellationEvent(cancellationToken);\n\n            var stopwatch = Stopwatch.StartNew();\n            var waitResult = cancellationEvent.WaitHandle.WaitOne(timeout);\n            stopwatch.Stop();\n\n            var timeoutThreshold = TimeSpan.FromMilliseconds(1000);\n            var elapsedThreshold = TimeSpan.FromMilliseconds(500);\n            var protectionTime = TimeSpan.FromSeconds(1);\n\n            // There was a precedent of the following message logged when CancellationToken.WaitHandle\n            // was used directly, instead of having our custom CancellationEvent class used, please see\n            // https://github.com/HangfireIO/Hangfire/issues/2447\n            if (!cancellationToken.IsCancellationRequested &&\n                timeout >= timeoutThreshold &&\n                stopwatch.Elapsed < elapsedThreshold)\n            {\n                try\n                {\n                    var logger = LogProvider.GetLogger(typeof(CancellationTokenExtentions));\n                    logger.Error($\"Actual wait time for non-canceled token was '{stopwatch.Elapsed.TotalMilliseconds}' ms instead of '{timeout.TotalMilliseconds}' ms, wait result: {waitResult}, using protective wait. Please report this to Hangfire developers.\");\n                }\n                finally\n                {\n                    Thread.Sleep(protectionTime);\n                }\n            }\n\n            return waitResult;\n        }\n\n        public sealed class CancellationEvent : IDisposable\n        {\n            private static readonly Action<object> SetEventCallback = SetEvent;\n\n            private readonly ManualResetEvent _mre;\n            private CancellationTokenRegistration _registration;\n\n            public CancellationEvent(CancellationToken cancellationToken)\n            {\n                _mre = new ManualResetEvent(false);\n                _registration = cancellationToken.Register(SetEventCallback, _mre);\n            }\n\n            public EventWaitHandle WaitHandle => _mre;\n\n            public void Dispose()\n            {\n                _registration.Dispose();\n                _mre.Dispose();\n            }\n\n            private static void SetEvent(object state)\n            {\n                try\n                {\n                    ((ManualResetEvent)state).Set();\n                }\n                catch (ObjectDisposedException)\n                {\n                    // When our event instance is already disposed, we already\n                    // aren't interested in any notifications. This statement\n                    // is just to ensure we don't throw any exceptions.\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/BinaryExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // BinaryExpression fingerprint class\n    // Useful for things like array[index]\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class BinaryExpressionFingerprint : ExpressionFingerprint\n    {\n        public BinaryExpressionFingerprint(ExpressionType nodeType, Type type, MethodInfo method)\n            : base(nodeType, type)\n        {\n            // Other properties on BinaryExpression (like IsLifted / IsLiftedToNull) are simply derived\n            // from Type and NodeType, so they're not necessary for inclusion in the fingerprint.\n\n            Method = method;\n        }\n\n        // http://msdn.microsoft.com/en-us/library/system.linq.expressions.binaryexpression.method.aspx\n        public MethodInfo Method { get; }\n\n        public override bool Equals(object obj)\n        {\n            BinaryExpressionFingerprint other = obj as BinaryExpressionFingerprint;\n            return (other != null)\n                   && Equals(Method, other.Method)\n                   && Equals(other);\n        }\n\n        internal override void AddToHashCodeCombiner(HashCodeCombiner combiner)\n        {\n            combiner.AddObject(Method);\n            base.AddToHashCodeCombiner(combiner);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/CachedExpressionCompiler.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\n\n// ReSharper disable All\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    [ExcludeFromCodeCoverage]\n    internal static class CachedExpressionCompiler\n    {\n        // This is the entry point to the cached expression compilation system. The system\n        // will try to turn the expression into an actual delegate as quickly as possible,\n        // relying on cache lookups and other techniques to save time if appropriate.\n        // If the provided expression is particularly obscure and the system doesn't know\n        // how to handle it, we'll just compile the expression as normal.\n        public static Func<TModel, TValue> Process<TModel, TValue>(Expression<Func<TModel, TValue>> lambdaExpression)\n        {\n            return Compiler<TModel, TValue>.Compile(lambdaExpression);\n        }\n\n        private static class Compiler<TIn, TOut>\n        {\n            private static Func<TIn, TOut> _identityFunc;\n\n            private static readonly ConcurrentDictionary<MemberInfo, Func<TIn, TOut>> _simpleMemberAccessDict =\n                new ConcurrentDictionary<MemberInfo, Func<TIn, TOut>>();\n\n            private static readonly ConcurrentDictionary<MemberInfo, Func<object, TOut>> _constMemberAccessDict =\n                new ConcurrentDictionary<MemberInfo, Func<object, TOut>>();\n\n            private static readonly ConcurrentDictionary<ExpressionFingerprintChain, Hoisted<TIn, TOut>> _fingerprintedCache =\n                new ConcurrentDictionary<ExpressionFingerprintChain, Hoisted<TIn, TOut>>();\n\n            public static Func<TIn, TOut> Compile(Expression<Func<TIn, TOut>> expr)\n            {\n                return CompileFromIdentityFunc(expr)\n                       ?? CompileFromConstLookup(expr)\n                       ?? CompileFromMemberAccess(expr)\n                       ?? CompileFromFingerprint(expr)\n                       ?? CompileSlow(expr);\n            }\n\n            private static Func<TIn, TOut> CompileFromConstLookup(Expression<Func<TIn, TOut>> expr)\n            {\n                ConstantExpression constExpr = expr.Body as ConstantExpression;\n                if (constExpr != null)\n                {\n                    // model => {const}\n\n                    TOut constantValue = (TOut)constExpr.Value;\n                    return _ => constantValue;\n                }\n\n                return null;\n            }\n\n            private static Func<TIn, TOut> CompileFromIdentityFunc(Expression<Func<TIn, TOut>> expr)\n            {\n                if (expr.Body == expr.Parameters[0])\n                {\n                    // model => model\n\n                    // don't need to lock, as all identity funcs are identical\n                    return _identityFunc ?? (_identityFunc = expr.Compile());\n                }\n\n                return null;\n            }\n\n            private static Func<TIn, TOut> CompileFromFingerprint(Expression<Func<TIn, TOut>> expr)\n            {\n                List<object> capturedConstants;\n                ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);\n\n                if (fingerprint != null)\n                {\n                    var del = _fingerprintedCache.GetOrAdd(fingerprint, _ =>\n                    {\n                        // Fingerprinting succeeded, but there was a cache miss. Rewrite the expression\n                        // and add the rewritten expression to the cache.\n\n                        var hoistedExpr = HoistingExpressionVisitor<TIn, TOut>.Hoist(expr);\n                        return hoistedExpr.Compile();\n                    });\n                    return model => del(model, capturedConstants);\n                }\n\n                // couldn't be fingerprinted\n                return null;\n            }\n\n            private static Func<TIn, TOut> CompileFromMemberAccess(Expression<Func<TIn, TOut>> expr)\n            {\n                // Performance tests show that on the x64 platform, special-casing static member and\n                // captured local variable accesses is faster than letting the fingerprinting system\n                // handle them. On the x86 platform, the fingerprinting system is faster, but only\n                // by around one microsecond, so it's not worth it to complicate the logic here with\n                // an architecture check.\n\n                MemberExpression memberExpr = expr.Body as MemberExpression;\n                if (memberExpr != null)\n                {\n                    if (memberExpr.Expression == expr.Parameters[0] || memberExpr.Expression == null)\n                    {\n                        // model => model.Member or model => StaticMember\n                        return _simpleMemberAccessDict.GetOrAdd(memberExpr.Member, _ => expr.Compile());\n                    }\n\n                    ConstantExpression constExpr = memberExpr.Expression as ConstantExpression;\n                    if (constExpr != null)\n                    {\n                        // model => {const}.Member (captured local variable)\n                        var del = _constMemberAccessDict.GetOrAdd(memberExpr.Member, _ =>\n                        {\n                            // rewrite as capturedLocal => ((TDeclaringType)capturedLocal).Member\n                            var constParamExpr = Expression.Parameter(typeof(object), \"capturedLocal\");\n                            var constCastExpr = Expression.Convert(constParamExpr, memberExpr.Member.DeclaringType);\n                            var newMemberAccessExpr = memberExpr.Update(constCastExpr);\n                            var newLambdaExpr = Expression.Lambda<Func<object, TOut>>(newMemberAccessExpr, constParamExpr);\n                            return newLambdaExpr.Compile();\n                        });\n\n                        object capturedLocal = constExpr.Value;\n                        return _ => del(capturedLocal);\n                    }\n                }\n\n                return null;\n            }\n\n            private static Func<TIn, TOut> CompileSlow(Expression<Func<TIn, TOut>> expr)\n            {\n                // fallback compilation system - just compile the expression directly\n                return expr.Compile();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/ConditionalExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // ConditionalExpression fingerprint class\n    // Expression of form (test) ? ifTrue : ifFalse\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class ConditionalExpressionFingerprint : ExpressionFingerprint\n    {\n        public ConditionalExpressionFingerprint(ExpressionType nodeType, Type type)\n            : base(nodeType, type)\n        {\n            // There are no properties on ConditionalExpression that are worth including in\n            // the fingerprint.\n        }\n\n        public override bool Equals(object obj)\n        {\n            ConditionalExpressionFingerprint other = obj as ConditionalExpressionFingerprint;\n            return (other != null)\n                   && Equals(other);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/ConstantExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // ConstantExpression fingerprint class\n    //\n    // A ConstantExpression might represent a captured local variable, so we can't compile\n    // the value directly into the cached function. Instead, a placeholder is generated\n    // and the value is hoisted into a local variables array. This placeholder can then\n    // be compiled and cached, and the array lookup happens at runtime.\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class ConstantExpressionFingerprint : ExpressionFingerprint\n    {\n        public ConstantExpressionFingerprint(ExpressionType nodeType, Type type)\n            : base(nodeType, type)\n        {\n            // There are no properties on ConstantExpression that are worth including in\n            // the fingerprint.\n        }\n\n        public override bool Equals(object obj)\n        {\n            ConstantExpressionFingerprint other = obj as ConstantExpressionFingerprint;\n            return (other != null)\n                   && Equals(other);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/DefaultExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // DefaultExpression fingerprint class\n    // Expression of form default(T)\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class DefaultExpressionFingerprint : ExpressionFingerprint\n    {\n        public DefaultExpressionFingerprint(ExpressionType nodeType, Type type)\n            : base(nodeType, type)\n        {\n            // There are no properties on DefaultExpression that are worth including in\n            // the fingerprint.\n        }\n\n        public override bool Equals(object obj)\n        {\n            DefaultExpressionFingerprint other = obj as DefaultExpressionFingerprint;\n            return (other != null)\n                   && Equals(other);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/ExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // Serves as the base class for all expression fingerprints. Provides a default implementation\n    // of GetHashCode().\n\n    [ExcludeFromCodeCoverage]\n    internal abstract class ExpressionFingerprint\n    {\n        protected ExpressionFingerprint(ExpressionType nodeType, Type type)\n        {\n            NodeType = nodeType;\n            Type = type;\n        }\n\n        // the type of expression node, e.g. OP_ADD, MEMBER_ACCESS, etc.\n        public ExpressionType NodeType { get; }\n\n        // the CLR type resulting from this expression, e.g. int, string, etc.\n        public Type Type { get; }\n\n        internal virtual void AddToHashCodeCombiner(HashCodeCombiner combiner)\n        {\n            combiner.AddInt32((int)NodeType);\n            combiner.AddObject(Type);\n        }\n\n        protected bool Equals(ExpressionFingerprint other)\n        {\n            return (other != null)\n                   && (NodeType == other.NodeType)\n                   && Type == other.Type;\n        }\n\n        public override bool Equals(object obj)\n        {\n            return Equals(obj as ExpressionFingerprint);\n        }\n\n        public override int GetHashCode()\n        {\n            HashCodeCombiner combiner = new HashCodeCombiner();\n            AddToHashCodeCombiner(combiner);\n            return combiner.CombinedHash;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/ExpressionFingerprintChain.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\n// ReSharper disable All\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // Expression fingerprint chain class\n    // Contains information used for generalizing, comparing, and recreating Expression instances\n    //\n    // Since Expression objects are immutable and are recreated for every invocation of an expression\n    // helper method, they can't be compared directly. Fingerprinting Expression objects allows\n    // information about them to be abstracted away, and the fingerprints can be directly compared.\n    // Consider the process of fingerprinting that all values (parameters, constants, etc.) are hoisted\n    // and replaced with dummies. What remains can be decomposed into a sequence of operations on specific\n    // types and specific inputs.\n    //\n    // Some sample fingerprints chains:\n    //\n    // 2 + 4 -> OP_ADD, CONST:int, NULL, CONST:int\n    // 2 + 8 -> OP_ADD, CONST:int, NULL, CONST:int\n    // 2.0 + 4.0 -> OP_ADD, CONST:double, NULL, CONST:double\n    //\n    // 2 + 4 and 2 + 8 have the same fingerprint, but 2.0 + 4.0 has a different fingerprint since its\n    // underlying types differ. Note that this looks a bit like prefix notation and is a side effect\n    // of how the ExpressionVisitor class recurses into expressions. (Occasionally there will be a NULL\n    // in the fingerprint chain, which depending on context can denote a static member, a null Conversion\n    // in a BinaryExpression, and so forth.)\n    //\n    // \"Hello \" + \"world\" -> OP_ADD, CONST:string, NULL, CONST:string\n    // \"Hello \" + {model} -> OP_ADD, CONST:string, NULL, PARAM_0:string\n    //\n    // These string concatenations have different fingerprints since the inputs are provided differently:\n    // one is a constant, the other is a parameter.\n    //\n    // ({model} ?? \"sample\").Length -> MEMBER_ACCESS(String.Length), OP_COALESCE, PARAM_0:string, NULL, CONST:string\n    // ({model} ?? \"other sample\").Length -> MEMBER_ACCESS(String.Length), OP_COALESCE, PARAM_0:string, NULL, CONST:string\n    //\n    // These expressions have the same fingerprint since all constants of the same underlying type are\n    // treated equally.\n    //\n    // It's also important that the fingerprints don't reference the actual Expression objects that were\n    // used to generate them, as the fingerprints will be cached, and caching a fingerprint that references\n    // an Expression will root the Expression (and any objects it references).\n\n    [ExcludeFromCodeCoverage]\n    internal sealed class ExpressionFingerprintChain : IEquatable<ExpressionFingerprintChain>\n    {\n        public readonly List<ExpressionFingerprint> Elements = new List<ExpressionFingerprint>();\n\n        public bool Equals(ExpressionFingerprintChain other)\n        {\n            // Two chains are considered equal if two elements appearing in the same index in\n            // each chain are equal (value equality, not referential equality).\n\n            if (Elements.Count != other?.Elements.Count)\n            {\n                return false;\n            }\n\n            for (int i = 0; i < Elements.Count; i++)\n            {\n                if (!Equals(Elements[i], other.Elements[i]))\n                {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        public override bool Equals(object obj)\n        {\n            return Equals(obj as ExpressionFingerprintChain);\n        }\n\n        public override int GetHashCode()\n        {\n            HashCodeCombiner combiner = new HashCodeCombiner();\n            Elements.ForEach(combiner.AddFingerprint);\n            return combiner.CombinedHash;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/FingerprintingExpressionVisitor.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // This is a visitor which produces a fingerprint of an expression. It doesn't\n    // rewrite the expression in a form which can be compiled and cached.\n\n    [ExcludeFromCodeCoverage]\n    internal sealed class FingerprintingExpressionVisitor : ExpressionVisitor\n    {\n        private readonly List<object> _seenConstants = new List<object>();\n        private readonly List<ParameterExpression> _seenParameters = new List<ParameterExpression>();\n        private readonly ExpressionFingerprintChain _currentChain = new ExpressionFingerprintChain();\n        private bool _gaveUp;\n\n        private FingerprintingExpressionVisitor()\n        {\n        }\n\n        private T GiveUp<T>(T node)\n        {\n            // We don't understand this node, so just quit.\n\n            _gaveUp = true;\n            return node;\n        }\n\n        // Returns the fingerprint chain + captured constants list for this expression, or null\n        // if the expression couldn't be fingerprinted.\n        public static ExpressionFingerprintChain GetFingerprintChain(Expression expr, out List<object> capturedConstants)\n        {\n            FingerprintingExpressionVisitor visitor = new FingerprintingExpressionVisitor();\n            visitor.Visit(expr);\n\n            if (visitor._gaveUp)\n            {\n                capturedConstants = null;\n                return null;\n            }\n            else\n            {\n                capturedConstants = visitor._seenConstants;\n                return visitor._currentChain;\n            }\n        }\n\n        public override Expression Visit(Expression node)\n        {\n            if (node == null)\n            {\n                _currentChain.Elements.Add(null);\n                return null;\n            }\n            else\n            {\n                return base.Visit(node);\n            }\n        }\n\n        protected override Expression VisitBinary(BinaryExpression node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n            _currentChain.Elements.Add(new BinaryExpressionFingerprint(node.NodeType, node.Type, node.Method));\n            return base.VisitBinary(node);\n        }\n\n        protected override Expression VisitBlock(BlockExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override CatchBlock VisitCatchBlock(CatchBlock node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitConditional(ConditionalExpression node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n            _currentChain.Elements.Add(new ConditionalExpressionFingerprint(node.NodeType, node.Type));\n            return base.VisitConditional(node);\n        }\n\n        protected override Expression VisitConstant(ConstantExpression node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n\n            _seenConstants.Add(node.Value);\n            _currentChain.Elements.Add(new ConstantExpressionFingerprint(node.NodeType, node.Type));\n            return base.VisitConstant(node);\n        }\n\n        protected override Expression VisitDebugInfo(DebugInfoExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitDefault(DefaultExpression node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n            _currentChain.Elements.Add(new DefaultExpressionFingerprint(node.NodeType, node.Type));\n            return base.VisitDefault(node);\n        }\n\n#if !NETSTANDARD1_3\n        protected override Expression VisitDynamic(DynamicExpression node)\n        {\n            return GiveUp(node);\n        }\n#endif\n\n        protected override ElementInit VisitElementInit(ElementInit node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitExtension(Expression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitGoto(GotoExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitIndex(IndexExpression node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n            _currentChain.Elements.Add(new IndexExpressionFingerprint(node.NodeType, node.Type, node.Indexer));\n            return base.VisitIndex(node);\n        }\n\n        protected override Expression VisitInvocation(InvocationExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitLabel(LabelExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override LabelTarget VisitLabelTarget(LabelTarget node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitLambda<T>(Expression<T> node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n            _currentChain.Elements.Add(new LambdaExpressionFingerprint(node.NodeType, node.Type));\n            return base.VisitLambda(node);\n        }\n\n        protected override Expression VisitListInit(ListInitExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitLoop(LoopExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitMember(MemberExpression node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n            _currentChain.Elements.Add(new MemberExpressionFingerprint(node.NodeType, node.Type, node.Member));\n            return base.VisitMember(node);\n        }\n\n        protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override MemberBinding VisitMemberBinding(MemberBinding node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitMemberInit(MemberInitExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override MemberListBinding VisitMemberListBinding(MemberListBinding node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitMethodCall(MethodCallExpression node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n            _currentChain.Elements.Add(new MethodCallExpressionFingerprint(node.NodeType, node.Type, node.Method));\n            return base.VisitMethodCall(node);\n        }\n\n        protected override Expression VisitNew(NewExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitNewArray(NewArrayExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitParameter(ParameterExpression node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n\n            int parameterIndex = _seenParameters.IndexOf(node);\n            if (parameterIndex < 0)\n            {\n                // first time seeing this parameter\n                parameterIndex = _seenParameters.Count;\n                _seenParameters.Add(node);\n            }\n\n            _currentChain.Elements.Add(new ParameterExpressionFingerprint(node.NodeType, node.Type, parameterIndex));\n            return base.VisitParameter(node);\n        }\n\n        protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitSwitch(SwitchExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override SwitchCase VisitSwitchCase(SwitchCase node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitTry(TryExpression node)\n        {\n            return GiveUp(node);\n        }\n\n        protected override Expression VisitTypeBinary(TypeBinaryExpression node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n            _currentChain.Elements.Add(new TypeBinaryExpressionFingerprint(node.NodeType, node.Type, node.TypeOperand));\n            return base.VisitTypeBinary(node);\n        }\n\n        protected override Expression VisitUnary(UnaryExpression node)\n        {\n            if (_gaveUp)\n            {\n                return node;\n            }\n            _currentChain.Elements.Add(new UnaryExpressionFingerprint(node.NodeType, node.Type, node.Method));\n            return base.VisitUnary(node);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/HashCodeCombiner.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System.Collections;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // based on System.Web.Util.HashCodeCombiner\n    [ExcludeFromCodeCoverage]\n    internal sealed class HashCodeCombiner\n    {\n        private long _combinedHash64 = 0x1505L;\n\n        public int CombinedHash => _combinedHash64.GetHashCode();\n\n        public void AddFingerprint(ExpressionFingerprint fingerprint)\n        {\n            if (fingerprint != null)\n            {\n                fingerprint.AddToHashCodeCombiner(this);\n            }\n            else\n            {\n                AddInt32(0);\n            }\n        }\n\n        public void AddEnumerable(IEnumerable e)\n        {\n            if (e == null)\n            {\n                AddInt32(0);\n            }\n            else\n            {\n                int count = 0;\n                foreach (object o in e)\n                {\n                    AddObject(o);\n                    count++;\n                }\n                AddInt32(count);\n            }\n        }\n\n        public void AddInt32(int i)\n        {\n            _combinedHash64 = ((_combinedHash64 << 5) + _combinedHash64) ^ i;\n        }\n\n        public void AddObject(object o)\n        {\n            int hashCode = o?.GetHashCode() ?? 0;\n            AddInt32(hashCode);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/Hoisted.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System.Collections.Generic;\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    internal delegate TValue Hoisted<in TModel, out TValue>(TModel model, List<object> capturedConstants);\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/HoistingExpressionVisitor.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\n// ReSharper disable All\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // This is a visitor which rewrites constant expressions as parameter lookups. It's meant\n    // to produce an expression which can be cached safely.\n\n    [ExcludeFromCodeCoverage]\n    internal sealed class HoistingExpressionVisitor<TIn, TOut> : ExpressionVisitor\n    {\n        private static readonly ParameterExpression _hoistedConstantsParamExpr = Expression.Parameter(typeof(List<object>), \"hoistedConstants\");\n        private int _numConstantsProcessed;\n\n        // factory will create instance\n        private HoistingExpressionVisitor()\n        {\n        }\n\n        public static Expression<Hoisted<TIn, TOut>> Hoist(Expression<Func<TIn, TOut>> expr)\n        {\n            // rewrite Expression<Func<TIn, TOut>> as Expression<Hoisted<TIn, TOut>>\n\n            var visitor = new HoistingExpressionVisitor<TIn, TOut>();\n            var rewrittenBodyExpr = visitor.Visit(expr.Body);\n            var rewrittenLambdaExpr = Expression.Lambda<Hoisted<TIn, TOut>>(rewrittenBodyExpr, expr.Parameters[0], _hoistedConstantsParamExpr);\n            return rewrittenLambdaExpr;\n        }\n\n        protected override Expression VisitConstant(ConstantExpression node)\n        {\n            // rewrite the constant expression as (TConst)hoistedConstants[i];\n            // coverity[missing_super_call]\n            return Expression.Convert(Expression.Property(_hoistedConstantsParamExpr, \"Item\", Expression.Constant(_numConstantsProcessed++)), node.Type);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/IndexExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // IndexExpression fingerprint class\n    // Represents certain forms of array access or indexer property access\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class IndexExpressionFingerprint : ExpressionFingerprint\n    {\n        public IndexExpressionFingerprint(ExpressionType nodeType, Type type, PropertyInfo indexer)\n            : base(nodeType, type)\n        {\n            // Other properties on IndexExpression (like the argument count) are simply derived\n            // from Type and Indexer, so they're not necessary for inclusion in the fingerprint.\n\n            Indexer = indexer;\n        }\n\n        // http://msdn.microsoft.com/en-us/library/system.linq.expressions.indexexpression.indexer.aspx\n        public PropertyInfo Indexer { get; }\n\n        public override bool Equals(object obj)\n        {\n            IndexExpressionFingerprint other = obj as IndexExpressionFingerprint;\n            return (other != null)\n                   && Equals(Indexer, other.Indexer)\n                   && Equals(other);\n        }\n\n        internal override void AddToHashCodeCombiner(HashCodeCombiner combiner)\n        {\n            combiner.AddObject(Indexer);\n            base.AddToHashCodeCombiner(combiner);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/LambdaExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // LambdaExpression fingerprint class\n    // Represents a lambda expression (root element in Expression<T>)\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class LambdaExpressionFingerprint : ExpressionFingerprint\n    {\n        public LambdaExpressionFingerprint(ExpressionType nodeType, Type type)\n            : base(nodeType, type)\n        {\n            // There are no properties on LambdaExpression that are worth including in\n            // the fingerprint.\n        }\n\n        public override bool Equals(object obj)\n        {\n            LambdaExpressionFingerprint other = obj as LambdaExpressionFingerprint;\n            return (other != null)\n                   && Equals(other);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/MemberExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // MemberExpression fingerprint class\n    // Expression of form xxx.FieldOrProperty\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class MemberExpressionFingerprint : ExpressionFingerprint\n    {\n        public MemberExpressionFingerprint(ExpressionType nodeType, Type type, MemberInfo member)\n            : base(nodeType, type)\n        {\n            Member = member;\n        }\n\n        // http://msdn.microsoft.com/en-us/library/system.linq.expressions.memberexpression.member.aspx\n        public MemberInfo Member { get; }\n\n        public override bool Equals(object obj)\n        {\n            MemberExpressionFingerprint other = obj as MemberExpressionFingerprint;\n            return (other != null)\n                   && Equals(Member, other.Member)\n                   && Equals(other);\n        }\n\n        internal override void AddToHashCodeCombiner(HashCodeCombiner combiner)\n        {\n            combiner.AddObject(Member);\n            base.AddToHashCodeCombiner(combiner);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/MethodCallExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // MethodCallExpression fingerprint class\n    // Expression of form xxx.Foo(...), xxx[...] (get_Item()), etc.\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class MethodCallExpressionFingerprint : ExpressionFingerprint\n    {\n        public MethodCallExpressionFingerprint(ExpressionType nodeType, Type type, MethodInfo method)\n            : base(nodeType, type)\n        {\n            // Other properties on MethodCallExpression (like the argument count) are simply derived\n            // from Type and Indexer, so they're not necessary for inclusion in the fingerprint.\n\n            Method = method;\n        }\n\n        // http://msdn.microsoft.com/en-us/library/system.linq.expressions.methodcallexpression.method.aspx\n        public MethodInfo Method { get; }\n\n        public override bool Equals(object obj)\n        {\n            MethodCallExpressionFingerprint other = obj as MethodCallExpressionFingerprint;\n            return (other != null)\n                   && Equals(Method, other.Method)\n                   && Equals(other);\n        }\n\n        internal override void AddToHashCodeCombiner(HashCodeCombiner combiner)\n        {\n            combiner.AddObject(Method);\n            base.AddToHashCodeCombiner(combiner);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/ParameterExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // ParameterExpression fingerprint class\n    // Can represent the model parameter or an inner parameter in an open lambda expression\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class ParameterExpressionFingerprint : ExpressionFingerprint\n    {\n        public ParameterExpressionFingerprint(ExpressionType nodeType, Type type, int parameterIndex)\n            : base(nodeType, type)\n        {\n            ParameterIndex = parameterIndex;\n        }\n\n        // Parameter position within the overall expression, used to maintain alpha equivalence.\n        public int ParameterIndex { get; }\n\n        public override bool Equals(object obj)\n        {\n            ParameterExpressionFingerprint other = obj as ParameterExpressionFingerprint;\n            return (other != null)\n                   && (ParameterIndex == other.ParameterIndex)\n                   && Equals(other);\n        }\n\n        internal override void AddToHashCodeCombiner(HashCodeCombiner combiner)\n        {\n            combiner.AddInt32(ParameterIndex);\n            base.AddToHashCodeCombiner(combiner);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/TypeBinaryExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // TypeBinary fingerprint class\n    // Expression of form \"obj is T\"\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class TypeBinaryExpressionFingerprint : ExpressionFingerprint\n    {\n        public TypeBinaryExpressionFingerprint(ExpressionType nodeType, Type type, Type typeOperand)\n            : base(nodeType, type)\n        {\n            TypeOperand = typeOperand;\n        }\n\n        // http://msdn.microsoft.com/en-us/library/system.linq.expressions.typebinaryexpression.typeoperand.aspx\n        public Type TypeOperand { get; }\n\n        public override bool Equals(object obj)\n        {\n            TypeBinaryExpressionFingerprint other = obj as TypeBinaryExpressionFingerprint;\n            return (other != null)\n                   && TypeOperand == other.TypeOperand\n                   && Equals(other);\n        }\n\n        internal override void AddToHashCodeCombiner(HashCodeCombiner combiner)\n        {\n            combiner.AddObject(TypeOperand);\n            base.AddToHashCodeCombiner(combiner);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ExpressionUtil/UnaryExpressionFingerprint.cs",
    "content": "﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\n\n#pragma warning disable 659 // overrides AddToHashCodeCombiner instead\n\nnamespace Hangfire.Common.ExpressionUtil\n{\n    // UnaryExpression fingerprint class\n    // The most common appearance of a UnaryExpression is a cast or other conversion operator\n\n    [SuppressMessage(\"Microsoft.Usage\", \"CA2218:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [SuppressMessage(\"SonarLint\", \"S1206:OverrideGetHashCodeOnOverridingEquals\", Justification = \"Overrides AddToHashCodeCombiner() instead.\")]\n    [ExcludeFromCodeCoverage]\n    internal sealed class UnaryExpressionFingerprint : ExpressionFingerprint\n    {\n        public UnaryExpressionFingerprint(ExpressionType nodeType, Type type, MethodInfo method)\n            : base(nodeType, type)\n        {\n            // Other properties on UnaryExpression (like IsLifted / IsLiftedToNull) are simply derived\n            // from Type and NodeType, so they're not necessary for inclusion in the fingerprint.\n\n            Method = method;\n        }\n\n        // http://msdn.microsoft.com/en-us/library/system.linq.expressions.unaryexpression.method.aspx\n        public MethodInfo Method { get; }\n\n        public override bool Equals(object obj)\n        {\n            UnaryExpressionFingerprint other = obj as UnaryExpressionFingerprint;\n            return (other != null)\n                   && Equals(Method, other.Method)\n                   && Equals(other);\n        }\n\n        internal override void AddToHashCodeCombiner(HashCodeCombiner combiner)\n        {\n            combiner.AddObject(Method);\n            base.AddToHashCodeCombiner(combiner);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/IJobFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Defines members that specify the order of filters and \n    /// whether multiple filters are allowed.\n    /// </summary>\n    public interface IJobFilter\n    {\n        /// <summary>\n        /// When implemented in a class, gets or sets a value \n        /// that indicates whether multiple filters are allowed.\n        /// </summary>\n        bool AllowMultiple { get; }\n\n        /// <summary>\n        /// When implemented in a class, gets the filter order.\n        /// </summary>\n        int Order { get; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Common/IJobFilterProvider.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Provides an interface for finding filters.\n    /// </summary>\n    public interface IJobFilterProvider\n    {\n        /// <summary>\n        /// Returns an enumerator that contains all the <see cref=\"IJobFilterProvider\"/>.\n        /// </summary>\n        ///  \n        /// <returns>\n        /// The enumerator that contains all the <see cref=\"IJobFilterProvider\"/>.\n        /// </returns>\n        IEnumerable<JobFilter> GetFilters(Job job);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/Job.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing Hangfire.Annotations;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Hangfire.States;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Represents an action that can be marshalled to another process to\n    /// be performed.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// <para>The ability to serialize an action is the cornerstone of \n    /// marshalling it outside of a current process boundaries. We are leaving \n    /// behind all the tricky features, e.g. serializing lambdas with their\n    /// closures or so, and considering a simple method call information as \n    /// a such an action, and using reflection to perform it.</para>\n    /// \n    /// <para>Reflection-based method invocation requires an instance of\n    /// the <see cref=\"MethodInfo\"/> class, the arguments and an instance of \n    /// the type on which to invoke the method (unless it is static). Since the\n    /// same <see cref=\"MethodInfo\"/> instance can be shared across multiple \n    /// types (especially when they are defined in interfaces), we also allow \n    /// to specify a <see cref=\"Type\"/> that contains the defined method \n    /// explicitly for better flexibility.</para>\n    /// \n    /// <para>Marshalling imposes restrictions on a method that should be \n    /// performed:</para>\n    /// \n    /// <list type=\"bullet\">\n    ///     <item>Method should be public.</item>\n    ///     <item>Method should not contain <see langword=\"out\"/> and <see langword=\"ref\"/> parameters.</item>\n    ///     <item>Method should not contain open generic parameters.</item>\n    /// </list>\n    /// </remarks>\n    /// \n    /// <example>\n    /// <para>The following example demonstrates the creation of a <see cref=\"Job\"/>\n    /// type instances using expression trees. This is the recommended way of\n    /// creating jobs.</para>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\Job.cs\" region=\"Supported Methods\" />\n    /// \n    /// <para>The next example demonstrates unsupported methods. Any attempt\n    /// to create a job based on these methods fails with \n    /// <see cref=\"NotSupportedException\"/>.</para>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\Job.cs\" region=\"Unsupported Methods\" />\n    /// </example>\n    /// \n    /// <seealso cref=\"IBackgroundJobClient\"/>\n    /// <seealso cref=\"Server.IBackgroundJobPerformer\"/>\n    /// \n    /// <threadsafety static=\"true\" instance=\"false\" />\n    public partial class Job\n    {\n        private static readonly object[] EmptyObjectArray = [];\n        private static readonly ConcurrentDictionary<MethodInfo, AsyncStateMachineAttribute>\n            AsyncStateMachineAttributeCache = new();\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"Job\"/> class with the\n        /// metadata of a method with no arguments.\n        /// </summary>\n        /// \n        /// <param name=\"method\">Method that should be invoked.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"method\"/> argument is null.</exception>\n        /// <exception cref=\"NotSupportedException\"><paramref name=\"method\"/> is not supported.</exception>\n        public Job([NotNull] MethodInfo method)\n            : this(method, EmptyObjectArray)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"Job\"/> class with the\n        /// metadata of a method and the given list of arguments.\n        /// </summary>\n        /// \n        /// <param name=\"method\">Method that should be invoked.</param>\n        /// <param name=\"args\">Arguments that will be passed to a method invocation.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"method\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentException\">Parameter/argument count mismatch.</exception>\n        /// <exception cref=\"NotSupportedException\"><paramref name=\"method\"/> is not supported.</exception>\n        public Job([NotNull] MethodInfo method, [NotNull] params object[] args)\n            // ReSharper disable once AssignNullToNotNullAttribute\n            : this(method.DeclaringType, method, args)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"Job\"/> class with the\n        /// type, metadata of a method with no arguments.\n        /// </summary>\n        /// \n        /// <param name=\"type\">Type that contains the given method.</param>\n        /// <param name=\"method\">Method that should be invoked.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"type\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"method\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentException\">\n        /// <paramref name=\"type\"/> does not contain the given <paramref name=\"method\"/>.\n        /// </exception>\n        /// <exception cref=\"ArgumentException\">Parameter/argument count mismatch.</exception>\n        /// <exception cref=\"NotSupportedException\"><paramref name=\"method\"/> is not supported.</exception>\n        public Job([NotNull] Type type, [NotNull] MethodInfo method)\n            : this(type, method, EmptyObjectArray)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"Job\"/> class with the \n        /// type, metadata of a method, and the given list of arguments.\n        /// </summary>\n        /// \n        /// <param name=\"type\">Type that contains the given method.</param>\n        /// <param name=\"method\">Method that should be invoked.</param>\n        /// <param name=\"args\">Arguments that should be passed during the method call.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"type\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"method\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"args\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentException\">\n        /// <paramref name=\"type\"/> does not contain the given <paramref name=\"method\"/>.\n        /// </exception>\n        /// <exception cref=\"ArgumentException\">Parameter/argument count mismatch.</exception>\n        /// <exception cref=\"NotSupportedException\"><paramref name=\"method\"/> is not supported.</exception>\n        public Job([NotNull] Type type, [NotNull] MethodInfo method, [NotNull] params object[] args)\n            : this(type, method, args, null)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"Job\"/> class with the type, metadata of a method,\n        /// and the given list of arguments, specified in a read-only list.\n        /// </summary>\n        /// \n        /// <param name=\"type\">Type that contains the given method.</param>\n        /// <param name=\"method\">Method that should be invoked.</param>\n        /// <param name=\"args\">Arguments that should be passed during the method call.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"type\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"method\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"args\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentException\">\n        /// <paramref name=\"type\"/> does not contain the given <paramref name=\"method\"/>.\n        /// </exception>\n        /// <exception cref=\"ArgumentException\">Parameter/argument count mismatch.</exception>\n        /// <exception cref=\"NotSupportedException\"><paramref name=\"method\"/> is not supported.</exception>\n        public Job([NotNull] Type type, [NotNull] MethodInfo method, [NotNull] IReadOnlyList<object> args)\n            : this(type, method, args, null)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"Job\"/> class with the type, metadata of a method,\n        /// the given list of arguments, and the default target queue for a job.\n        /// </summary>\n        ///\n        /// <param name=\"type\">Type that contains the given method.</param>\n        /// <param name=\"method\">Method that should be invoked.</param>\n        /// <param name=\"args\">Arguments that should be passed during the method call.</param>\n        /// <param name=\"queue\">Default target queue for the job.</param>\n        ///\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"type\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"method\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"args\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentException\">\n        /// <paramref name=\"type\"/> does not contain the given <paramref name=\"method\"/>.\n        /// </exception>\n        /// <exception cref=\"ArgumentException\">Parameter/argument count mismatch.</exception>\n        /// <exception cref=\"NotSupportedException\"><paramref name=\"method\"/> is not supported.</exception>\n        public Job([NotNull] Type type, [NotNull] MethodInfo method, [NotNull] IReadOnlyList<object> args, [CanBeNull] string queue)\n        {\n            if (type == null) throw new ArgumentNullException(nameof(type));\n            if (method == null) throw new ArgumentNullException(nameof(method));\n            if (args == null) throw new ArgumentNullException(nameof(args));\n            \n            Validate(type, nameof(type), method, nameof(method), args.Count, nameof(args));\n\n            if (queue != null)\n            {\n                EnqueuedState.ValidateQueueName(nameof(queue), queue);\n            }\n\n            Type = type;\n            Method = method;\n            Args = args;\n            Queue = queue;\n        }\n\n        /// <summary>\n        /// Gets the metadata of a type that contains a method that should be \n        /// invoked during the performance.\n        /// </summary>\n        [NotNull]\n        public Type Type { get; }\n\n        /// <summary>\n        /// Gets the metadata of a method that should be invoked during the \n        /// performance.\n        /// </summary>\n        [NotNull]\n        public MethodInfo Method { get; }\n\n        /// <summary>\n        /// Gets a read-only collection of arguments that Should be passed to a \n        /// method invocation during the performance.\n        /// </summary>\n        [NotNull]\n        public IReadOnlyList<object> Args { get; }\n\n        /// <summary>\n        /// Gets a default target queue for a job to which it will be enqueued unless\n        /// overriden by a job filter.\n        /// </summary>\n        [CanBeNull]\n        public string Queue { get; }\n\n        public override string ToString()\n        {\n            return ToString(includeQueue: true);\n        }\n\n        public string ToString(bool includeQueue)\n        {\n            var sb = new StringBuilder()\n                .Append(Type.ToGenericTypeString())\n                .Append('.')\n                .Append(Method.Name);\n\n            if (includeQueue && Queue != null)\n            {\n                sb.Append(\" (\").Append(Queue).Append(')');\n            }\n\n            return sb.ToString();\n        }\n\n        internal IEnumerable<JobFilterAttribute> GetTypeFilterAttributes(bool useCache)\n        {\n            return useCache\n                ? ReflectedAttributeCache.GetTypeFilterAttributes(Type)\n                : GetFilterAttributes(Type.GetTypeInfo());\n        }\n\n        internal IEnumerable<JobFilterAttribute> GetMethodFilterAttributes(bool useCache)\n        {\n            return useCache\n                ? ReflectedAttributeCache.GetMethodFilterAttributes(Method)\n                : GetFilterAttributes(Method);\n        }\n\n        private static IEnumerable<JobFilterAttribute> GetFilterAttributes(MemberInfo memberInfo)\n        {\n            return memberInfo.GetCustomAttributes<JobFilterAttribute>(inherit: true);\n        }\n\n        /// <summary>\n        /// Gets a new instance of the <see cref=\"Job\"/> class based on the\n        /// given expression tree of a method call.\n        /// </summary>\n        /// \n        /// <param name=\"methodCall\">Expression tree of a method call.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"methodCall\"/> is null.</exception>\n        /// <exception cref=\"ArgumentException\">\n        /// <paramref name=\"methodCall\"/> expression body is not of type \n        /// <see cref=\"MethodCallExpression\"/>.</exception>\n        /// <exception cref=\"NotSupportedException\"><paramref name=\"methodCall\"/> \n        /// expression contains a method that is not supported.</exception>\n        /// <exception cref=\"InvalidOperationException\"><paramref name=\"methodCall\"/>\n        /// instance object of a given expression points to <see langword=\"null\"/>.\n        /// </exception>\n        /// \n        /// <remarks>\n        /// <para>The <see cref=\"Job.Type\"/> property of a returning job will \n        /// point to the type of a given instance object when it is specified, \n        /// or to the declaring type otherwise. All the arguments are evaluated \n        /// using the expression compiler that uses caching where possible to \n        /// decrease the performance penalty.</para>\n        /// \n        /// <note>Instance object (e.g. <c>() => instance.Method()</c>) is \n        /// <b>only used to obtain the type</b> for a job. It is not\n        /// serialized and not passed across the process boundaries.</note>\n        /// </remarks>\n        public static Job FromExpression([NotNull, InstantHandle] Expression<Action> methodCall)\n        {\n            return FromExpression(methodCall, null);\n        }\n\n        public static Job FromExpression([NotNull, InstantHandle] Expression<Action> methodCall, [CanBeNull] string queue)\n        {\n            return FromExpression(methodCall, null, queue);\n        }\n\n        /// <summary>\n        /// Gets a new instance of the <see cref=\"Job\"/> class based on the\n        /// given expression tree of a method call.\n        /// </summary>\n        /// \n        /// <param name=\"methodCall\">Expression tree of a method call.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"methodCall\"/> is null.</exception>\n        /// <exception cref=\"ArgumentException\">\n        /// <paramref name=\"methodCall\"/> expression body is not of type \n        /// <see cref=\"MethodCallExpression\"/>.</exception>\n        /// <exception cref=\"NotSupportedException\"><paramref name=\"methodCall\"/> \n        /// expression contains a method that is not supported.</exception>\n        /// <exception cref=\"InvalidOperationException\"><paramref name=\"methodCall\"/>\n        /// instance object of a given expression points to <see langword=\"null\"/>.\n        /// </exception>\n        /// \n        /// <remarks>\n        /// <para>The <see cref=\"Job.Type\"/> property of a returning job will \n        /// point to the type of a given instance object when it is specified, \n        /// or to the declaring type otherwise. All the arguments are evaluated \n        /// using the expression compiler that uses caching where possible to \n        /// decrease the performance penalty.</para>\n        /// \n        /// <note>Instance object (e.g. <c>() => instance.Method()</c>) is \n        /// <b>only used to obtain the type</b> for a job. It is not\n        /// serialized and not passed across the process boundaries.</note>\n        /// </remarks>\n        public static Job FromExpression([NotNull, InstantHandle] Expression<Func<Task>> methodCall)\n        {\n            return FromExpression(methodCall, null);\n        }\n\n        public static Job FromExpression([NotNull, InstantHandle] Expression<Func<Task>> methodCall, [CanBeNull] string queue)\n        {\n            return FromExpression(methodCall, null, queue);\n        }\n\n        /// <summary>\n        /// Gets a new instance of the <see cref=\"Job\"/> class based on the\n        /// given expression tree of an instance method call with explicit\n        /// type specification.\n        /// </summary>\n        /// <typeparam name=\"TType\">Explicit type that should be used on method call.</typeparam>\n        /// <param name=\"methodCall\">Expression tree of a method call on <typeparamref name=\"TType\"/>.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"methodCall\"/> is null.</exception>\n        /// <exception cref=\"ArgumentException\">\n        /// <paramref name=\"methodCall\"/> expression body is not of type \n        /// <see cref=\"MethodCallExpression\"/>.</exception>\n        /// <exception cref=\"NotSupportedException\"><paramref name=\"methodCall\"/> \n        /// expression contains a method that is not supported.</exception>\n        /// \n        /// <remarks>\n        /// <para>All the arguments are evaluated using the expression compiler\n        /// that uses caching where possible to decrease the performance \n        /// penalty.</para>\n        /// </remarks>\n        public static Job FromExpression<TType>([NotNull, InstantHandle] Expression<Action<TType>> methodCall)\n        {\n            return FromExpression(methodCall, null);\n        }\n\n        public static Job FromExpression<TType>([NotNull, InstantHandle] Expression<Action<TType>> methodCall, [CanBeNull] string queue)\n        {\n            return FromExpression(methodCall, typeof(TType), queue);\n        }\n\n        /// <summary>\n        /// Gets a new instance of the <see cref=\"Job\"/> class based on the\n        /// given expression tree of an instance method call with explicit\n        /// type specification.\n        /// </summary>\n        /// <typeparam name=\"TType\">Explicit type that should be used on method call.</typeparam>\n        /// <param name=\"methodCall\">Expression tree of a method call on <typeparamref name=\"TType\"/>.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"methodCall\"/> is null.</exception>\n        /// <exception cref=\"ArgumentException\">\n        /// <paramref name=\"methodCall\"/> expression body is not of type \n        /// <see cref=\"MethodCallExpression\"/>.</exception>\n        /// <exception cref=\"NotSupportedException\"><paramref name=\"methodCall\"/> \n        /// expression contains a method that is not supported.</exception>\n        /// \n        /// <remarks>\n        /// <para>All the arguments are evaluated using the expression compiler\n        /// that uses caching where possible to decrease the performance \n        /// penalty.</para>\n        /// </remarks>\n        public static Job FromExpression<TType>([NotNull, InstantHandle] Expression<Func<TType, Task>> methodCall)\n        {\n            return FromExpression(methodCall, null);\n        }\n\n        public static Job FromExpression<TType>([NotNull, InstantHandle] Expression<Func<TType, Task>> methodCall, [CanBeNull] string queue)\n        {\n            return FromExpression(methodCall, typeof(TType), queue);\n        }\n\n        private static Job FromExpression([NotNull] LambdaExpression methodCall, [CanBeNull] Type explicitType, [CanBeNull] string queue)\n        {\n            if (methodCall == null) throw new ArgumentNullException(nameof(methodCall));\n\n            var callExpression = methodCall.Body as MethodCallExpression;\n            if (callExpression == null)\n            {\n                throw new ArgumentException(\"Expression body should be of type `MethodCallExpression`\", nameof(methodCall));\n            }\n\n            var type = explicitType ?? callExpression.Method.DeclaringType;\n            var method = callExpression.Method;\n\n            if (explicitType == null && callExpression.Object != null)\n            {\n                // Creating a job that is based on a scope variable. We should infer its\n                // type and method based on its value, and not from the expression tree.\n\n                // TODO: BREAKING: Consider removing this special case entirely.\n                // People consider that the whole object is serialized, this is not true.\n\n                var objectValue = GetExpressionValue(callExpression.Object);\n                if (objectValue == null)\n                {\n                    throw new InvalidOperationException(\"Expression object should be not null.\");\n                }\n\n                // TODO: BREAKING: Consider using `callExpression.Object.Type` expression instead.\n                type = objectValue.GetType();\n\n                // If an expression tree is based on interface, we should use its own\n                // MethodInfo instance, based on the same method name and parameter types.\n                method = type.GetNonOpenMatchingMethod(\n                    callExpression.Method.Name,\n                    callExpression.Method.GetParameters().Select(static x => x.ParameterType).ToArray());\n            }\n\n            return new Job(\n                // ReSharper disable once AssignNullToNotNullAttribute\n                type,\n                method,\n                GetExpressionValues(callExpression.Arguments),\n                queue);\n        }\n\n        private static void Validate(\n            Type type, \n            [InvokerParameterName] string typeParameterName,\n            MethodInfo method, \n            // ReSharper disable once UnusedParameter.Local\n            [InvokerParameterName] string methodParameterName,\n            // ReSharper disable once UnusedParameter.Local\n            int argumentCount,\n            [InvokerParameterName] string argumentParameterName)\n        {\n            if (!method.IsPublic)\n            {\n                throw new NotSupportedException(\"Only public methods can be invoked in the background. Ensure your method has the `public` access modifier, and you aren't using explicit interface implementation.\");\n            }\n\n            if (method.ContainsGenericParameters)\n            {\n                throw new NotSupportedException(\"Job method can not contain unassigned generic type parameters.\");\n            }\n\n            if (method.DeclaringType == null)\n            {\n                throw new NotSupportedException(\"Global methods are not supported. Use class methods instead.\");\n            }\n\n            if (!method.DeclaringType.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))\n            {\n                throw new ArgumentException(\n                    $\"The type `{method.DeclaringType}` must be derived from the `{type}` type.\",\n                    typeParameterName);\n            }\n\n            if (method.ReturnType == typeof(void) &&\n                AsyncStateMachineAttributeCache.GetOrAdd(method, static m => m.GetCustomAttribute<AsyncStateMachineAttribute>()) != null)\n            {\n                throw new NotSupportedException(\"Async void methods are not supported. Use async Task instead.\");\n            }\n\n            var parameters = method.GetParameters();\n\n            if (parameters.Length != argumentCount)\n            {\n                throw new ArgumentException(\n                    \"Argument count must be equal to method parameter count.\",\n                    argumentParameterName);\n            }\n\n            foreach (var parameter in parameters)\n            {\n                // There is no guarantee that specified method will be invoked\n                // in the same process. Therefore, output parameters and parameters\n                // passed by reference are not supported.\n\n                if (parameter.IsOut)\n                {\n                    throw new NotSupportedException(\n                        \"Output parameters are not supported: there is no guarantee that specified method will be invoked inside the same process.\");\n                }\n\n                if (parameter.ParameterType.IsByRef)\n                {\n                    throw new NotSupportedException(\n                        \"Parameters, passed by reference, are not supported: there is no guarantee that specified method will be invoked inside the same process.\");\n                }\n\n                var parameterTypeInfo = parameter.ParameterType.GetTypeInfo();\n                \n                if (parameterTypeInfo.IsSubclassOf(typeof(Delegate)) || parameterTypeInfo.IsSubclassOf(typeof(Expression)))\n                {\n                    throw new NotSupportedException(\n                        \"Anonymous functions, delegates and lambda expressions aren't supported in job method parameters: it's very hard to serialize them and all their scope in general.\");\n                }\n            }\n        }\n\n        private static object[] GetExpressionValues(ReadOnlyCollection<Expression> expressions)\n        {\n            var result = expressions.Count > 0 ? new object[expressions.Count] : [];\n            var index = 0;\n\n            foreach (var expression in expressions)\n            {\n                result[index++] = GetExpressionValue(expression);\n            }\n\n            return result;\n        }\n\n        private static object GetExpressionValue(Expression expression)\n        {\n            var constantExpression = expression as ConstantExpression;\n\n            return constantExpression != null\n                ? constantExpression.Value\n                : CachedExpressionCompiler.Evaluate(expression);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/JobFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Represents a metadata class that contains a reference to the \n    /// implementation of one or more of the filter interfaces, the filter's \n    /// order, and the filter's scope.\n    /// </summary>\n    public class JobFilter\n    {\n        /// <summary>\n        /// Represents a constant that is used to specify the default ordering of filters.\n        /// </summary>\n        public const int DefaultOrder = -1;\n\n        /// <summary>\n        /// Initializes a new instance of the Filter class.\n        /// </summary>\n        /// <param name=\"instance\">Filter instance.</param>\n        /// <param name=\"scope\">Filter scope.</param>\n        /// <param name=\"order\">The run order.</param>\n        public JobFilter(object instance, JobFilterScope scope, int? order)\n        {\n            if (instance == null)\n            {\n                throw new ArgumentNullException(nameof(instance));\n            }\n\n            if (order == null && instance is IJobFilter jobFilter)\n            {\n                order = jobFilter.Order;\n            }\n\n            Instance = instance;\n            Order = order ?? DefaultOrder;\n            Scope = scope;\n        }\n\n        /// <summary>\n        /// Gets the instance of the filter.\n        /// </summary>\n        public object Instance { get; protected set; }\n\n        /// <summary>\n        /// Gets the order in which the filter is applied.\n        /// </summary>\n        public int Order { get; protected set; }\n\n        /// <summary>\n        /// Gets the scope ordering of the filter.\n        /// </summary>\n        public JobFilterScope Scope { get; protected set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Common/JobFilterAttribute.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Reflection;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Represents the base class for job filter attributes.\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method)]\n    public abstract class JobFilterAttribute : Attribute, IJobFilter\n    {\n        private static readonly ConcurrentDictionary<Type, bool> MultiuseAttributeCache = new ConcurrentDictionary<Type, bool>();\n        private int _order = JobFilter.DefaultOrder;\n\n        [JsonIgnore]\n        public bool AllowMultiple => AllowsMultiple(GetType());\n\n        [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]\n        [DefaultValue(JobFilter.DefaultOrder)]\n        public int Order\n        {\n            get { return _order; }\n            set\n            {\n                if (value < JobFilter.DefaultOrder)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), \"The Order value should be greater or equal to '-1'\");\n                }\n                _order = value;\n            }\n        }\n\n#if !NETSTANDARD1_3\n        [JsonIgnore]\n        public override object TypeId => base.TypeId;\n#endif\n\n        private static bool AllowsMultiple(Type attributeType)\n        {\n            return MultiuseAttributeCache.GetOrAdd(\n                attributeType,\n                static type => type.GetTypeInfo()\n                            .GetCustomAttributes<AttributeUsageAttribute>(inherit: true)\n                            .First()\n                            .AllowMultiple);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/JobFilterAttributeFilterProvider.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Defines a filter provider for filter attributes.\n    /// </summary>\n    public class JobFilterAttributeFilterProvider : IJobFilterProvider\n    {\n        private readonly bool _cacheAttributeInstances;\n\n        /// <summary>\n        /// Initializes a new instance of the  <see cref=\"JobFilterAttributeFilterProvider\"/>\n        /// class with the attribute instance caching enabled.\n        /// </summary>\n        public JobFilterAttributeFilterProvider()\n            : this(true)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"JobFilterAttributeFilterProvider\"/> \n        /// class and optionally caches attribute instances.\n        /// </summary>\n        /// <param name=\"cacheAttributeInstances\"></param>\n        public JobFilterAttributeFilterProvider(bool cacheAttributeInstances)\n        {\n            _cacheAttributeInstances = cacheAttributeInstances;\n        }\n\n        protected virtual IEnumerable<JobFilterAttribute> GetTypeAttributes(Job job)\n        {\n            return job.GetTypeFilterAttributes(_cacheAttributeInstances);\n        }\n\n        protected virtual IEnumerable<JobFilterAttribute> GetMethodAttributes(Job job)\n        {\n            return job.GetMethodFilterAttributes(_cacheAttributeInstances);\n        }\n\n        public virtual IEnumerable<JobFilter> GetFilters(Job job)\n        {\n            if (job == null) return Enumerable.Empty<JobFilter>();\n\n            var typeAttributes = GetTypeAttributes(job);\n            var methodAttributes = GetMethodAttributes(job);\n\n            List<JobFilter> combined = null;\n\n            foreach (var typeAttribute in typeAttributes)\n            {\n                (combined ??= []).Add(new JobFilter(typeAttribute, JobFilterScope.Type, null));\n            }\n            \n            foreach (var methodAttribute in methodAttributes)\n            {\n                (combined ??= []).Add(new JobFilter(methodAttribute, JobFilterScope.Method, null));\n            }\n\n            return combined ?? Enumerable.Empty<JobFilter>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/JobFilterCollection.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Client;\nusing Hangfire.Server;\nusing Hangfire.States;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Represents a class that contains the job filters.\n    /// </summary>\n    /// <remarks>\n    /// Job filters run for every create, perform and state change\n    /// of every job. All the instances in the filters collection\n    /// should be thread-safe.\n    /// \n    /// You can register a filter using the \n    /// <see cref=\"GlobalJobFilters.Filters\"/> registration endpoint.\n    /// </remarks>\n    public class JobFilterCollection : IJobFilterProvider, IEnumerable<JobFilter>\n    {\n        private readonly List<JobFilter> _filters = new List<JobFilter>();\n\n        /// <summary>\n        /// Gets the number of filters in the global job filter collection.\n        /// </summary>\n        public int Count => _filters.Count;\n\n        /// <summary>\n        /// Adds the specified filter to the global filter collection.\n        /// </summary>\n        /// <param name=\"filter\">The filter instance.</param>\n        public void Add(object filter)\n        {\n            AddInternal(filter, order: null);\n        }\n\n        /// <summary>\n        /// Adds the specified filter to the global filter collection \n        /// using the specified filter run order.\n        /// </summary>\n        /// <param name=\"filter\">The filter instance.</param>\n        /// <param name=\"order\">The run order.</param>\n        public void Add(object filter, int order)\n        {\n            AddInternal(filter, order);\n        }\n\n        private void AddInternal(object filter, int? order)\n        {\n            ValidateFilterInstance(filter);\n            _filters.Add(new JobFilter(filter, JobFilterScope.Global, order));\n        }\n\n        /// <summary>\n        /// Removes all filters from the global filter collection.\n        /// </summary>\n        public void Clear()\n        {\n            _filters.Clear();\n        }\n\n        /// <summary>\n        /// Determines whether a filter is in the global filter collection.\n        /// </summary>\n        /// <param name=\"filter\">The filter instance.</param>\n        /// <returns>True if the global filter collection contains the filter, otherwise false.</returns>\n        public bool Contains(object filter)\n        {\n            return _filters.Any(x => x.Instance == filter);\n        }\n\n        /// <summary>\n        /// Removes all filters that match the specified filter.\n        /// </summary>\n        /// <param name=\"filter\">The filter instance.</param>\n        public void Remove(object filter)\n        {\n            _filters.RemoveAll(x => x.Instance == filter);\n        }\n\n        /// <summary>\n        /// Remove all filters of the specified type with strict type matching.\n        /// </summary>\n        /// <typeparam name=\"T\">Type of filters to remove.</typeparam>\n        public void Remove<T>()\n        {\n            Remove(typeof(T));\n        }\n\n        /// <summary>\n        /// Remove all filters of the specified type with strict type matching.\n        /// </summary>\n        /// <param name=\"type\">Type of filters to remove.</param>\n        public void Remove(Type type)\n        {\n            _filters.RemoveAll(x => type == x.Instance.GetType());\n        }\n\n        public IEnumerator<JobFilter> GetEnumerator()\n        {\n            return _filters.GetEnumerator();\n        }\n\n        IEnumerable<JobFilter> IJobFilterProvider.GetFilters(Job job)\n        {\n            return _filters;\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        // ReSharper disable once UnusedParameter.Local\n        private static void ValidateFilterInstance(object instance)\n        {\n            if (instance != null &&\n                !(instance is IClientFilter \n                || instance is IServerFilter \n                || instance is IClientExceptionFilter \n                || instance is IServerExceptionFilter\n                || instance is IApplyStateFilter\n                || instance is IElectStateFilter))\n            {\n                throw new InvalidOperationException(\"Unsupported filter instance\");\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Common/JobFilterInfo.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Client;\nusing Hangfire.Server;\nusing Hangfire.States;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Encapsulates information about the available job filters.\n    /// </summary>\n    internal readonly struct JobFilterInfo\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"JobFilterInfo\"/> class using the specified filters collection.\n        /// </summary>\n        /// <param name=\"filters\">The filters collection.</param>\n        public JobFilterInfo(IEnumerable<JobFilter> filters)\n        {\n            var filtersList = filters as List<JobFilter> ?? filters.ToList();\n            ClientFilters = new FilterCollection<IClientFilter>(filtersList);\n            ServerFilters = new FilterCollection<IServerFilter>(filtersList);\n            ElectStateFilters = new FilterCollection<IElectStateFilter>(filtersList);\n            ApplyStateFilters = new FilterCollection<IApplyStateFilter>(filtersList);\n            ClientExceptionFiltersReversed = new ReversedFilterCollection<IClientExceptionFilter>(filtersList);\n            ServerExceptionFiltersReversed = new ReversedFilterCollection<IServerExceptionFilter>(filtersList);\n        }\n\n        /// <summary>\n        /// Gets all the client filters in the application.\n        /// </summary>\n        /// \n        /// <returns>\n        /// The client filters.\n        /// </returns>\n        public FilterCollection<IClientFilter> ClientFilters { get; }\n\n        /// <summary>\n        /// Gets all the server filters in the application.\n        /// </summary>\n        /// \n        /// <returns>\n        /// The server filters.\n        /// </returns>\n        public FilterCollection<IServerFilter> ServerFilters { get; }\n\n        /// <summary>\n        /// Gets all the stat changing filters in the application.\n        /// </summary>\n        /// \n        /// <returns>\n        /// The state changing filters.\n        /// </returns>\n        public FilterCollection<IElectStateFilter> ElectStateFilters { get; }\n\n        /// <summary>\n        /// Gets all the state changed filters in the application.\n        /// </summary>\n        /// \n        /// <returns>\n        /// The state changed filters.\n        /// </returns>\n        public FilterCollection<IApplyStateFilter> ApplyStateFilters { get; }\n\n        /// <summary>\n        /// Gets all the client exception filters in the application.\n        /// </summary>\n        /// \n        /// <returns>\n        /// The client exception filters.\n        /// </returns>\n        public ReversedFilterCollection<IClientExceptionFilter> ClientExceptionFiltersReversed { get; }\n\n        /// <summary>\n        /// Gets all the server exception filters in the application.\n        /// </summary>\n        /// \n        /// <returns>\n        /// The server exception filters.\n        /// </returns>\n        public ReversedFilterCollection<IServerExceptionFilter> ServerExceptionFiltersReversed { get; }\n\n        public readonly struct FilterCollection<T>(List<JobFilter> filters)\n        {\n            public Enumerator GetEnumerator() => new Enumerator(filters);\n\n            public ref struct Enumerator(List<JobFilter> filters)\n            {\n                private readonly List<JobFilter> _filters = filters;\n                private int _index = 0;\n                private T _current = default;\n\n                public bool MoveNext()\n                {\n                    List<JobFilter> localFilters = _filters;\n\n                    while (_index < localFilters.Count)\n                    {\n                        if (localFilters[_index++].Instance is T instance)\n                        {\n                            _current = instance;\n                            return true;\n                        }\n                    }\n\n                    return MoveNextRare();\n                }\n\n                public T Current => _current;\n\n                private bool MoveNextRare()\n                {\n                    _index = _filters.Count + 1;\n                    _current = default;\n                    return false;\n                }\n            }\n        }\n\n        public readonly struct ReversedFilterCollection<T>(List<JobFilter> filters)\n        {\n            public ReversedEnumerator GetEnumerator() => new ReversedEnumerator(filters);\n\n            public ref struct ReversedEnumerator(List<JobFilter> filters)\n            {\n                private readonly List<JobFilter> _filters = filters;\n                private int _index = filters.Count - 1;\n                private T _current = default;\n\n                public bool MoveNext()\n                {\n                    List<JobFilter> localFilters = _filters;\n\n                    while (_index >= 0)\n                    {\n                        if (localFilters[_index--].Instance is T instance)\n                        {\n                            _current = instance;\n                            return true;\n                        }\n                    }\n\n                    return MoveNextRare();\n                }\n\n                public T Current => _current;\n\n                private bool MoveNextRare()\n                {\n                    _index = -1;\n                    _current = default;\n                    return false;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/JobFilterProviderCollection.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Linq;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Represents the collection of filter providers for the application.\n    /// </summary>\n    public class JobFilterProviderCollection : Collection<IJobFilterProvider>, IJobFilterProvider\n    {\n        private static readonly JobFilterComparer FilterComparer = new JobFilterComparer();\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"JobFilterProviderCollection\"/> \n        /// class.\n        /// </summary>\n        public JobFilterProviderCollection()\n        {\n        }\n\n        public JobFilterProviderCollection(params IJobFilterProvider[] providers)\n            : base(providers)\n        {\n        }\n\n        /// <summary>\n        /// Returns the collection of filter providers.\n        /// </summary>\n        /// <param name=\"job\">Job, can be null.</param>\n        /// <returns>The collection of filter providers.</returns>\n        public IEnumerable<JobFilter> GetFilters(Job job)\n        {\n            var combinedFilters = new List<JobFilter>();\n\n            for (var i = 0; i < Items.Count; i++)\n            {\n                combinedFilters.AddRange(Items[i].GetFilters(job));\n            }\n\n            if (combinedFilters.Count > 1)\n            {\n                // Sorting before removing duplicates in the correct order\n                combinedFilters = combinedFilters.OrderBy(static filter => filter, FilterComparer).ToList();\n                RemoveDuplicates(combinedFilters);\n            }\n\n            return combinedFilters;\n        }\n\n        private static void RemoveDuplicates(List<JobFilter> filters)\n        {\n            var visitedTypes = new HashSet<Type>();\n\n            // Remove duplicates from the back forward\n            for (var i = filters.Count - 1; i >= 0; i--)\n            {\n                var filterInstance = filters[i].Instance;\n\n                if (!visitedTypes.Add(filterInstance.GetType()) && !AllowMultiple(filterInstance))\n                {\n                    filters.RemoveAt(i);\n                }\n            }\n        }\n\n        private static bool AllowMultiple(object filterInstance)\n        {\n            if (filterInstance is IJobFilter jobFilter)\n            {\n                return jobFilter.AllowMultiple;\n            }\n\n            return true;\n        }\n\n        private sealed class JobFilterComparer : IComparer<JobFilter>\n        {\n            public int Compare(JobFilter x, JobFilter y)\n            {\n                // Nulls always have to be less than non-nulls\n                if (x == null && y == null)\n                {\n                    return 0;\n                }\n                if (x == null)\n                {\n                    return -1;\n                }\n                if (y == null)\n                {\n                    return 1;\n                }\n\n                // Sort first by order...\n\n                if (x.Order < y.Order)\n                {\n                    return -1;\n                }\n                if (x.Order > y.Order)\n                {\n                    return 1;\n                }\n\n                // ...then by scope\n\n                if (x.Scope < y.Scope)\n                {\n                    return -1;\n                }\n                if (x.Scope > y.Scope)\n                {\n                    return 1;\n                }\n\n                return 0;\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Common/JobFilterProviders.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Provides a registration point for filters.\n    /// </summary>\n    public static class JobFilterProviders\n    {\n        static JobFilterProviders()\n        {\n            // ReSharper disable once UseObjectOrCollectionInitializer\n            Providers = new JobFilterProviderCollection();\n            Providers.Add(GlobalJobFilters.Filters);\n            Providers.Add(new JobFilterAttributeFilterProvider());\n        }\n\n        /// <summary>\n        /// Provides a registration point for filters.\n        /// </summary>\n        public static JobFilterProviderCollection Providers { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/JobFilterScope.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing Hangfire.Client;\nusing Hangfire.Server;\nusing Hangfire.States;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// Defines values that specify the order in which Hangfire filters \n    /// run within the same filter type and filter order.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// Hangfire supports the following types of filters:\n    /// \n    /// <list type=\"number\">\n    ///     <item>\n    ///         <description>\n    ///             Client / Server filters, which implement\n    ///             <see cref=\"IClientFilter\"/> and <see cref=\"IServerFilter\"/>\n    ///             interfaces respectively.\n    ///         </description>\n    ///     </item>\n    ///     <item>\n    ///         <description>\n    ///             State changing filters, which implement the\n    ///             <see cref=\"IElectStateFilter\"/> interface.\n    ///         </description>\n    ///     </item>\n    ///     <item>\n    ///         <description>\n    ///             State changed filters, which implement the\n    ///             <see cref=\"IApplyStateFilter\"/> interface.\n    ///         </description>\n    ///     </item>\n    ///     <item>\n    ///         <description>\n    ///             Client / Server exception filters, which implement\n    ///             <see cref=\"IClientExceptionFilter\"/> or \n    ///             <see cref=\"IServerExceptionFilter\"/> interfaces\n    ///             respectively.\n    ///         </description>\n    ///     </item>\n    /// </list>\n    /// \n    /// Порядок запуска указанных типов фильтров строго фиксирован, например,\n    /// фильтры исключений всегда выполняются после всех остальных фильтров,\n    /// а фильтры состояний всегда запускаются внутри клиентских и серверных\n    /// фильтров.\n    /// \n    /// Внутри же одного типа фильтров, порядок выполнения сначала определяется\n    /// значением Order, а затем значением Scope. Перечисление <see cref=\"JobFilterScope\"/> \n    /// определяет следующие значения (в порядке, в котором они будут выполнены):\n    /// \n    /// <list type=\"number\">\n    ///     <item>\n    ///         <description>\n    ///             <see cref=\"JobFilterScope.Global\"/>.\n    ///         </description>\n    ///     </item>\n    ///     <item>\n    ///         <description>\n    ///             <see cref=\"Type\"/>.\n    ///         </description>\n    ///     </item>\n    ///     <item>\n    ///         <description>\n    ///             <see cref=\"Method\"/>.\n    ///         </description>\n    ///     </item>\n    /// </list>\n    /// \n    /// Для примера, клиентский фильтр, у которого свойство Order имеет значение 0,\n    /// а значение filter scope равно <see cref=\"JobFilterScope.Global\"/>,\n    /// будет выполнен раньше фильтра с тем же самым значением Order,\n    /// но c filter scope, равным <see cref=\"Type\"/>.\n    /// \n    /// Значения Scope задаются, в основном, в реализациях интерфейса\n    /// <see cref=\"IJobFilterProvider\"/>. Так, класс <see cref=\"JobFilterCollection\"/>\n    /// определяет значение Scope как <see cref=\"JobFilterScope.Global\"/>.\n    /// \n    /// Порядок выполнения фильтров одинакового типа, с одинаковым значением\n    /// Order и с одинаковым scope, не оговаривается.\n    /// </remarks>\n    public enum JobFilterScope\n    {\n        /// <summary>\n        /// Specifies an order before the <see cref=\"Type\"/>.\n        /// </summary>\n        Global = 10,\n\n        /// <summary>\n        /// Specifies an order after the <see cref=\"Global\"/> and\n        /// before the <see cref=\"Method\"/>.\n        /// </summary>\n        Type = 20,\n\n        /// <summary>\n        /// Specifies an order after the <see cref=\"Type\"/>.\n        /// </summary>\n        Method = 30,\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Common/JobHelper.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Globalization;\nusing Hangfire.Annotations;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.Common\n{\n    public static class JobHelper\n    {\n        [Obsolete(\"Please use `GlobalConfiguration.UseSerializerSettings` instead. Will be removed in 2.0.0\")]\n        public static void SetSerializerSettings(JsonSerializerSettings setting)\n        {\n            SerializationHelper.SetUserSerializerSettings(setting);\n        }\n\n        [Obsolete(\"Please use `SerializationHelper.Serialize` with appropriate serialization option instead. Will be removed in 2.0.0\")]\n        public static string ToJson(object value)\n        {\n            return SerializationHelper.Serialize(value, null, SerializationOption.User);\n        }\n\n        [Obsolete(\"Please use `SerializationHelper.Deserialize` with appropriate serialization option instead. Will be removed in 2.0.0\")]\n        public static T FromJson<T>(string value)\n        {\n            return SerializationHelper.Deserialize<T>(value, SerializationOption.User);\n        }\n\n        [Obsolete(\"Please use `SerializationHelper.Deserialize` with appropriate serialization option instead. Will be removed in 2.0.0\")]\n        public static object FromJson(string value, [NotNull] Type type)\n        {\n            return SerializationHelper.Deserialize(value, type, SerializationOption.User);\n        }\n\n        private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);\n        private static readonly DateTime MillisecondTimestampBoundaryDate = new DateTime(1978, 1, 11, 21, 31, 40, 799, DateTimeKind.Utc);\n        private static readonly long MillisecondTimestampBoundary = 253402300799L;\n\n        public static long ToTimestamp(DateTime value)\n        {\n            TimeSpan elapsedTime = value - Epoch;\n            return (long)elapsedTime.TotalSeconds;\n        }\n\n        public static DateTime FromTimestamp(long value)\n        {\n            return Epoch.AddSeconds(value);\n        }\n\n        public static long ToMillisecondTimestamp(DateTime value)\n        {\n            TimeSpan elapsedTime = value - Epoch;\n            return (long)elapsedTime.TotalMilliseconds;\n        }\n\n        public static DateTime FromMillisecondTimestamp(long value)\n        {\n            return Epoch.AddMilliseconds(value);\n        }\n\n        public static string SerializeDateTime(DateTime value)\n        {\n            if (value > MillisecondTimestampBoundaryDate && \n                value < DateTime.MaxValue &&\n                GlobalConfiguration.HasCompatibilityLevel(CompatibilityLevel.Version_170))\n            {\n                return ToMillisecondTimestamp(value).ToString(\"D\", CultureInfo.InvariantCulture);\n            }\n\n            return value.ToString(\"O\", CultureInfo.InvariantCulture);\n        }\n\n        public static DateTime DeserializeDateTime(string value)\n        {\n            if (long.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out var timestamp))\n            {\n                return timestamp > MillisecondTimestampBoundary\n                    ? FromMillisecondTimestamp(timestamp) \n                    : FromTimestamp(timestamp);\n            }\n\n            return DateTime.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);\n        }\n\n        public static DateTime? DeserializeNullableDateTime(string value)\n        {\n            if (String.IsNullOrEmpty(value))\n            {\n                return null;\n            }\n\n            return DeserializeDateTime(value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/JobLoadException.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Runtime.Serialization;\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// The exception that is thrown when a job could not\n    /// be loaded from the storage due to missing or incorrect \n    /// information about its type or method.\n    /// </summary>\n#if !NETSTANDARD1_3\n    [Serializable]\n#endif\n    public class JobLoadException : Exception\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"JobLoadException\"/>\n        /// class with a given message and information about inner exception.\n        /// </summary>\n        public JobLoadException(string message, Exception inner) : base(message, inner)\n        {\n        }\n\n#if !NETSTANDARD1_3\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"JobLoadException\"/> class\n        /// with serialized data.\n        /// </summary>\n        /// <param name=\"info\">The <see cref=\"SerializationInfo\"/> that holds the serialized object data about the exception being thrown.</param>\n        /// <param name=\"context\">The <see cref=\"StreamingContext\"/> that contains contextual information about the source or destination.</param>\n        protected JobLoadException(SerializationInfo info, StreamingContext context)\n            : base(info, context)\n        {\n        }\n#endif\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Common/LanguagePolyfills.cs",
    "content": "﻿using System.ComponentModel;\n\nnamespace System.Runtime.CompilerServices\n{\n#if !NET5_0_OR_GREATER\n\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    internal static class IsExternalInit {}\n\n#endif // !NET5_0_OR_GREATER\n\n#if !NET7_0_OR_GREATER\n\n    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]\n    internal sealed class RequiredMemberAttribute : Attribute {}\n\n    [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]\n    internal sealed class CompilerFeatureRequiredAttribute : Attribute\n    {\n        public CompilerFeatureRequiredAttribute(string featureName)\n        {\n            FeatureName = featureName;\n        }\n\n        public string FeatureName { get; }\n        public bool   IsOptional  { get; init; }\n\n        public const string RefStructs      = nameof(RefStructs);\n        public const string RequiredMembers = nameof(RequiredMembers);\n    }\n\n#endif // !NET7_0_OR_GREATER\n}\n\nnamespace System.Diagnostics.CodeAnalysis\n{\n#if !NET7_0_OR_GREATER\n    [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]\n    internal sealed class SetsRequiredMembersAttribute : Attribute {}\n#endif\n}"
  },
  {
    "path": "src/Hangfire.Core/Common/MethodInfoExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Linq;\nusing System.Reflection;\n\nnamespace Hangfire.Common\n{\n    internal static class MethodInfoExtensions\n    {\n        public static string GetNormalizedName(this MethodInfo methodInfo)\n        {\n            // Method names containing '.' are considered explicitly implemented interface methods\n            // https://stackoverflow.com/a/17854048/1398672\n            return methodInfo.Name.Contains('.') && methodInfo.IsFinal && methodInfo.IsPrivate\n                ? methodInfo.Name.Split('.').Last()\n                : methodInfo.Name;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Common/ReflectedAttributeCache.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\n\nnamespace Hangfire.Common\n{\n    internal static class ReflectedAttributeCache\n    {\n        private static readonly ConcurrentDictionary<TypeInfo, ReadOnlyCollection<JobFilterAttribute>> TypeFilterAttributeCache \n            = new ConcurrentDictionary<TypeInfo, ReadOnlyCollection<JobFilterAttribute>>();\n\n        private static readonly ConcurrentDictionary<MethodInfo, ReadOnlyCollection<JobFilterAttribute>> MethodFilterAttributeCache\n            = new ConcurrentDictionary<MethodInfo, ReadOnlyCollection<JobFilterAttribute>>();\n\n        public static ICollection<JobFilterAttribute> GetTypeFilterAttributes(Type type)\n        {\n            return GetAttributes(TypeFilterAttributeCache, type.GetTypeInfo());\n        }\n\n        public static ICollection<JobFilterAttribute> GetMethodFilterAttributes(MethodInfo methodInfo)\n        {\n            return GetAttributes(MethodFilterAttributeCache, methodInfo);\n        }\n\n        private static ReadOnlyCollection<TAttribute> GetAttributes<TMemberInfo, TAttribute>(\n            ConcurrentDictionary<TMemberInfo, ReadOnlyCollection<TAttribute>> lookup, \n            TMemberInfo memberInfo)\n            where TAttribute : Attribute\n            where TMemberInfo : MemberInfo\n        {\n            Debug.Assert(memberInfo != null);\n            Debug.Assert(lookup != null);\n\n            return lookup.GetOrAdd(memberInfo, static mi => new ReadOnlyCollection<TAttribute>(mi.GetCustomAttributes<TAttribute>(inherit: true).ToArray()));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/SerializationHelper.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Globalization;\nusing System.IO;\nusing System.Reflection;\nusing System.Runtime.ExceptionServices;\nusing System.Text;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.Common\n{\n    public enum SerializationOption\n    {\n        /// <summary>\n        /// For internal data using isolated settings that can't be changed from user code.\n        /// </summary>\n        Internal,\n\n        /// <summary>\n        /// For internal data using isolated settings with types information (<see cref=\"TypeNameHandling.Objects\"/> setting) \n        /// that can't be changed from user code.\n        /// </summary>\n        TypedInternal,\n\n        /// <summary>\n        /// For user data like arguments and parameters, configurable via <see cref=\"SerializationHelper.SetUserSerializerSettings\"/>.\n        /// </summary>\n        User\n    }\n\n    /// <summary>\n    /// Provides methods to serialize/deserialize data with Hangfire default settings. \n    /// Isolates internal serialization process from user interference including `JsonConvert.DefaultSettings` modification.\n    /// </summary>\n    public static class SerializationHelper\n    {\n        private static readonly Lazy<JsonSerializerSettings> InternalSerializerSettings =\n            new Lazy<JsonSerializerSettings>(GetInternalSettings, LazyThreadSafetyMode.PublicationOnly);\n\n        private static JsonSerializerSettings _userSerializerSettings;\n\n        /// <summary>\n        /// Serializes data with <see cref=\"SerializationOption.Internal\"/> option.\n        /// Use this method to serialize internal data. Using isolated settings that can't be changed from user code.\n        /// </summary>\n        public static string Serialize<T>([CanBeNull] T value)\n        {\n            return Serialize(value, SerializationOption.Internal);\n        }\n\n        /// <summary>\n        /// Serializes data with specified option. \n        /// Use <see cref=\"SerializationOption.Internal\"/> option to serialize internal data.\n        /// Use <see cref=\"SerializationOption.TypedInternal\"/> option if you need to store type information.\n        /// Use <see cref=\"SerializationOption.User\"/> option to serialize user data like arguments and parameters,\n        /// configurable via <see cref=\"SetUserSerializerSettings\"/>.\n        /// </summary>\n        public static string Serialize<T>([CanBeNull] T value, SerializationOption option)\n        {\n            return Serialize(value, typeof(T), option);\n        }\n\n        /// <summary>\n        /// Serializes data with specified option. \n        /// Use <see cref=\"SerializationOption.Internal\"/> option to serialize internal data.\n        /// Use <see cref=\"SerializationOption.TypedInternal\"/> option if you need to store type information.\n        /// Use <see cref=\"SerializationOption.User\"/> option to serialize user data like arguments and parameters,\n        /// configurable via <see cref=\"SetUserSerializerSettings\"/>.\n        /// </summary>\n        public static string Serialize([CanBeNull] object value, [CanBeNull] Type type, SerializationOption option)\n        {\n            if (value == null) return null;\n\n            if (GlobalConfiguration.HasCompatibilityLevel(CompatibilityLevel.Version_170))\n            {\n                var serializerSettings = GetSerializerSettings(option);\n\n                if (option == SerializationOption.User)\n                {\n                    var formatting = serializerSettings?.Formatting ?? Formatting.None;\n                    return JsonConvert.SerializeObject(value, type, formatting, serializerSettings);\n                }\n\n                // For internal purposes we should ensure that JsonConvert.DefaultSettings don't affect\n                // the serialization process, and the only way is to create a custom serializer.\n                using var stringWriter = new StringWriter(new StringBuilder(256), CultureInfo.InvariantCulture);\n\n                using (var jsonWriter = new JsonTextWriter(stringWriter))\n                {\n                    var serializer = JsonSerializer.Create(serializerSettings);\n                    serializer.Serialize(jsonWriter, value, type);\n                }\n\n                return stringWriter.ToString();\n            }\n            else\n            {\n                // Previously almost all the data was serialized with the user settings, except\n                // when we explicitly needed to persist the type information. In the latter case\n                // custom settings passed to serializer, identical to TypedInternal.\n                var serializerSettings = option == SerializationOption.TypedInternal\n                    ? GetLegacyTypedSerializerSettings()\n                    : GetUserSerializerSettings();\n\n                // JsonConvert is used here, because previously global default settings affected\n                // the serialization process.\n                var formatting = serializerSettings?.Formatting ?? Formatting.None;\n                return JsonConvert.SerializeObject(value, type, formatting, serializerSettings);\n            }\n        }\n\n        /// <summary>\n        /// Deserializes data with <see cref=\"SerializationOption.Internal\"/> option.\n        /// Use this method to deserialize internal data. Using isolated settings that can't be changed from user code.\n        /// </summary>\n        public static object Deserialize([CanBeNull] string value, [NotNull] Type type)\n        {\n            return Deserialize(value, type, SerializationOption.Internal);\n        }\n\n        /// <summary>\n        /// Deserializes data with specified option. \n        /// Use <see cref=\"SerializationOption.Internal\"/> to deserialize internal data.\n        /// Use <see cref=\"SerializationOption.TypedInternal\"/> if deserializable internal data has type names information.\n        /// Use <see cref=\"SerializationOption.User\"/> to deserialize user data like arguments and parameters, \n        /// configurable via <see cref=\"SetUserSerializerSettings\"/>.\n        /// </summary>\n        public static object Deserialize([CanBeNull] string value, [NotNull] Type type, SerializationOption option)\n        {\n            if (type == null) throw new ArgumentNullException(nameof(type));\n            if (value == null) return null;\n\n            Exception exception = null;\n\n            if (option != SerializationOption.User)\n            {\n                var serializerSettings = GetSerializerSettings(option);\n\n                try\n                {\n                    // For internal purposes we should ensure that JsonConvert.DefaultSettings don't affect\n                    // the deserialization process, and the only way is to create a custom serializer.\n                    using var stringReader = new StringReader(value);\n                    using var jsonReader = new JsonTextReader(stringReader);\n\n                    var serializer = JsonSerializer.Create(serializerSettings);\n                    return serializer.Deserialize(jsonReader, type);\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    // If there was an exception, we should try to deserialize the value using user-based\n                    // settings, because prior to 1.7.0 they were used for almost everything. So we are saving\n                    // the exception to re-throw it if even serializer based on user settings couldn't handle\n                    // our value. In that case an original exception should be thrown as it is the reason.\n                    exception = ex;\n                }\n            }\n\n            try\n            {\n                return JsonConvert.DeserializeObject(value, type, GetSerializerSettings(SerializationOption.User));\n            }\n            catch (Exception ex) when (exception != null && ex.IsCatchableExceptionType())\n            {\n                ExceptionDispatchInfo.Capture(exception).Throw();\n                throw;\n            }\n        }\n\n        /// <summary>\n        /// Deserializes data with <see cref=\"SerializationOption.Internal\"/> option.\n        /// Use this method to deserialize internal data. Using isolated settings that can't be changed from user code.\n        /// </summary>\n        public static T Deserialize<T>([CanBeNull] string value)\n        {\n            if (value == null) return default(T);\n            return Deserialize<T>(value, SerializationOption.Internal);\n        }\n\n        /// <summary>\n        /// Deserializes data with specified option. \n        /// Use <see cref=\"SerializationOption.Internal\"/> to deserialize internal data.\n        /// Use <see cref=\"SerializationOption.TypedInternal\"/> if deserializable internal data has type names information.\n        /// Use <see cref=\"SerializationOption.User\"/> to deserialize user data like arguments and parameters, \n        /// configurable via <see cref=\"SetUserSerializerSettings\"/>.\n        /// </summary>\n        public static T Deserialize<T>([CanBeNull] string value, SerializationOption option)\n        {\n            if (value == null) return default(T);\n            return (T) Deserialize(value, typeof(T), option);\n        }\n\n        internal static JsonSerializerSettings GetInternalSettings()\n        {\n            var serializerSettings = new JsonSerializerSettings();\n\n            SetSimpleTypeNameAssemblyFormat(serializerSettings);\n\n            serializerSettings.TypeNameHandling = TypeNameHandling.Auto;\n            serializerSettings.DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate;\n            serializerSettings.NullValueHandling = NullValueHandling.Ignore;\n            serializerSettings.CheckAdditionalContent = true; // Default option in JsonConvert.Deserialize method\n            serializerSettings.MaxDepth = 128;\n#if NETSTANDARD2_0\n            serializerSettings.SerializationBinder = new TypeHelperSerializationBinder();\n#else\n            serializerSettings.Binder = new TypeHelperSerializationBinder();\n#endif\n\n            return serializerSettings;\n        }\n\n        internal static void SetUserSerializerSettings([CanBeNull] JsonSerializerSettings settings)\n        {\n            Volatile.Write(ref _userSerializerSettings, settings);\n        }\n\n        private static JsonSerializerSettings GetLegacyTypedSerializerSettings()\n        {\n            var serializerSettings = new JsonSerializerSettings();\n            serializerSettings.TypeNameHandling = TypeNameHandling.Objects;\n            serializerSettings.MaxDepth = 128;\n\n            SetSimpleTypeNameAssemblyFormat(serializerSettings);\n\n            return serializerSettings;\n        }\n\n        private static void SetSimpleTypeNameAssemblyFormat(JsonSerializerSettings serializerSettings)\n        {\n            // Setting TypeNameAssemblyFormatHandling to Simple. Using reflection, because latest versions\n            // of Newtonsoft.Json contain breaking changes.\n            var typeNameAssemblyFormatHandling =\n                typeof(JsonSerializerSettings).GetRuntimeProperty(\"TypeNameAssemblyFormatHandling\");\n            var typeNameAssemblyFormat = typeof(JsonSerializerSettings).GetRuntimeProperty(\"TypeNameAssemblyFormat\");\n\n            var property = typeNameAssemblyFormatHandling ?? typeNameAssemblyFormat;\n            property.SetValue(serializerSettings, Enum.Parse(property.PropertyType, \"Simple\"));\n        }\n\n        private static JsonSerializerSettings GetSerializerSettings(SerializationOption serializationOption)\n        {\n            switch (serializationOption)\n            {\n                case SerializationOption.Internal:\n                case SerializationOption.TypedInternal: return InternalSerializerSettings.Value;\n                case SerializationOption.User: return GetUserSerializerSettings();\n                default: throw new ArgumentOutOfRangeException(nameof(serializationOption), serializationOption, null);\n            }\n        }\n\n        private static JsonSerializerSettings GetUserSerializerSettings()\n        {\n            return Volatile.Read(ref _userSerializerSettings);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/ShallowExceptionHelper.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.IO;\nusing System.Text;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Common\n{\n    internal static class ShallowExceptionHelper\n    {\n        private static readonly object DataKey = \"OriginalStackTrace\";\n\n        public static void PreserveOriginalStackTrace(this Exception exception)\n        {\n            if (exception != null && !exception.Data.Contains(DataKey))\n            {\n                exception.Data.Add(DataKey, GetStackTrace(exception, includeFileInfo: false));\n            }\n        }\n\n        public static string ToStringWithOriginalStackTrace([NotNull] this Exception exception, int? numLines, bool includeFileInfo)\n        {\n            if (exception == null) throw new ArgumentNullException(nameof(exception));\n            return GetFirstLines(ToStringHelper(exception, false, includeFileInfo), numLines);\n        }\n\n        private static string ToStringHelper(Exception exception, bool isInner, bool includeFileInfo)\n        {\n            var sb = new StringBuilder();\n            sb.Append(exception.GetType().FullName);\n            sb.Append(\": \");\n            sb.Append(exception.Message);\n\n            if (exception.InnerException != null)\n            {\n                sb.Append(\" ---> \");\n                sb.Append(ToStringHelper(exception.InnerException, true, includeFileInfo));\n            }\n            else sb.Append('\\n');\n\n            var stackTrace = exception.Data.Contains(DataKey) ? (string)exception.Data[DataKey] : GetStackTrace(exception, includeFileInfo);\n            if (!String.IsNullOrWhiteSpace(stackTrace))\n            {\n                sb.Append(stackTrace);\n                sb.Append('\\n');\n            }\n\n            if (isInner) sb.Append(\"   --- End of inner exception stack trace ---\\n\");\n\n            return sb.ToString();\n        }\n\n        private static string GetFirstLines(string text, int? numLines)\n        {\n            if (text == null) return null;\n            if (!numLines.HasValue || numLines.Value < 0) return text;\n\n            using (var reader = new StringReader(text))\n            {\n                var builder = new StringBuilder();\n\n                while (numLines-- > 0)\n                {\n                    var line = reader.ReadLine();\n                    if (line == null) break;\n\n                    if (builder.Length > 0) builder.AppendLine();\n                    builder.Append(line);\n                }\n\n                return builder.ToString();\n            }\n        }\n\n        private static string GetStackTrace(Exception ex, bool includeFileInfo)\n        {\n#if NETSTANDARD1_3\n            return ex.StackTrace;\n#else\n            return new System.Diagnostics.StackTrace(ex, includeFileInfo).ToString();\n#endif\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Common/TypeExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text.RegularExpressions;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Common\n{\n    internal static class TypeExtensions\n    {\n        private static readonly Regex GenericArgumentsRegex = new Regex(@\"`[1-9]\\d*\", RegexOptions.Compiled, TimeSpan.FromSeconds(5));\n\n        public static string ToGenericTypeString(this Type type)\n        {\n            if (!type.GetTypeInfo().IsGenericType)\n            {\n                return type.GetFullNameWithoutNamespace()\n                        .ReplacePlusWithDotInNestedTypeName();\n            }\n\n            return type.GetGenericTypeDefinition()\n                    .GetFullNameWithoutNamespace()\n                    .ReplacePlusWithDotInNestedTypeName()\n                    .ReplaceGenericParametersInGenericTypeName(type);\n        }\n\n        public static MethodInfo GetNonOpenMatchingMethod(\n            [NotNull] this Type type,\n            [NotNull] string name,\n            [CanBeNull] Type[] parameterTypes)\n        {\n            if (type == null) throw new ArgumentNullException(nameof(type));\n            if (name == null) throw new ArgumentNullException(nameof(name));\n\n            parameterTypes = parameterTypes ?? Type.EmptyTypes;\n\n            var methodCandidates = new List<MethodInfo>(type.GetRuntimeMethods());\n\n            if (type.GetTypeInfo().IsInterface)\n            {\n                methodCandidates.AddRange(type.GetTypeInfo()\n                    .ImplementedInterfaces.SelectMany(static x => x.GetRuntimeMethods()));\n            }\n\n            foreach (var methodCandidate in methodCandidates)\n            {\n                if (!methodCandidate.GetNormalizedName().Equals(name, StringComparison.Ordinal))\n                {\n                    continue;\n                }\n\n                var parameters = methodCandidate.GetParameters();\n                if (parameters.Length != parameterTypes.Length)\n                {\n                    continue;\n                }\n\n                var parameterTypesMatched = true;\n\n                var genericArguments = methodCandidate.ContainsGenericParameters\n                    ? new Type[methodCandidate.GetGenericArguments().Length]\n                    : null;\n                \n                // Determining whether we can use this method candidate with\n                // current parameter types.\n                for (var i = 0; i < parameters.Length; i++)\n                {\n                    var parameterType = parameters[i].ParameterType.GetTypeInfo();\n                    var actualType = parameterTypes[i].GetTypeInfo();\n\n                    if (!TypesMatchRecursive(parameterType, actualType, genericArguments))\n                    {\n                        parameterTypesMatched = false;\n                        break;\n                    }\n                }\n\n                if (parameterTypesMatched)\n                {\n                    if (genericArguments != null)\n                    {\n                        var genericArgumentsResolved = true;\n\n                        foreach (var genericArgument in genericArguments)\n                        {\n                            if (genericArgument == null)\n                            {\n                                genericArgumentsResolved = false;\n                            }\n                        }\n\n                        if (genericArgumentsResolved)\n                        {\n                            return methodCandidate.MakeGenericMethod(genericArguments);\n                        }\n                    }\n                    else\n                    {\n                        // Return first found method candidate with matching parameters.\n                        return methodCandidate;\n                    }\n                }\n            }\n\n            return null;\n        }\n\n        public static Type[] GetAllGenericArguments(this TypeInfo type)\n        {\n            return type.GenericTypeArguments.Length > 0 ? type.GenericTypeArguments : type.GenericTypeParameters;\n        }\n\n        private static bool TypesMatchRecursive(TypeInfo parameterType, TypeInfo actualType, IList<Type> genericArguments)\n        {\n            if (parameterType.IsGenericParameter)\n            {\n                var position = parameterType.GenericParameterPosition;\n                \n                // Return false if this generic parameter has been identified and it's not the same as actual type\n                if (genericArguments[position] != null && genericArguments[position].GetTypeInfo() != actualType)\n                {\n                    return false;\n                }\n\n                genericArguments[position] = actualType.AsType();\n                return true;\n            }\n\n            if (parameterType.ContainsGenericParameters)\n            {\n                if (parameterType.IsArray)\n                {\n                    // Return false if parameterType is array whereas actualType isn't\n                    if (!actualType.IsArray) return false;\n\n                    var parameterElementType = parameterType.GetElementType();\n                    var actualElementType = actualType.GetElementType();\n\n                    return TypesMatchRecursive(parameterElementType.GetTypeInfo(), actualElementType.GetTypeInfo(), genericArguments);\n                }\n\n                if (!actualType.IsGenericType || parameterType.GetGenericTypeDefinition() != actualType.GetGenericTypeDefinition())\n                {\n                    return false;\n                }\n\n                for (var i = 0; i < parameterType.GenericTypeArguments.Length; i++)\n                {\n                    var parameterGenericArgument = parameterType.GenericTypeArguments[i];\n                    var actualGenericArgument = actualType.GenericTypeArguments[i];\n\n                    if (!TypesMatchRecursive(parameterGenericArgument.GetTypeInfo(), actualGenericArgument.GetTypeInfo(), genericArguments))\n                    {\n                        return false;\n                    }\n                }\n\n                return true;\n            }\n\n            return parameterType != typeof(object).GetTypeInfo() \n                ? parameterType.IsAssignableFrom(actualType)\n                : parameterType == actualType;\n        }\n\n        private static string GetFullNameWithoutNamespace(this Type type)\n        {\n            if (type.IsGenericParameter)\n            {\n                return type.Name;\n            }\n\n            const int dotLength = 1;\n            // ReSharper disable once PossibleNullReferenceException\n            return !String.IsNullOrEmpty(type.Namespace)\n                ? type.FullName.Substring(type.Namespace.Length + dotLength)\n                : type.FullName;\n        }\n\n        private static string ReplacePlusWithDotInNestedTypeName(this string typeName)\n        {\n            return typeName.Replace('+', '.');\n        }\n\n        private static string ReplaceGenericParametersInGenericTypeName(this string typeName, Type type)\n        {\n            var genericArguments = type .GetTypeInfo().GetAllGenericArguments();\n\n            typeName = GenericArgumentsRegex.Replace(typeName, match =>\n            {\n                var currentGenericArgumentNumbers = int.Parse(match.Value.Substring(1), CultureInfo.InvariantCulture);\n                var currentArguments = string.Join(\",\", genericArguments.Take(currentGenericArgumentNumbers).Select(ToGenericTypeString));\n                genericArguments = genericArguments.Skip(currentGenericArgumentNumbers).ToArray();\n                return string.Concat(\"<\", currentArguments, \">\");\n            });\n\n            return typeName;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/TypeHelper.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing System.Threading;\n\nnamespace Hangfire.Common\n{\n    public class TypeHelper\n    {\n        private static readonly ConcurrentDictionary<Type, string> DefaultTypeSerializerCache = new ConcurrentDictionary<Type, string>();\n        private static readonly ConcurrentDictionary<Type, string> SimpleAssemblyTypeSerializerCache = new ConcurrentDictionary<Type, string>();\n\n        private static readonly ConcurrentDictionary<string, Type> DefaultTypeResolverCache = new ConcurrentDictionary<string, Type>();\n        private static readonly ConcurrentDictionary<string, Type> IgnoredAssemblyVersionTypeResolverCache = new ConcurrentDictionary<string, Type>();\n\n        private static readonly Assembly CoreLibrary = typeof(int).GetTypeInfo().Assembly;\n        private static readonly AssemblyName MscorlibAssemblyName = new AssemblyName(\"mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\");\n        private static readonly ConcurrentDictionary<string, Assembly> AssemblyCache = new ConcurrentDictionary<string, Assembly>();\n\n        private static readonly Regex VersionRegex = new Regex(@\", Version=\\d+.\\d+.\\d+.\\d+\", RegexOptions.Compiled, TimeSpan.FromSeconds(5));\n        private static readonly Regex CultureRegex = new Regex(@\", Culture=\\w+\", RegexOptions.Compiled, TimeSpan.FromSeconds(5));\n        private static readonly Regex PublicKeyTokenRegex = new Regex(@\", PublicKeyToken=\\w+\", RegexOptions.Compiled, TimeSpan.FromSeconds(5));\n\n        private static Func<string, Type> _currentTypeResolver;\n        private static Func<Type, string> _currentTypeSerializer;\n\n        public static Func<string, Type> CurrentTypeResolver\n        {\n            get => Volatile.Read(ref _currentTypeResolver) ?? DefaultTypeResolver;\n            set => Volatile.Write(ref _currentTypeResolver, value);\n        }\n\n        public static Func<Type, string> CurrentTypeSerializer\n        {\n            get => Volatile.Read(ref _currentTypeSerializer) ?? DefaultTypeSerializer;\n            set => Volatile.Write(ref _currentTypeSerializer, value);\n        }\n\n        public static string DefaultTypeSerializer(Type type)\n        {\n            return DefaultTypeSerializerCache.GetOrAdd(type, static t => t.AssemblyQualifiedName);\n        }\n\n        public static string SimpleAssemblyTypeSerializer(Type type)\n        {\n            return SimpleAssemblyTypeSerializerCache.GetOrAdd(type, static value =>\n            {\n                var builder = new StringBuilder();\n                SerializeType(value, true, builder);\n\n                return builder.ToString();\n            });\n        }\n\n        public static Type DefaultTypeResolver(string typeName)\n        {\n            return DefaultTypeResolverCache.GetOrAdd(typeName, static name =>\n            {\n#if NETSTANDARD1_3\n                name = name.Replace(\"System.Private.CoreLib\", \"mscorlib\");\n                return Type.GetType(\n                    name,\n                    throwOnError: true);\n#else\n                return Type.GetType(\n                    name,\n                    typeResolver: TypeResolver,\n                    assemblyResolver: CachedAssemblyResolver,\n                    throwOnError: true);\n#endif\n            });\n        }\n\n        public static Type IgnoredAssemblyVersionTypeResolver(string typeName)\n        {\n            return IgnoredAssemblyVersionTypeResolverCache.GetOrAdd(typeName, static value =>\n            {\n                value = VersionRegex.Replace(value, String.Empty);\n                value = CultureRegex.Replace(value, String.Empty);\n                value = PublicKeyTokenRegex.Replace(value, String.Empty);\n\n                return DefaultTypeResolver(value);\n            });\n        }\n\n        private static void SerializeType(Type type, bool withAssemblyName, StringBuilder typeNameBuilder)\n        {\n            if (type == typeof(System.Console))\n            {\n                typeNameBuilder.Append(\"System.Console, mscorlib\");\n                return;\n            }\n\n            if (type == typeof(System.Threading.Thread))\n            {\n                typeNameBuilder.Append(\"System.Threading.Thread, mscorlib\");\n                return;\n            }\n\n            if (type.IsArray && type.HasElementType)\n            {\n                var elementType = type.GetElementType()!;\n                SerializeType(elementType, false, typeNameBuilder);\n                typeNameBuilder.Append(\"[]\");\n            }\n            else\n            {\n                if (type.DeclaringType != null)\n                {\n                    SerializeType(type.DeclaringType, false, typeNameBuilder);\n                    typeNameBuilder.Append('+');\n                }\n                else if (type.Namespace != null)\n                {\n                    typeNameBuilder.Append(type.Namespace).Append('.');\n                }\n\n                typeNameBuilder.Append(type.Name);\n            }\n\n            if (type.GenericTypeArguments.Length > 0)\n            {\n                SerializeTypes(type.GenericTypeArguments, typeNameBuilder);\n            }\n\n            if (!withAssemblyName) return;\n\n            var typeInfo = type.GetTypeInfo();\n\n            if (type != typeof(object) && type != typeof(string) && !typeInfo.IsPrimitive)\n            {\n                string assemblyName;\n\n                var typeForwardedFrom = typeInfo.GetCustomAttribute<TypeForwardedFromAttribute>();\n                if (typeForwardedFrom != null)\n                {\n                    assemblyName = typeForwardedFrom.AssemblyFullName;\n\n                    var delimiterIndex = assemblyName.IndexOf(\",\", StringComparison.OrdinalIgnoreCase);\n\n                    assemblyName = delimiterIndex >= 0 ? assemblyName.Substring(0, delimiterIndex) : assemblyName;\n                }\n                else\n                {\n                    assemblyName = typeInfo.Assembly.GetName().Name;\n                }\n\n                if (assemblyName.Equals(\"System.Private.CoreLib\", StringComparison.OrdinalIgnoreCase))\n                {\n                    assemblyName = \"mscorlib\";\n                }\n\n                typeNameBuilder.Append(\", \").Append(assemblyName);\n            }\n        }\n\n        private static void SerializeTypes(Type[] types, StringBuilder typeNamesBuilder)\n        {\n            if (types == null) return;\n\n            typeNamesBuilder.Append('[');\n\n            for (var i = 0; i < types.Length; i++)\n            {\n                typeNamesBuilder.Append('[');\n                SerializeType(types[i], true, typeNamesBuilder);\n                typeNamesBuilder.Append(']');\n\n                if (i != types.Length - 1) typeNamesBuilder.Append(',');\n            }\n\n            typeNamesBuilder.Append(']');\n        }\n\n        private static Assembly CachedAssemblyResolver(AssemblyName assemblyName)\n        {\n            return AssemblyCache.GetOrAdd(assemblyName.FullName, AssemblyResolver);\n        }\n\n        private static Assembly AssemblyResolver(string assemblyString)\n        {\n            var assemblyName = new AssemblyName(assemblyString);\n\n            if (assemblyName.Name.Equals(\"System.Console\", StringComparison.OrdinalIgnoreCase) ||\n                assemblyName.Name.Equals(\"System.Private.CoreLib\", StringComparison.OrdinalIgnoreCase) ||\n                assemblyName.Name.Equals(\"mscorlib\", StringComparison.OrdinalIgnoreCase))\n            {\n                assemblyName = MscorlibAssemblyName;\n            }\n            else if (assemblyName.Name.Equals(\"System.Private.Xml.Linq\", StringComparison.OrdinalIgnoreCase))\n            {\n                assemblyName = new AssemblyName(\"System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\");\n            }\n\n            var publicKeyToken = assemblyName.GetPublicKeyToken();\n\n#if !NETSTANDARD1_3\n            if (assemblyName.Version == null && assemblyName.CultureInfo == null && publicKeyToken == null)\n            {\n#pragma warning disable 618\n                return Assembly.LoadWithPartialName(assemblyName.Name);\n#pragma warning restore 618\n            }\n#endif\n\n            try\n            {\n                return Assembly.Load(assemblyName);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                var shortName = new AssemblyName(assemblyName.Name);\n                if (publicKeyToken != null)\n                {\n                    shortName.SetPublicKeyToken(publicKeyToken);\n                }\n\n                return Assembly.Load(shortName);\n            }\n        }\n\n        private static Type TypeResolver(Assembly assembly, string typeName, bool ignoreCase)\n        {\n            if (typeName.Equals(\"System.Diagnostics.Debug\", StringComparison.Ordinal))\n            {\n                return typeof(System.Diagnostics.Debug);\n            }\n\n            assembly = assembly ?? CoreLibrary;\n\n            if (assembly != CoreLibrary &&\n                assembly.GetName().Name.Equals(\"mscorlib\", StringComparison.OrdinalIgnoreCase))\n            {\n                // Everything defaults to `mscorlib` for interoperability reasons between\n                // .NET Framework and .NET Core. Most of the types have the proper forwarding,\n                // but newer types like DateOnly or TimeOnly don't. So for types from `mscorlib`\n                // we perform the first search in the current core library.\n                var type = CoreLibrary.GetType(typeName, false, ignoreCase);\n                if (type != null) return type;\n            }\n\n            return assembly.GetType(typeName, true, ignoreCase);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Common/TypeHelperSerializationBinder.cs",
    "content": "// This file is part of Hangfire. Copyright © 2022 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Runtime.Serialization;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.Common\n{\n    public sealed class TypeHelperSerializationBinder : SerializationBinder\n#if NETSTANDARD2_0\n            , Newtonsoft.Json.Serialization.ISerializationBinder\n#endif\n    {\n        public override Type BindToType(string assemblyName, string typeName)\n        {\n            return TypeHelper.CurrentTypeResolver($\"{typeName}, {assemblyName}\");\n        }\n\n        public override void BindToName(Type serializedType, out string assemblyName, out string typeName)\n        {\n            assemblyName = null;\n            typeName = TypeHelper.CurrentTypeSerializer(serializedType);\n\n            if (typeName == null) return;\n\n            var delimiterIndex = GetAssemblyNameDelimiterIndex(typeName);\n\n            if (delimiterIndex >= 0)\n            {\n                assemblyName = typeName.Substring(delimiterIndex + 1).Trim();\n                typeName = typeName.Substring(0, delimiterIndex).Trim();\n            }\n        }\n\n        private static int GetAssemblyNameDelimiterIndex(string typeName)\n        {\n            var level = 0;\n\n            for (var index = 0; index < typeName.Length; index++)\n            {\n                var current = typeName[index];\n                if (current == '[') level++;\n                else if (current == ']') level--;\n                else if (current == ',' && level == 0) return index;\n            }\n\n            return -1;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/ContinuationsSupportAttribute.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.States;\nusing Hangfire.Storage;\n\nnamespace Hangfire\n{\n    public class ContinuationsSupportAttribute : JobFilterAttribute, IElectStateFilter, IApplyStateFilter\n    {\n        internal static readonly HashSet<string> KnownFinalStates = new HashSet<string> { DeletedState.StateName, SucceededState.StateName };\n\n        private static readonly TimeSpan AddJobLockTimeout = TimeSpan.FromMinutes(1);\n        private static readonly TimeSpan ContinuationStateFetchTimeout = TimeSpan.FromMinutes(1);\n        private static readonly TimeSpan ContinuationInvalidTimeout = TimeSpan.FromMinutes(15);\n\n        private readonly ILog _logger = LogProvider.For<ContinuationsSupportAttribute>();\n\n        private readonly bool _pushResults;\n        private readonly HashSet<string> _knownFinalStates;\n        private readonly IBackgroundJobStateChanger _stateChanger;\n\n        public ContinuationsSupportAttribute()\n            : this(false)\n        {\n        }\n\n        public ContinuationsSupportAttribute(bool pushResults)\n            : this(pushResults, KnownFinalStates)\n        {\n        }\n\n        public ContinuationsSupportAttribute(HashSet<string> knownFinalStates)\n            : this(false, knownFinalStates)\n        {\n        }\n\n        public ContinuationsSupportAttribute(bool pushResults, HashSet<string> knownFinalStates)\n            : this(pushResults, knownFinalStates, new BackgroundJobStateChanger())\n        {\n        }\n\n        public ContinuationsSupportAttribute(\n            [NotNull] HashSet<string> knownFinalStates,\n            [NotNull] IBackgroundJobStateChanger stateChanger)\n            : this(false, knownFinalStates, stateChanger)\n        {\n        }\n\n        public ContinuationsSupportAttribute(\n            bool pushResults,\n            [NotNull] HashSet<string> knownFinalStates, \n            [NotNull] IBackgroundJobStateChanger stateChanger)\n        {\n            if (knownFinalStates == null) throw new ArgumentNullException(nameof(knownFinalStates));\n            if (stateChanger == null) throw new ArgumentNullException(nameof(stateChanger));\n\n            _pushResults = pushResults;\n            _knownFinalStates = knownFinalStates;\n            _stateChanger = stateChanger;\n\n            // Ensure this filter is the last filter in the chain to start\n            // continuations on the last candidate state only.\n            Order = 1000;\n        }\n\n        public void OnStateElection(ElectStateContext context)\n        {\n            var awaitingState = context.CandidateState as AwaitingState;\n            if (awaitingState != null)\n            {\n                // Branch for a child background job.\n                AddContinuation(context, awaitingState);\n            }\n            else if (_knownFinalStates.Contains(context.CandidateState.Name))\n            {\n                // Branch for a parent background job.\n                ExecuteContinuationsIfExist(context);\n            }\n        }\n\n        public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)\n        {\n            // TODO: Remove this method and IApplyStateFilter interface in 2.0.0.\n        }\n\n        internal static List<Continuation> DeserializeContinuations(string serialized)\n        {\n            var continuations =  SerializationHelper.Deserialize<List<Continuation>>(serialized);\n\n            if (continuations != null && continuations.TrueForAll(static x => x.JobId == null))\n            {\n                continuations = SerializationHelper.Deserialize<List<Continuation>>(serialized, SerializationOption.User);\n            }\n\n            return continuations ?? new List<Continuation>();\n        }\n\n        private void AddContinuation(ElectStateContext context, AwaitingState awaitingState)\n        {\n            var connection = context.Connection;\n            var parentId = awaitingState.ParentId;\n\n            // We store continuations as a json array in a job parameter. Since there \n            // is no way to add a continuation in an atomic way, we are placing a \n            // distributed lock on parent job to prevent race conditions, when\n            // multiple threads add continuation to the same parent job.\n            using (connection.AcquireDistributedJobLock(parentId, AddJobLockTimeout))\n            {\n                var jobData = connection.GetJobData(parentId);\n                if (jobData == null)\n                {\n                    // When we try to add a continuation for a removed job,\n                    // the system should throw an exception instead of creating\n                    // corrupted state.\n                    throw new InvalidOperationException(\n                        $\"Can not add a continuation: parent background job '{parentId}' does not exist.\");\n                }\n\n                var continuations = GetContinuations(context, parentId);\n\n                // Continuation may be already added. This may happen, when outer transaction\n                // was failed after adding a continuation last time, since the addition is\n                // performed outside of an outer transaction.\n                if (!continuations.Exists(x => x.JobId == context.BackgroundJob.Id))\n                {\n                    continuations.Add(new Continuation { JobId = context.BackgroundJob.Id, Options = awaitingState.Options });\n\n                    // Set continuation only after ensuring that parent job still\n                    // exists. Otherwise we could create add non-expiring (garbage)\n                    // parameter for the parent job.\n                    SetContinuations(connection, parentId, continuations);\n                }\n\n                var currentState = connection.GetStateData(parentId);\n\n                if (currentState != null && _knownFinalStates.Contains(currentState.Name))\n                {\n                    var startImmediately = ShouldStartContinuation(currentState.Name, awaitingState.Options);\n\n                    if (_pushResults && startImmediately)\n                    {\n                        if (SucceededState.StateName.Equals(currentState.Name, StringComparison.OrdinalIgnoreCase))\n                        {\n                            if (currentState.Data.TryGetValue(\"Result\", out var antecedentResult))\n                            {\n                                context.Connection.SetJobParameter(context.BackgroundJob.Id, \"AntecedentResult\", antecedentResult);\n                            }\n                        }\n                        else if (DeletedState.StateName.Equals(currentState.Name, StringComparison.OrdinalIgnoreCase))\n                        {\n                            if (!currentState.Data.TryGetValue(\"Exception\", out var antecedentException))\n                            {\n                                antecedentException = JobParameterInjectionFilter.DefaultException;\n                            }\n                            \n                            context.Connection.SetJobParameter(context.BackgroundJob.Id, \"AntecedentException\", antecedentException);\n                        }\n                    }\n\n                    context.CandidateState = startImmediately\n                        ? awaitingState.NextState\n                        : new DeletedState { Reason = \"Continuation condition was not met\" };\n                }\n            }\n        }\n\n        private void ExecuteContinuationsIfExist(ElectStateContext context)\n        {\n            // The following lines are executed inside a distributed job lock,\n            // so it is safe to get continuation list here.\n            var continuations = GetContinuations(context, null);\n            var nextStates = new Dictionary<string, IState>();\n\n            // Getting continuation data for all continuations – state they are waiting \n            // for and their next state.\n            foreach (var continuation in continuations)\n            {\n                if (String.IsNullOrWhiteSpace(continuation.JobId)) continue;\n\n                var currentState = GetContinuationState(context, continuation.JobId, ContinuationStateFetchTimeout);\n                if (currentState == null)\n                {\n                    continue;\n                }\n\n                // All continuations should be in the awaiting state. If someone changed \n                // the state of a continuation, we should simply skip it.\n                if (currentState.Name != AwaitingState.StateName) continue;\n\n                IState nextState;\n\n                if (!ShouldStartContinuation(context.CandidateState.Name, continuation.Options))\n                {\n                    nextState = new DeletedState { Reason = \"Continuation condition was not met\" };\n                }\n                else\n                {\n                    try\n                    {\n                        nextState = SerializationHelper.Deserialize<IState>(currentState.Data[\"NextState\"], SerializationOption.TypedInternal);\n                    }\n                    catch (Exception ex) when (ex.IsCatchableExceptionType())\n                    {\n                        nextState = new FailedState(ex)\n                        {\n                            Reason = \"An error occurred while deserializing the continuation\"\n                        };\n                    }\n                }\n\n                if (!nextStates.ContainsKey(continuation.JobId))\n                {\n                    // Duplicate continuations possible, when they were added before version 1.6.10.\n                    // Please see details in comments for the AddContinuation method near the line\n                    // with checking for existence (continuations.Exists).\n                    nextStates.Add(continuation.JobId, nextState);\n                }\n            }\n            \n            string antecedentResult = null;\n            string antecedentException = null;\n\n            if (_pushResults)\n            {\n                if (context.CandidateState is SucceededState)\n                {\n                    var serializedData = context.CandidateState.SerializeData();\n                    serializedData.TryGetValue(\"Result\", out antecedentResult);\n                }\n                else if (context.CandidateState is DeletedState)\n                {\n                    var serializedData = context.CandidateState.SerializeData();\n                    if (!serializedData.TryGetValue(\"Exception\", out antecedentException))\n                    {\n                        antecedentException = JobParameterInjectionFilter.DefaultException;\n                    }\n                }\n            }\n\n            foreach (var tuple in nextStates)\n            {\n                if (antecedentResult != null)\n                {\n                    context.Connection.SetJobParameter(tuple.Key, \"AntecedentResult\", antecedentResult);\n                }\n\n                if (antecedentException != null)\n                {\n                    context.Connection.SetJobParameter(tuple.Key, \"AntecedentException\", antecedentException);\n                }\n\n                _stateChanger.ChangeState(new StateChangeContext(\n                    context.Storage,\n                    context.Connection,\n                    tuple.Key,\n                    tuple.Value,\n                    AwaitingState.StateName));\n            }\n        }\n\n        private StateData GetContinuationState(ElectStateContext context, string continuationJobId, TimeSpan timeout)\n        {\n            StateData currentState = null;\n\n            var started = Stopwatch.StartNew();\n            var firstAttempt = true;\n\n            while (true)\n            {\n                var continuationData = context.Connection.GetJobData(continuationJobId);\n                if (continuationData == null)\n                {\n                    _logger.Warn(\n                        $\"Can not start continuation '{continuationJobId}' for background job '{context.BackgroundJob.Id}': continuation does not exist.\");\n\n                    break;\n                }\n\n                currentState = context.Connection.GetStateData(continuationJobId);\n                if (currentState != null)\n                {\n                    break;\n                }\n\n                if (DateTime.UtcNow - continuationData.CreatedAt > ContinuationInvalidTimeout)\n                {\n                    _logger.Warn(\n                        $\"Continuation '{continuationJobId}' has been ignored: it was deemed to be aborted, because its state is still non-initialized.\");\n\n                    break;\n                }\n\n                if (started.Elapsed >= timeout)\n                {\n                    _logger.Warn(\n                        $\"Can not start continuation '{continuationJobId}' for background job '{context.BackgroundJob.Id}': timeout expired while trying to fetch continuation state.\");\n\n                    break;\n                }\n\n                Thread.Sleep(firstAttempt ? 0 : 100);\n                firstAttempt = false;\n            }\n\n            return currentState;\n        }\n\n        private bool ShouldStartContinuation(string antecedentStateName, JobContinuationOptions options)\n        {\n            if (options == JobContinuationOptions.OnAnyFinishedState)\n            {\n                return _knownFinalStates.Contains(antecedentStateName);\n            }\n\n            if (options.HasFlag(JobContinuationOptions.OnlyOnSucceededState) &&\n                SucceededState.StateName.Equals(antecedentStateName, StringComparison.OrdinalIgnoreCase))\n            {\n                return true;\n            }\n\n            if (options.HasFlag(JobContinuationOptions.OnlyOnDeletedState) &&\n                DeletedState.StateName.Equals(antecedentStateName, StringComparison.OrdinalIgnoreCase))\n            {\n                return true;\n            }\n\n            return false;\n        }\n\n        private static void SetContinuations(\n            IStorageConnection connection, string jobId, List<Continuation> continuations)\n        {\n            connection.SetJobParameter(jobId, \"Continuations\", SerializationHelper.Serialize(continuations));\n        }\n\n        private static List<Continuation> GetContinuations(ElectStateContext context, string jobId)\n        {\n            // We are altering continuation list only when its background job is locked,\n            // and parameter snapshot is obtained only when background job is locked, so\n            // it's safe to use cached list when possible.\n            string serialized;\n\n            if (String.IsNullOrEmpty(jobId) && context.BackgroundJob.ParametersSnapshot != null)\n            {\n                context.BackgroundJob.ParametersSnapshot.TryGetValue(\"Continuations\", out serialized);\n            }\n            else\n            {\n                serialized = context.Connection.GetJobParameter(jobId ?? context.BackgroundJob.Id, \"Continuations\");\n            }\n            \n            return DeserializeContinuations(serialized);\n        }\n\n        void IApplyStateFilter.OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)\n        {\n        }\n\n        internal struct Continuation\n        {\n            public string JobId { get; set; }\n            public JobContinuationOptions Options { get; set; }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Cron.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Helper class that provides common values for the cron expressions.\n    /// </summary>\n    public static class Cron\n    {\n        /// <summary>\n        /// Returns cron expression that fires every minute.\n        /// </summary>\n        public static string Minutely()\n        {\n            return \"* * * * *\";\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every hour at the first minute.\n        /// </summary>\n        public static string Hourly()\n        {\n            return Hourly(minute: 0);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every hour at the specified minute.\n        /// </summary>\n        /// <param name=\"minute\">The minute in which the schedule will be activated (0-59).</param>\n        public static string Hourly(int minute)\n        {\n            return $\"{minute} * * * *\";\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every day at 00:00 UTC.\n        /// </summary>\n        public static string Daily()\n        {\n            return Daily(hour: 0);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every day at the first minute of\n        /// the specified hour in UTC.\n        /// </summary>\n        /// <param name=\"hour\">The hour in which the schedule will be activated (0-23).</param>\n        public static string Daily(int hour)\n        {\n            return Daily(hour, minute: 0);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every day at the specified hour and minute\n        /// in UTC.\n        /// </summary>\n        /// <param name=\"hour\">The hour in which the schedule will be activated (0-23).</param>\n        /// <param name=\"minute\">The minute in which the schedule will be activated (0-59).</param>\n        public static string Daily(int hour, int minute)\n        {\n            return $\"{minute} {hour} * * *\";\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every week at Monday, 00:00 UTC.\n        /// </summary>\n        public static string Weekly()\n        {\n            return Weekly(DayOfWeek.Monday);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every week at 00:00 UTC of the specified\n        /// day of the week.\n        /// </summary>\n        /// <param name=\"dayOfWeek\">The day of week in which the schedule will be activated.</param>\n        public static string Weekly(DayOfWeek dayOfWeek)\n        {\n            return Weekly(dayOfWeek, hour: 0);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every week at the first minute\n        /// of the specified day of week and hour in UTC.\n        /// </summary>\n        /// <param name=\"dayOfWeek\">The day of week in which the schedule will be activated.</param>\n        /// <param name=\"hour\">The hour in which the schedule will be activated (0-23).</param>\n        public static string Weekly(DayOfWeek dayOfWeek, int hour)\n        {\n            return Weekly(dayOfWeek, hour, minute: 0);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every week at the specified day\n        /// of week, hour and minute in UTC.\n        /// </summary>\n        /// <param name=\"dayOfWeek\">The day of week in which the schedule will be activated.</param>\n        /// <param name=\"hour\">The hour in which the schedule will be activated (0-23).</param>\n        /// <param name=\"minute\">The minute in which the schedule will be activated (0-59).</param>\n        public static string Weekly(DayOfWeek dayOfWeek, int hour, int minute)\n        {\n            return $\"{minute} {hour} * * {(int) dayOfWeek}\";\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every month at 00:00 UTC of the first\n        /// day of month.\n        /// </summary>\n        public static string Monthly()\n        {\n            return Monthly(day: 1);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every month at 00:00 UTC of the specified\n        /// day of month.\n        /// </summary>\n        /// <param name=\"day\">The day of month in which the schedule will be activated (1-31).</param>\n        public static string Monthly(int day)\n        {\n            return Monthly(day, hour: 0);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every month at the first minute of the\n        /// specified day of month and hour in UTC.\n        /// </summary>\n        /// <param name=\"day\">The day of month in which the schedule will be activated (1-31).</param>\n        /// <param name=\"hour\">The hour in which the schedule will be activated (0-23).</param>\n        public static string Monthly(int day, int hour)\n        {\n            return Monthly(day, hour, minute: 0);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every month at the specified day of month,\n        /// hour and minute in UTC.\n        /// </summary>\n        /// <param name=\"day\">The day of month in which the schedule will be activated (1-31).</param>\n        /// <param name=\"hour\">The hour in which the schedule will be activated (0-23).</param>\n        /// <param name=\"minute\">The minute in which the schedule will be activated (0-59).</param>\n        public static string Monthly(int day, int hour, int minute)\n        {\n            return $\"{minute} {hour} {day} * *\";\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every year on Jan, 1st at 00:00 UTC.\n        /// </summary>\n        public static string Yearly()\n        {\n            return Yearly(month: 1);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every year in the first day at 00:00 UTC\n        /// of the specified month.\n        /// </summary>\n        /// <param name=\"month\">The month in which the schedule will be activated (1-12).</param>\n        public static string Yearly(int month)\n        {\n            return Yearly(month, day: 1);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every year at 00:00 UTC of the specified\n        /// month and day of month.\n        /// </summary>\n        /// <param name=\"month\">The month in which the schedule will be activated (1-12).</param>\n        /// <param name=\"day\">The day of month in which the schedule will be activated (1-31).</param>\n        public static string Yearly(int month, int day)\n        {\n            return Yearly(month, day, hour: 0);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every year at the first minute of the\n        /// specified month, day and hour in UTC.\n        /// </summary>\n        /// <param name=\"month\">The month in which the schedule will be activated (1-12).</param>\n        /// <param name=\"day\">The day of month in which the schedule will be activated (1-31).</param>\n        /// <param name=\"hour\">The hour in which the schedule will be activated (0-23).</param>\n        public static string Yearly(int month, int day, int hour)\n        {\n            return Yearly(month, day, hour, minute: 0);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every year at the specified month, day,\n        /// hour and minute in UTC.\n        /// </summary>\n        /// <param name=\"month\">The month in which the schedule will be activated (1-12).</param>\n        /// <param name=\"day\">The day of month in which the schedule will be activated (1-31).</param>\n        /// <param name=\"hour\">The hour in which the schedule will be activated (0-23).</param>\n        /// <param name=\"minute\">The minute in which the schedule will be activated (0-59).</param>\n        public static string Yearly(int month, int day, int hour, int minute)\n        {\n            return $\"{minute} {hour} {day} {month} *\";\n        }\n\n        /// <summary>\n        /// Returns cron expression that never fires. Specifically 31st of February.\n        /// </summary>\n        public static string Never()\n        {\n            return Yearly(2, 31);\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every <paramref name=\"interval\"/> minutes.\n        /// </summary>\n        /// <remarks>\n        /// Please note that only those intervals into which the number 60 is evenly divisible make sense\n        /// in Cron expressions, such as 2, 5, 10, 15, 30, etc. Intervals such as 7, 13, 25 will not work\n        /// correctly.\n        /// </remarks>\n        /// <param name=\"interval\">The number of minutes to wait between every activation.</param>\n        public static string MinuteInterval(int interval)\n        {\n            return $\"*/{interval} * * * *\";\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every <paramref name=\"interval\"/> hours.\n        /// </summary>\n        /// <remarks>\n        /// Please note that only those intervals into which the number 24 is evenly divisible make sense\n        /// in Cron expressions, such as 2, 4, 6, 8, 12, etc. Intervals such as 7, 13, 25 will not work\n        /// correctly.\n        /// </remarks>\n        /// <param name=\"interval\">The number of hours to wait between every activation.</param>\n        public static string HourInterval(int interval)\n        {\n            return $\"0 */{interval} * * *\";\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every <paramref name=\"interval\"/> days.\n        /// </summary>\n        /// <remarks>\n        /// Please note that only those intervals into which the number 30 is evenly divisible make sense\n        /// in Cron expressions, such as 2, 5, 6, 10, 15, etc. Intervals such as 7, 13, 25 will not work\n        /// correctly. However, even in this case the actual intervals may be longer in months with\n        /// 31 or 28 days.\n        /// </remarks>\n        /// <param name=\"interval\">The number of days to wait between every activation.</param>\n        public static string DayInterval(int interval)\n        {\n            return $\"0 0 */{interval} * *\";\n        }\n\n        /// <summary>\n        /// Returns cron expression that fires every <paramref name=\"interval\"/> months.\n        /// </summary>\n        /// <remarks>\n        /// Please note that only those intervals into which the number 12 is evenly divisible make sense\n        /// in Cron expressions, such as 2, 3, 4, 6. Intervals such as 7, 13, 25 will not work correctly.\n        /// </remarks>\n        /// <param name=\"interval\">The number of months to wait between every activation.</param>\n        public static string MonthInterval(int interval)\n        {\n            return $\"0 0 1 */{interval} *\";\n        }\n\n#if FEATURE_CRONDESCRIPTOR\n        /// <summary>\n        /// Converts a Cron expression string into a description.\n        /// </summary>\n        /// <param name=\"cronExpression\">A Cron expression string.</param>\n        /// <returns>English description.</returns>\n        [Obsolete(\"Please install `CronExpressionDescriptor` package manually and use it.\")]\n        public static string GetDescription(string cronExpression)\n        {\n            string[] expressionParts = cronExpression.Split(' ');\n\n            if (expressionParts.Length != 5)\n            {\n                throw new InvalidCastException(\"Invalid Cron Expression\");\n            }\n\n            foreach (string expressionPart in expressionParts)\n            {\n                int num;\n                if (!Int32.TryParse(expressionPart, out num) && expressionPart != \"*\")\n                {\n                    throw new InvalidCastException(\"Invalid Cron Expression\");\n                }\n            }\n\n            return CronExpressionDescriptor.ExpressionDescriptor.GetDescription(cronExpression);\n        }\n#endif\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/BatchCommandDispatcher.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Net;\nusing System.Threading.Tasks;\n\nnamespace Hangfire.Dashboard\n{\n    internal sealed class BatchCommandDispatcher : IDashboardDispatcher\n    {\n        private readonly Action<DashboardContext, string> _command;\n\n        public BatchCommandDispatcher(Action<DashboardContext, string> command)\n        {\n            _command = command;\n        }\n\n#if FEATURE_OWIN\n        [Obsolete(\"Use the `BatchCommandDispatcher(Action<DashboardContext>, string)` instead. Will be removed in 2.0.0.\")]\n        public BatchCommandDispatcher(Action<RequestDispatcherContext, string> command)\n        {\n            _command = (context, jobId) => command(RequestDispatcherContext.FromDashboardContext(context), jobId);\n        }\n#endif\n\n        public async Task Dispatch(DashboardContext context)\n        {\n            if (context.IsReadOnly)\n            {\n                context.Response.StatusCode = 401;\n                return;\n            }\n\n            var jobIds = await context.Request.GetFormValuesAsync(\"jobs[]\").ConfigureAwait(false);\n            if (jobIds.Count == 0)\n            {\n                context.Response.StatusCode = 422;\n                return;\n            }\n\n            foreach (var jobId in jobIds)\n            {\n                _command(context, jobId);\n            }\n\n            context.Response.StatusCode = (int)HttpStatusCode.NoContent;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/CombinedResourceDispatcher.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard\n{\n    internal sealed class CombinedResourceDispatcher : EmbeddedResourceDispatcher\n    {\n        private readonly IEnumerable<Tuple<Assembly, string>> _resources;\n\n        public CombinedResourceDispatcher(\n            [NotNull] string contentType, \n            [NotNull] IEnumerable<Tuple<Assembly, string>> resources)\n            : base(contentType, null, null)\n        {\n            if (resources == null) throw new ArgumentNullException(nameof(resources));\n            _resources = resources;\n        }\n\n        protected override async Task WriteResponse(DashboardResponse response)\n        {\n            IEnumerable<Tuple<Assembly, string>> copy;\n\n            lock (_resources)\n            {\n                copy = _resources.ToArray();\n            }\n\n            foreach (var resource in copy)\n            {\n                await WriteResource(\n                    response,\n                    resource.Item1,\n                    resource.Item2).ConfigureAwait(false);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/CommandDispatcher.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Net;\nusing System.Threading.Tasks;\n\nnamespace Hangfire.Dashboard\n{\n    internal sealed class CommandDispatcher : IDashboardDispatcher\n    {\n        private readonly Func<DashboardContext, bool> _command;\n\n        public CommandDispatcher(Func<DashboardContext, bool> command)\n        {\n            _command = command;\n        }\n\n#if FEATURE_OWIN\n        [Obsolete(\"Use the `CommandDispatcher(Func<DashboardContext, bool>)` ctor instead. Will be removed in 2.0.0.\")]\n        public CommandDispatcher(Func<RequestDispatcherContext, bool> command)\n        {\n            _command = context => command(RequestDispatcherContext.FromDashboardContext(context));\n        }\n#endif\n\n        public Task Dispatch(DashboardContext context)\n        {\n            if (context.IsReadOnly)\n            {\n                context.Response.StatusCode = 401;\n                return Task.FromResult(false);\n            }\n\n            var request = context.Request;\n            var response = context.Response;\n\n            if (!\"POST\".Equals(request.Method, StringComparison.OrdinalIgnoreCase))\n            {\n                response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;\n                return Task.FromResult(false);\n            }\n\n            if (_command(context))\n            {\n                response.StatusCode = (int)HttpStatusCode.NoContent;\n            }\n            else\n            {\n                response.StatusCode = 422;\n            }\n\n            return Task.FromResult(true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/css/hangfire-dark.css",
    "content": "@media (prefers-color-scheme: dark) {\n    html {\n        color-scheme: dark;\n    }\n\n    /* Common */\n    .page-header {\n        border-bottom: 1px solid #4d4d4d;\n    }\n\n    body {\n        background-color: #22272e;\n        color: #adbac7;\n    }\n\n    a {\n        color: #539bf5;\n    }\n\n    a:focus, \n    a:hover {\n        color: #539bf5;\n        text-decoration: underline;\n    }\n\n    /* Tables */\n\n    .table .table {\n        background-color: transparent;\n    }\n\n    .table-striped > tbody > tr:nth-of-type(odd) {\n        background-color: #2d333b;\n    }\n\n    .table > tbody > tr.hover:hover > td, \n    .table > tbody > tr.hover:hover > th {\n        background-color: #2d333b;\n    }\n\n    .table > tbody > tr.highlight > td, \n    .table > tbody > tr.highlight > th {\n        background-color: #2d333b;\n        border-color: #4d4d4d\n    }\n\n    .table > tbody > tr.highlight:hover > td, \n    .table > tbody > tr.highlight:hover > th {\n        background-color: #2d333b;\n        border-color: #4d4d4d\n    }\n\n    .table > tbody > tr > td, \n    .table > tbody > tr > th, \n    .table > tfoot > tr > td, \n    .table > tfoot > tr > th, \n    .table > thead > tr > td, \n    .table > thead > tr > th {\n        border-color: #4d4d4d\n    }\n\n    /* Footer */\n\n    div#footer {\n        background: #2d333b;\n    }\n\n    /* State cards */\n\n    .state-card {\n        background-color: transparent;\n        border: 1px solid #4d4d4d;\n        padding: 0;\n    }\n\n    .state-card-body {\n        background-color: transparent;\n        border-top: 1px solid #4d4d4d;\n        padding: 12px;\n        margin: 0;\n    }\n\n    .state-card-title {\n        background-color: transparent;\n        padding: 12px;\n    }\n\n    .state-card-text {\n        padding: 0 12px 12px 12px;\n        margin: 0;\n    }\n\n    /* State card success */\n\n    .state-card-state-success {\n        border-color: rgba(87, 171, 90, 0.4) !important;\n    }\n\n    .state-card-state-success .state-card-title {\n        background-color: rgba(87, 171, 90, 0.1);\n        border-color: rgba(87, 171, 90, 0.4);\n        color: rgba(87, 171, 90) !important;\n    }\n\n    .state-card-state-success .state-card-body {\n        background-color: transparent !important;\n        border-top-color: rgba(87, 171, 90, 0.4);\n    }\n\n    .state-card-state-success .state-card-text {\n        background-color: rgba(87, 171, 90, 0.1);\n    }\n\n    /* State card danger */\n\n    .state-card-state-danger {\n        border-color: rgba(235, 156, 166, 0.3) !important;\n    }\n\n    .state-card-state-danger .state-card-title {\n        background-color: rgba(215, 58, 74, 0.1);\n        border-color: rgba(235, 156, 166, 0.3) !important;\n        color: rgb(235, 156, 166) !important;\n    }\n\n    .state-card-state-danger .state-card-body {\n        background-color: transparent !important;\n        border-color: rgba(235, 156, 166, 0.3);\n    }\n\n    .state-card-state-danger .state-card-text {\n        background-color: rgba(215, 58, 74, 0.1);\n    }\n\n    /* State card warning */\n\n    .state-card-state-warning {\n        border-color: rgba(250, 201, 5, 0.3) !important;\n    }\n\n    .state-card-state-warning .state-card-title {\n        background-color: rgba(251, 202, 4, 0.1);\n        border-color: rgba(250, 201, 5, 0.3) !important;\n        color: rgb(250, 201, 5) !important;\n    }\n\n    .state-card-state-warning .state-card-body {\n        background-color: transparent !important;\n        border-color: rgba(250, 201, 5, 0.3);\n    }\n\n    .state-card-state-warning .state-card-text {\n        background-color: rgba(251, 202, 4, 0.1);\n    }\n\n    /* State card info */\n\n    .state-card-state-info {\n        border-color: rgba(104, 167, 234, 0.3) !important;\n    }\n\n    .state-card-state-info .state-card-title {\n        background-color: rgba(29, 118, 219, 0.1);\n        border-color: rgba(104, 167, 234, 0.3) !important;\n        color: rgb(104, 167, 234) !important;\n    }\n\n    .state-card-state-info .state-card-body {\n        background-color: transparent !important;\n        border-color: rgba(104, 167, 234, 0.3)\n    }\n\n    .state-card-state-info .state-card-text {\n        background-color: rgba(29, 118, 219, 0.1);\n    }\n\n    /* State card inactive */\n\n    .state-card-state-inactive {\n        border-color: #777 !important;\n    }\n\n    .state-card-state-inactive .state-card-title {\n        color: #777 !important;\n    }\n\n    .state-card-state-inactive .state-card-body {\n        background-color: #333 !important;\n    }\n\n    /* State card active */\n\n    .state-card-state-active {\n        border-color: #999 !important;\n    }\n\n    .state-card-state-active .state-card-title {\n        color: #999 !important;\n    }\n\n    .state-card-state-active .state-card-body {\n        background-color: #999 !important;\n        color: #444 !important;\n    }\n\n    /* Stats */\n\n    #stats {\n        background: #2d333b;\n    }\n\n    #stats .list-group-item {\n        border-color: #4d4d4d;\n        background-color: transparent;\n    }\n\n    #stats a.list-group-item {\n        color: rgba(255, 255, 255, 0.6);\n        border-color: #4d4d4d;\n    }\n\n    #stats a.list-group-item:hover,\n    #stats a.list-group-item:focus {\n        border-color: #4d4d4d;\n        color: #fff;\n    }\n\n    #stats .list-group-item.active,\n    #stats .list-group-item.active:hover,\n    #stats .list-group-item.active:focus {\n        background: #373e47;\n        border-color: #4d4d4d;\n        color: #fff;\n    }\n\n    /* Navbar */\n\n    .navbar-default {\n        background: #2d333b;\n        border: none;\n    }\n\n    .navbar-default .navbar-brand {\n        color: rgba(255, 255, 255, 0.6);\n    }\n\n        .navbar-default .navbar-brand:hover {\n            color: rgba(255, 255, 255, 1);\n        }\n\n    .navbar-default .navbar-nav > li > a {\n        color: rgba(255, 255, 255, 0.6);\n    }\n\n    .navbar-default .navbar-nav > li > a:focus,\n    .navbar-default .navbar-nav > li > a:hover {\n        color: #ffffff;\n    }\n\n    .navbar-default .navbar-nav > .active > a,\n    .navbar-default .navbar-nav > .active > a:focus,\n    .navbar-default .navbar-nav > .active > a:hover {\n        background: #373e47;\n        color: #ffffff;\n    }\n\n    .navbar-default .navbar-toggle {\n        border-color: #4d4d4d;\n    }\n\n    .navbar-default .navbar-collapse, .navbar-default .navbar-form {\n        border-color: #4d4d4d;\n    }\n\n    .navbar-default .navbar-toggle:focus, .navbar-default .navbar-toggle:hover {\n        background-color: #373e47;\n    }\n\n    /* Paginator */\n\n    .paginator .btn {\n        background: #2d333b;\n        color: rgba(255, 255, 255, 0.6);\n    }\n\n    .paginator .btn.active,\n    .paginator  a.btn.btn-default.active {\n        background: rgba(29, 118, 219, 0.6);\n        color: #fff;\n    }\n\n    .btn-group .btn.active,\n    .btn-group .paginator  a.btn.btn-default.active {\n        background: rgba(29, 118, 219, 0.6);\n        color: #fff;\n    }\n\n    /* btn-default */\n\n    .btn-default {\n        background: #2d333b;\n        border-color: #4d4d4d;\n        color: rgba(255, 255, 255, 0.9);\n    }\n\n    .btn-default.active.focus, \n    .btn-default.active:focus, \n    .btn-default.active:hover, \n    .btn-default:active.focus, \n    .btn-default:active:focus, \n    .btn-default:active:hover, \n    .open>.dropdown-toggle.btn-default.focus, \n    .open>.dropdown-toggle.btn-default:focus, \n    .open>.dropdown-toggle.btn-default:hover,\n    .btn-default:hover,\n    .btn-default:focus,\n    .btn-default:active:focus,\n    a.btn.btn-default.active {\n        background: #373e47;\n        border-color: #4d4d4d;\n        color: rgba(255, 255, 255, 1);\n    }\n\n    .btn-default.disabled.focus, \n    .btn-default.disabled:focus, \n    .btn-default.disabled:hover, \n    .btn-default[disabled].focus, \n    .btn-default[disabled]:focus, \n    .btn-default[disabled]:hover, \n    fieldset[disabled] .btn-default.focus, \n    fieldset[disabled] .btn-default:focus, \n    fieldset[disabled] .btn-default:hover {\n        background: #2d333b;\n        border-color: #4d4d4d;\n        color: rgba(255, 255, 255, 0.9);\n    }\n\n    /* btn-primary */\n\n    .btn-primary {\n        background-color: rgba(29, 118, 219, 0.6);\n        border: 1px solid rgba(29, 118, 219, 0.3);\n        color: rgba(255, 255, 255, 0.9);\n    }\n\n    .btn-primary.active.focus, \n    .btn-primary.active:focus, \n    .btn-primary.active:hover, \n    .btn-primary:active.focus, \n    .btn-primary:active:focus, \n    .btn-primary:active:hover, \n    .open>.dropdown-toggle.btn-primary.focus, \n    .open>.dropdown-toggle.btn-primary:focus, \n    .open>.dropdown-toggle.btn-primary:hover,\n    .btn-primary:hover,\n    .btn-primary:focus,\n    .btn-primary:active:focus,\n    a.btn.btn-primary.active {\n        background-color: rgba(29, 118, 219, 0.8);\n        border: 1px solid rgba(29, 118, 219, 0.3);\n        color: rgba(255, 255, 255, 1);\n    }\n\n    .btn-primary.disabled.focus, \n    .btn-primary.disabled:focus, \n    .btn-primary.disabled:hover, \n    .btn-primary[disabled].focus, \n    .btn-primary[disabled]:focus, \n    .btn-primary[disabled]:hover, \n    fieldset[disabled] .btn-primary.focus, \n    fieldset[disabled] .btn-primary:focus, \n    fieldset[disabled] .btn-primary:hover {\n        background-color: rgba(29, 118, 219, 0.6);\n        border: 1px solid rgba(29, 118, 219, 0.3);\n        color: rgba(255, 255, 255, 0.9);\n    }\n\n    /* btn-death */\n\n    .btn-death {\n        background-color: rgba(229, 88, 79, 0.6);\n        border: 1px solid rgba(229, 88, 79, 0.3);\n        color: rgba(255, 255, 255, 0.9);\n    }\n\n    .btn-death.active.focus, \n    .btn-death.active:focus, \n    .btn-death.active:hover, \n    .btn-death:active.focus, \n    .btn-death:active:focus, \n    .btn-death:active:hover, \n    .open>.dropdown-toggle.btn-death.focus, \n    .open>.dropdown-toggle.btn-death:focus, \n    .open>.dropdown-toggle.btn-death:hover,\n    .btn-death:hover,\n    .btn-death:focus,\n    .btn-death:active:focus,\n    a.btn.btn-death.active {\n        background-color: rgba(229, 88, 79, 0.8);\n        border: 1px solid rgba(229, 88, 79, 0.3);\n        color: rgba(255, 255, 255, 1);\n    }\n\n    .btn-death.disabled.focus, \n    .btn-death.disabled:focus, \n    .btn-death.disabled:hover, \n    .btn-death[disabled].focus, \n    .btn-death[disabled]:focus, \n    .btn-death[disabled]:hover, \n    fieldset[disabled] .btn-death.focus, \n    fieldset[disabled] .btn-death:focus, \n    fieldset[disabled] .btn-death:hover {\n        background-color: rgba(229, 88, 79, 0.6);\n        border: 1px solid rgba(229, 88, 79, 0.3);\n        color: rgba(255, 255, 255, 0.9);\n    }\n\n    /* btn-group */\n\n    /* Alerts */\n\n    .alert-info {\n        background-color: rgba(29, 118, 219, 0.1);\n        border-color: rgba(104, 167, 234, 0.3);\n        color: rgb(104, 167, 234);\n    }\n\n    .alert-warning {\n        background-color: rgba(251, 202, 4, 0.1);\n        border-color: rgba(250, 201, 5, 0.3);\n        color: rgb(250, 201, 5);\n    }\n\n    .alert-success {\n        background-color: rgba(87, 171, 90, 0.1);\n        border-color: rgba(87, 171, 90, 0.4);\n        color: rgba(87, 171, 90);\n    }\n\n    .alert-danger {\n        background-color: rgba(215, 58, 74, 0.1);\n        border-color: rgba(235, 156, 166, 0.3);\n        color: rgb(235, 156, 166);\n    }\n\n    /* Job Snippet */\n\n    .job-snippet {\n        background: #22272e;\n        border: 1px solid #4d4d4d;\n    }\n\n    .job-snippet-code code { \n        color: #fff;\n    }\n\n    .job-snippet-code pre .comment {\n        color: #57ab5a;\n    }\n\n    .job-snippet-code pre .keyword {\n        color: #539bf5;\n    }\n\n    .job-snippet-code pre .string {\n        color: #e5534b;\n    }\n\n    .job-snippet-code pre .type {\n        color: rgb(43, 145, 175);\n    }\n\n    .job-snippet-code pre .xmldoc {\n        color: rgb(128, 128, 128);\n    }\n\n    .job-snippet-properties code {\n        color: #e5534b;\n    }\n\n    /* code */\n\n    pre code {\n        border: none;\n    }\n\n    code {\n        background-color: rgba(29, 118, 219, 0.1);\n        border: 1px solid rgba(104, 167, 234, 0.3);\n        color: rgb(104, 167, 234);\n    }\n\n    /* Stack Trace */\n\n    .stack-trace {\n        background: transparent;\n        border: 1px solid #4d4d4d;  \n        color: #fff; \n    }\n\n    .st-type {\n        font-weight: bold;\n    }\n\n    .st-param-name {\n        color: #666;\n    }\n\n    .st-file {\n        color: #999;\n    }\n\n    .st-method {\n        color: #539bf5;\n        font-weight: bold;\n    }\n\n    .st-line {\n        color: #bb00bb;\n    }\n\n    /* Metric */\n\n    div.metric.metric-default {\n        border-color: #ddd;\n    }\n\n    div.metric-info,\n    span.metric-info {\n        color: #5bc0de;\n        border-color: #5bc0de;\n    }\n\n    span.metric-info.highlighted {\n        background-color: #5bc0de;\n    }\n\n    div.metric-warning,\n    span.metric-warning {\n        color: #f0ad4e;\n        border-color: #f0ad4e;\n    }\n\n    span.metric-warning.highlighted {\n        background-color: #f0ad4e;\n    }\n\n    div.metric-success,\n    span.metric-success {\n        color: #5cb85c;\n        border-color: #5cb85c;\n    }\n\n    span.metric-success.highlighted {\n        background-color: #5cb85c;\n    }\n\n    div.metric-danger,\n    span.metric-danger {\n        color: #d9534f;\n        border-color: #d9534f;\n    }\n\n    span.metric-danger.highlighted {\n        background-color: #d9534f;\n    }\n\n    /* State labels */\n\n    .label-state-success {\n        background-color: rgba(87, 171, 90, 0.1) !important;\n        border: 1px solid rgba(87, 171, 90, 0.4) !important;\n        color: rgba(87, 171, 90) !important;\n    }\n\n        a:hover .label-hover.label-state-success {\n            background-color: rgba(87, 171, 90, 0.4) !important;\n            color: rgba(87, 171, 90) !important;\n        }\n\n    .label-state-danger {\n        background-color: rgba(215, 58, 74, 0.1) !important;\n        border: 1px solid rgba(235, 156, 166, 0.3) !important;\n        color: rgb(235, 156, 166) !important;\n    }\n\n        a:hover .label-hover.label-state-danger {\n            background-color: rgba(215, 58, 74, 0.4) !important;\n            color: rgb(235, 156, 166) !important;\n        }\n\n    .label-state-warning {\n        background-color: rgba(251, 202, 4, 0.1) !important;\n        border: 1px solid rgba(250, 201, 5, 0.3) !important;\n        color: rgb(250, 201, 5) !important;\n    }\n\n        a:hover .label-hover.label-state-warning {\n            background-color: rgba(251, 202, 4, 0.4) !important;\n            color: rgb(250, 201, 5) !important;\n        }\n\n    .label-state-info {\n        background-color: rgba(29, 118, 219, 0.1) !important;\n        border: 1px solid rgba(104, 167, 234, 0.3) !important;\n        color: rgb(104, 167, 234) !important;\n    }\n\n        a:hover .label-hover.label-state-info {\n            background-color: rgba(29, 118, 219, 0.4) !important;\n            color: rgb(104, 167, 234) !important;\n        }\n\n    .label-state-inactive {\n        background-color: rgba(204, 204, 204, 0.1) !important;\n        border: 1px solid rgba(204, 204, 204, 0.3) !important;\n        color: rgb(204, 204, 204) !important;\n    }\n\n        a:hover .label-hover.label-state-inactive {\n            background-color: rgba(204, 204, 204, 0.4) !important;\n            color: rgb(204, 204, 204) !important;\n        }\n\n    .label-state-active {\n        background-color: rgba(204, 204, 204, 0.1);\n        border: 1px solid rgba(204, 204, 204, 0.3) !important;\n        color: rgb(204, 204, 204) !important;\n    }\n\n        a:hover .label-hover.label-state-active {\n            background-color: rgba(204, 204, 204, 0.4) !important;\n            color: rgb(204, 204, 204) !important;\n        }\n\n    /* Common */\n\n    .table td.failed-job-details {\n        background-color: transparent;\n    }\n\n    .nav-tabs > li > a {\n        color: rgba(255, 255, 255, 0.6);\n    }\n\n    .nav-tabs > li > a:hover {\n        color: white;\n        background-color: transparent;\n        border-color: transparent!important;\n    }\n\n    .nav-tabs > li.active > a, .nav-tabs > li.active > a:focus, .nav-tabs > li.active > a:hover {\n        color: white;\n        background-color: #373e47;\n        border-color: #4d4d4d #4d4d4d transparent #4d4d4d !important;\n    }\n\n    .table-hover > tbody > tr:hover {\n        background-color: #2d333b;\n    }\n\n    .progress {\n        background-color: #2d333b;\n    }\n\n    div.metric.metric-default {\n        border-color: #4d4d4d;\n    }\n\n    @media (min-width: 768px) {\n        .nav-tabs > li > a {\n            border-bottom-color: #4d4d4d!important;\n        }\n\n        .nav-tabs > li > a:hover {\n            border-bottom-color: #4d4d4d!important;\n        }\n    }\n\n    @media screen and (max-width: 767px) {\n        .table-responsive {\n            border-color: #4d4d4d;\n        }\n    }\n\n    .table-vertical tbody > tr > th {\n        border-right-color: #4d4d4d!important;\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/css/hangfire.css",
    "content": "/* Sticky footer styles\n-------------------------------------------------- */\n\nhtml, body {\n  height: 100%;\n  /* The html and body elements cannot have any padding or margin. */\n}\n\nbody {\n    /* 75px to make the container go all the way to the bottom of the topbar */\n    padding-top: 75px;\n}\n\n.col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, .col-lg-4,\n.col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-md-1, .col-md-10, .col-md-11,\n.col-md-12, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8,\n.col-md-9, .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, .col-sm-4,\n.col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-xs-1, .col-xs-10, .col-xs-11,\n.col-xs-12, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8,\n.col-xs-9 {\n    min-height: 2px !important;\n}\n\n/* Wrapper for page content to push down footer */\n#wrap {\n  min-height: 100%;\n  height: auto !important;\n  height: 100%;\n  /* Negative indent footer by its height */\n  margin: 0 auto -60px;\n  /* Pad bottom by footer height */\n  padding: 0 0 60px;\n}\n\n/* Set the fixed height of the footer here */\n#footer {\n  background-color: #f5f5f5;\n}\n\n\n/* Custom page CSS\n-------------------------------------------------- */\n\n.container .credit {\n  margin: 20px 0;\n  word-break: break-word;\n}\n\n.page-header {\n    margin-top: 0;\n    overflow: hidden;\n    white-space: nowrap;\n    text-overflow: ellipsis;\n}\n\n.btn-death {\n    background-color: #777;\n    border-color: #666;\n    color: #fff;\n}\n\n.btn-death:hover {\n    background-color: #666;\n    border-color: #555;\n    color: #fff;\n}\n\n.list-group .list-group-item .glyphicon {\n    margin-right: 3px;\n}\n\n.breadcrumb {\n    margin-bottom: 10px;\n    background-color: inherit;\n    padding: 0;\n}\n\n.btn-toolbar-label {\n    padding: 7px 0;\n    vertical-align: middle;\n    display: inline-block;\n    margin-left: 5px;\n}\n\n.btn-toolbar-label-sm {\n    padding: 5px 0;\n}\n\n.btn-toolbar-spacer {\n    width: 5px;\n    display: inline-block;\n    height: 1px;\n}\n\n.tooltip {\n    word-break: break-word;\n}\n\na:hover .label-hover {\n    background-color: #2a6496!important;\n    color: #fff!important;\n}\n\n.expander {\n    cursor: pointer;\n}\n\n.expandable {\n    display: none;\n}\n\n.table-inner {\n    margin-bottom: 7px;\n    font-size: 90%;\n}\n\n.min-width {\n    width: 1%;\n    white-space: nowrap;\n}\n\n.min-width-125p {\n    min-width: 125px;\n}\n\n.min-width-200p {\n    min-width: 200px;\n}\n\n.width-15 {\n    width: 15%;\n}\n\n.width-20 {\n    width: 20%;\n}\n\n.width-30 {\n    width: 30%;\n}\n\n.glyphicon-sm {\n    font-size: 10px;\n}\n\n@media (max-width: 991px) {\n    .width-30 { width: 15%; }\n}\n\n.align-right {\n    text-align: right;\n}\n\n.table>tbody>tr.hover:hover>td, .table>tbody>tr.hover:hover>th {\n    background-color: #f9f9f9;\n}\n\n.table>tbody>tr.highlight>td, .table>tbody>tr.highlight>th {\n    background-color: #fcf8e3;\n    border-color: #fbeed5;\n}\n\n.table>tbody>tr.highlight:hover>td, .table>tbody>tr.highlight:hover>th {\n    background-color: #f6f2dd;\n    border-color: #f5e8ce;\n}\n\n.word-break {\n    word-break: break-all;\n}\n\n/* Statistics widget\n-------------------------------------------------- */\n\n#stats .list-group-item {\n    border-color: #e7e7e7;\n    background-color: #f8f8f8;\n}\n\n#stats a.list-group-item {\n    color: #777;\n}\n\n#stats a.list-group-item:hover,\n#stats a.list-group-item:focus {\n    color: #333;\n}\n\n#stats .list-group-item.active,\n#stats .list-group-item.active:hover,\n#stats .list-group-item.active:focus {\n    color: #555;\n    background-color: #e7e7e7;\n    border-color: #e7e7e7;\n}\n\n.table td.failed-job-details {\n    padding-top: 0;\n    padding-bottom: 0;\n    border-top: none;\n    background-color: #f5f5f5;\n}\n\n.obsolete-data, .obsolete-data a, .obsolete-data pre, .obsolete-data .label {\n    color: #999;\n}\n\n.obsolete-data pre, .obsolete-data .label {\n    background-color: #999;\n    color: #fff;\n}\n\n.obsolete-data .glyphicon-question-sign {\n    font-size: 80%;\n    color: #999;\n}\n\n.stack-trace {\n    padding: 10px;\n    border: none;   \n}\n\n.st-type {\n    font-weight: bold;\n}\n\n.st-param-name {\n    color: #666;\n}\n\n.st-file {\n    color: #999;\n}\n\n.st-method {\n    color: #00008B;\n    font-weight: bold;\n}\n\n.st-line {\n    color: #8B008B;\n}\n\n.width-200 {\n    width: 200px;\n}\n\n.btn-toolbar-top {\n    margin-bottom: 10px;\n}\n\n.paginator .btn {\n    color: #428bca;\n}\n\n.paginator .btn.active {\n    color: #333;\n}\n\n/* Job Snippet styles */\n\n.job-snippet {\n    margin-bottom: 20px;\n    padding: 15px;\n    display: table;\n    width: 100%;\n    -ms-border-radius: 4px;\n    border-radius: 4px;\n    background-color: #f5f5f5;\n}\n\n.job-snippet > * {\n    display: table-cell;\n    vertical-align: top;\n}\n\n.job-snippet-code {\n    vertical-align: top;\n}\n\n.job-snippet-code pre {\n    border: none;\n    margin: 0;\n    background: inherit;\n    padding: 0;\n    -ms-border-radius: 0;\n    border-radius: 0;\n    font-size: 14px;\n}\n\n.job-snippet-code code {\n    display: block; \n    color: black;\n}\n\n.job-snippet-code pre .comment {\n    color: rgb(0, 128, 0);\n}\n\n.job-snippet-code pre .keyword {\n    color: rgb(0, 0, 255);\n}\n\n.job-snippet-code pre .string {\n    color: rgb(163, 21, 21);\n}\n\n.job-snippet-code pre .type {\n    color: rgb(43, 145, 175);\n}\n\n.job-snippet-code pre .xmldoc {\n    color: rgb(128, 128, 128);\n}\n\n.job-snippet-properties pre {\n    background-color: inherit;\n    -webkit-box-shadow: none;\n    -ms-box-shadow: none;\n    padding: 2px 4px;\n    border: none;\n    margin: 0;\n    overflow: hidden;\n}\n\n.job-snippet-properties code {\n    color: rgb(163, 21, 21);\n}\n\n.alert-warning .table td {\n    border-color: rgb(229, 220, 195);\n}\n\n.alert-fixed {\n    border-radius: 0;\n    margin-bottom: -1px;\n    padding-left: 0;\n    padding-right: 0;\n    border-left: none;\n    border-right: none;\n}\n\n.state-card {\n    position: relative;\n    display: block;\n    margin-bottom: 7px;\n    padding: 12px;\n    background-color: #fff;\n    border: 1px solid #e5e5e5;\n    border-radius: 3px;\n}\n\n.state-card-title {\n    margin-bottom: 0;\n}\n\n.state-card-title .pull-right {\n    margin-top: 3px;\n}\n\n.state-card-text {\n    margin-top: 5px;\n    margin-bottom: 0;\n}\n\n.state-card h4 {\n    margin-top: 0;\n}\n\n.state-card-body {\n    padding: 10px;\n    margin: 10px -12px -12px -12px;\n    border-bottom-left-radius: 3px;\n    border-bottom-right-radius: 3px;\n    background-color: #f5f5f5;\n}\n\n.state-card-body dl {\n    margin-top: 5px;\n    margin-bottom: 0;\n}\n\n.state-card-body dd {\n    word-break: break-all;\n}\n\n.state-card-body pre {\n    white-space: pre-wrap;       /* CSS 3 */\n    word-wrap: break-word;       /* Internet Explorer 5.5+ */\n    background: transparent;\n    padding: 0;\n}\n\n.state-card-body .stack-trace {\n    background-color: transparent;\n    padding: 0 20px;\n    margin-bottom: 0px;\n}\n\n.state-card-body .exception-type {\n    margin-top: 0;\n}\n\n.state-card-state-active {\n    border-color: #999;\n}\n\n.state-card-state-active .state-card-title {\n    color: #999;\n}\n\n.state-card-state-active .state-card-body {\n    background-color: #F5F5F5;\n}\n\n.state-card-state-success {\n    border-color: #5cb85c;\n}\n\n.state-card-state-success .state-card-title {\n    color: #5cb85c;\n}\n\n.state-card-state-success .state-card-body {\n    background-color: #EDF7ED;\n}\n\n.state-card-state-danger {\n    border-color: #d9534f;\n}\n\n.state-card-state-danger .state-card-title {\n    color: #d9534f;\n}\n\n.state-card-state-danger .state-card-body {\n    background-color: #FAEBEA;\n}\n\n.state-card-state-warning {\n    border-color: #f0ad4e;\n}\n\n.state-card-state-warning .state-card-title {\n    color: #f0ad4e;\n}\n\n.state-card-state-warning .state-card-body {\n    background-color: #FCEFDC;\n}\n\n.state-card-state-info {\n    border-color: #5bc0de;\n}\n\n.state-card-state-info .state-card-title {\n    color: #5bc0de;\n}\n\n.state-card-state-info .state-card-body {\n    background-color: #E0F3F8;\n}\n\n.state-card-state-inactive {\n    border-color: #777;\n}\n\n.state-card-state-inactive .state-card-title {\n    color: #777;\n}\n\n.state-card-state-inactive .state-card-body {\n    background-color: #ddd;\n}\n\n.label-state-active {\n    background-color: #999;\n}\n\n.label-state-success {\n    background-color: #5cb85c;\n}\n\n.label-state-danger {\n    background-color: #d9534f;\n}\n\n.label-state-warning {\n    background-color: #f0ad4e;\n}\n\n.label-state-info {\n    background-color: #5bc0de;\n}\n\n.label-state-inactive {\n    background-color: #777;\n}\n\n/* Job History styles */\n.job-history {\n    margin-bottom: 10px;\n    opacity: 0.8;\n}\n\n.job-history.job-history-current {\n    opacity: 1.0;\n}\n\n.job-history-heading {\n    padding: 5px 10px;\n    color: #666;\n    -ms-border-top-left-radius: 4px;\n    border-top-left-radius: 4px;\n    -ms-border-top-right-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\n.job-history-body {\n    background-color: #f5f5f5;\n    padding: 10px;\n}\n\n.job-history-title {\n    margin-top: 0;\n    margin-bottom: 2px;\n}\n\n.job-history dl {\n    margin-top: 5px;\n    margin-bottom: 5px;\n}\n\n.job-history .stack-trace {\n    background-color: transparent;\n    padding: 0 20px;\n    margin-bottom: 5px;\n}\n\n.job-history .exception-type {\n    margin-top: 0;\n}\n\n.job-history-current .job-history-heading,\n.job-history-current small {\n    color: white;\n}\n\na.job-method {\n    color: inherit;    \n}\n\n.list-group .glyphicon {\n    top: 2px;\n}\n\n.text-decoration-none {\n    text-decoration: none;\n}\n\n.display-none {\n    display: none;\n}\n\n.display-block {\n    display: block;\n}\n\n.margin-top-2p {\n    margin-top: 2px;\n}\n\n.margin-bottom-0 {\n    margin-bottom: 0;\n}\n\n.margin-bottom-20p {\n    margin-bottom: 20px;\n}\n\n.margin-right-14p {\n    margin-right: 14px;\n}\n\nspan.metric {\n    display: inline-block;\n    min-width: 10px;\n    padding: 2px 6px;\n    font-size: 12px;\n    line-height: 1;\n    text-align: center;\n    white-space: nowrap;\n    vertical-align: baseline;\n    background-color: transparent;\n    border-radius: 10px;\n    border: solid 1px;\n    -webkit-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;\n    -moz-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;\n    -ms-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;\n    -o-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;\n    transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;\n}\n\nspan.metric.highlighted {\n    font-weight: bold;\n    color: #fff!important;\n}\n\nspan.metric-default {\n    color: #777;\n    border-color: #777;\n}\n\nspan.metric-default.highlighted {\n    background-color: #777;\n}\n\n.clickable-metric, .clickable-metric:hover, .clickable-metric:active, .clickable-metric:visited, .clickable-metric:focus {\n    color: inherit;\n    text-decoration: none;\n}\n\ndiv.metric {\n    border: solid 1px transparent;\n    border-radius: 4px;\n    -webkit-box-shadow: 0 1px 1px rgba(0,0,0,.05);\n    box-shadow: 0 1px 1px rgba(0,0,0,.05);\n    margin-bottom: 20px;\n    transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;\n}\n\ndiv.metric .metric-body {\n    padding: 15px 15px 0;\n    font-size: 26px;\n    text-align: center;\n    overflow: hidden;\n    text-overflow: ellipsis;\n}\n\ndiv.metric .metric-description {\n    padding: 0 15px 15px;\n    text-align: center;\n}\n\ndiv.metric.metric-default {\n    border-color: #ddd;\n}\n\ndiv.metric-info,\nspan.metric-info {\n    color: #5bc0de;\n    border-color: #5bc0de;\n}\n\nspan.metric-info.highlighted {\n    background-color: #5bc0de;\n}\n\ndiv.metric-warning,\nspan.metric-warning {\n    color: #f0ad4e;\n    border-color: #f0ad4e;\n}\n\nspan.metric-warning.highlighted {\n    background-color: #f0ad4e;\n}\n\ndiv.metric-success,\nspan.metric-success {\n    color: #5cb85c;\n    border-color: #5cb85c;\n}\n\nspan.metric-success.highlighted {\n    background-color: #5cb85c;\n}\n\ndiv.metric-danger,\nspan.metric-danger {\n    color: #d9534f;\n    border-color: #d9534f;\n}\n\nspan.metric-danger.highlighted {\n    background-color: #d9534f;\n}\n\nspan.metric-null,\ndiv.metric-null {\n    display: none;\n}\n\n@media (min-width: 992px) {\n    #stats {\n        position: fixed;\n        width: 220px\n    }\n}\n\n@media (min-width: 1200px) {\n    #stats {\n        width: 262.5px;\n    }\n}\n\n/* Recurring Jobs Page */\n\n.cron-badge {\n    display: inline-block;\n    max-width: 125px;\n    white-space: normal;\n    word-break: break-word;\n}\n\n/* Full width styles */\n\n@media (min-width: 768px) {\n    .container {\n        width: 100%;\n        padding-left: 25px;\n        padding-right: 25px;\n    }\n}\n\n@media (min-width: 992px) {\n    #wrap > .container > .row > .col-md-3 {\n        width: 251px;\n    }\n\n    #wrap > .container > .row > .col-md-9 {\n        width: calc(100% - 251px);\n    }\n}\n\n@media (min-width: 1200px) {\n    #wrap > .container > .row > .col-md-3 {\n        width: 293px;\n    }\n\n    #wrap > .container > .row > .col-md-9 {\n        width: calc(100% - 293px);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/js/hangfire.js",
    "content": "(function (hangfire) {\n    var changeDatasetColorScheme = function(newColorScheme) {\n        this._chart.data.datasets[0].backgroundColor = COLORS[newColorScheme].failed.backgroundColor;\n        this._chart.data.datasets[0].borderColor = COLORS[newColorScheme].failed.borderColor;\n\n        this._chart.data.datasets[1].backgroundColor = COLORS[newColorScheme].deleted.backgroundColor;\n        this._chart.data.datasets[1].borderColor = COLORS[newColorScheme].deleted.borderColor;\n\n        this._chart.data.datasets[2].backgroundColor = COLORS[newColorScheme].succeeded.backgroundColor;\n        this._chart.data.datasets[2].borderColor = COLORS[newColorScheme].succeeded.borderColor;\n        \n        this._chart.options.scales.xAxes[0].gridLines.color = COLORS[newColorScheme].cartesianColor;\n        this._chart.options.scales.yAxes[0].gridLines.color = COLORS[newColorScheme].cartesianColor;\n\n        this._chart.update();\n    }\n\n    var COLORS = {\n        light: {\n            cartesianColor: '#e5e5e5',\n            failed: {\n                backgroundColor: '#D55251',\n                borderColor: null,\n            },\n            deleted: {\n                backgroundColor: '#919191',\n                borderColor: null,\n            },\n            succeeded: {\n                backgroundColor: '#6FCD6D',\n                borderColor: '#62B35F',\n            },\n        },\n        dark: {\n            cartesianColor: '#5f5f5f',\n            failed: {\n                backgroundColor: 'rgba(215, 58, 74, 0.4)',\n            },\n            deleted: {\n                backgroundColor: 'rgba(204, 204, 204, 0.4)',\n            },\n            succeeded: {\n                backgroundColor: 'rgba(87, 171, 90, 0.4)',\n                borderColor: 'rgba(87, 171, 90, 1)',\n            },\n        },\n    };\n\n    hangfire.config = {\n        pollInterval: $(\"#hangfireConfig\").data(\"pollinterval\"),\n        pollUrl: $(\"#hangfireConfig\").data(\"pollurl\"),\n        locale: document.documentElement.lang,\n        darkMode: $(\"#hangfireConfig\").data(\"darkmode\")\n    };\n\n    var colorScheme = \"light\";\n\n    if (hangfire.config.darkMode) {\n        if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {\n            colorScheme = \"dark\";\n        }\n\n        window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {\n            colorScheme = e.matches ? \"dark\" : \"light\";\n\n            hangfire.page.realtimeGraph.changeDatasetColorScheme(colorScheme);\n            hangfire.page.historyGraph.changeDatasetColorScheme(colorScheme);\n        });\n    }\n\n    hangfire.ErrorAlert = (function () {\n        function ErrorAlert(title, message) {\n            this._errorAlert = $('#errorAlert');\n            this._errorAlertTitle = $('#errorAlertTitle');\n            this._errorAlertMessage = $('#errorAlertMessage');\n            this._title = title;\n            this._message = message;\n        }\n\n        ErrorAlert.prototype.show = function() {\n            this._errorAlertTitle.html(this._title);\n            this._errorAlertMessage.html(this._message);\n\n            $('#errorAlert').show();\n            var alertHeight = $('#errorAlert').outerHeight();\n            $('#errorAlert').hide();\n\n            $('#errorAlert').slideDown(\"fast\");\n            $('.js-page-container').animate({ 'padding-top': alertHeight + 'px' }, \"fast\");\n        };\n\n        return ErrorAlert;\n    })();\n\n    hangfire.Metrics = (function() {\n        function Metrics() {\n            this._metrics = {};\n        }\n\n        Metrics.prototype.addElement = function(name, element) {\n            if (!(name in this._metrics)) {\n                this._metrics[name] = [];\n            }\n\n            this._metrics[name].push(element);\n        };\n\n        Metrics.prototype.getElements = function(name) {\n            if (!(name in this._metrics)) {\n                return [];\n            }\n\n            return this._metrics[name];\n        };\n\n        Metrics.prototype.getNames = function() {\n            var result = [];\n            var metrics = this._metrics;\n\n            for (var name in metrics) {\n                if (metrics.hasOwnProperty(name)) {\n                    result.push(name);\n                }\n            }\n\n            return result;\n        };\n\n        return Metrics;\n    })();\n\n    hangfire.RealtimeGraph = (function() {\n        function RealtimeGraph(element, succeeded, failed, deleted, succeededStr, failedStr, deletedStr, pollInterval) {\n            this._succeeded = succeeded;\n            this._failed = failed;\n            this._deleted = deleted;\n            this._last = Date.now();\n            this._pollInterval = pollInterval;\n            \n            this._chart = new Chart(element, {\n                type: 'line',\n                data: {\n                    datasets: [\n                        { \n                            label: failedStr,  \n                            borderColor: COLORS[colorScheme].failed.borderColor, \n                            backgroundColor: COLORS[colorScheme].failed.backgroundColor, \n                            borderWidth: 2 \n                        },\n                        { \n                            label: deletedStr,\n                            borderColor: COLORS[colorScheme].deleted.borderColor, \n                            backgroundColor: COLORS[colorScheme].deleted.backgroundColor, \n                            borderWidth: 2 \n                        },\n                        { \n                            label: succeededStr,\n                            borderColor: COLORS[colorScheme].succeeded.borderColor, \n                            backgroundColor: COLORS[colorScheme].succeeded.backgroundColor \n                        },\n                    ]\n                },\n                options: {\n                    scales: {\n                        xAxes: [{\n                            gridLines: { color: COLORS[colorScheme].cartesianColor },\n                            type: 'realtime',\n                            realtime: { duration: 60 * 1000, delay: pollInterval },\n                            time: { unit: 'second', tooltipFormat: 'LL LTS', displayFormats: { second: 'LTS', minute: 'LTS' } },\n                            ticks: { maxRotation: 0 }\n                        }],\n                        yAxes: [{\n                            gridLines: { color: COLORS[colorScheme].cartesianColor },\n                            ticks: { beginAtZero: true, precision: 0, min: 0, maxTicksLimit: 6, suggestedMax: 10 }, \n                            stacked: true \n                        }]\n                    },\n                    elements: { line: { tension: 0 }, point: { radius: 0 } },\n                    animation: { duration: 0 },\n                    hover: { animationDuration: 0 },\n                    responsiveAnimationDuration: 0,\n                    legend: { display: false },\n                    tooltips: { mode: 'index', intersect: false }\n                }\n            });\n        }\n\n        RealtimeGraph.prototype.appendHistory = function (statistics) {\n            var newFailed = parseInt(statistics[\"failed:count\"].intValue);\n            var newDeleted = parseInt(statistics[\"deleted:count\"].intValue);\n            var newSucceeded = parseInt(statistics[\"succeeded:count\"].intValue);\n            var now = Date.now();\n\n            if (this._succeeded !== null && this._failed !== null && this._deleted !== null && (now - this._last < this._pollInterval * 2)) {\n                var failed = Math.max(newFailed - this._failed, 0);\n                var deleted = Math.max(newDeleted - this._deleted, 0);\n                var succeeded = Math.max(newSucceeded - this._succeeded, 0);\n\n                this._chart.data.datasets[0].data.push({ x: now, y: failed });\n                this._chart.data.datasets[1].data.push({ x: now, y: deleted });\n                this._chart.data.datasets[2].data.push({ x: now, y: succeeded });\n                \n                this._chart.update();\n            }\n            \n            this._failed = newFailed;\n            this._deleted = newDeleted;\n            this._succeeded = newSucceeded;\n            this._last = now;\n        };\n\n        RealtimeGraph.prototype.changeDatasetColorScheme = changeDatasetColorScheme;\n\n        return RealtimeGraph;\n    })();\n\n    hangfire.HistoryGraph = (function() {\n        function HistoryGraph(element, succeeded, failed, deleted, succeededStr, failedStr, deletedStr) {\n            var timeOptions = $(element).data('period') === 'week'\n                ? { unit: 'day', tooltipFormat: 'LL', displayFormats: { day: 'll' } }\n                : { unit: 'hour', tooltipFormat: 'LLL', displayFormats: { hour: 'LT', day: 'll' } };\n\n            this._chart = new Chart(element, {\n                type: 'line',\n                data: {\n                    datasets: [\n                        { \n                            label: failedStr,  \n                            borderColor: COLORS[colorScheme].failed.borderColor, \n                            backgroundColor: COLORS[colorScheme].failed.backgroundColor, \n                            borderWidth: 2,\n                            data: failed,\n                        },\n                        { \n                            label: deletedStr,\n                            borderColor: COLORS[colorScheme].deleted.borderColor, \n                            backgroundColor: COLORS[colorScheme].deleted.backgroundColor, \n                            borderWidth: 2,\n                            data: deleted,\n                        },\n                        { \n                            label: succeededStr,\n                            borderColor: COLORS[colorScheme].succeeded.borderColor, \n                            backgroundColor: COLORS[colorScheme].succeeded.backgroundColor,\n                            data: succeeded,\n                        },\n                    ]\n                },\n                options: {\n                    scales: {\n                        xAxes: [{ gridLines: { color: COLORS[colorScheme].cartesianColor }, type: 'time', time: timeOptions, ticks: { maxRotation: 0 } }],\n                        yAxes: [{ gridLines: { color: COLORS[colorScheme].cartesianColor }, ticks: { beginAtZero: true, precision: 0, maxTicksLimit: 6 }, stacked: true }]\n                    },\n                    elements: { line: { tension: 0 }, point: { radius: 0 } },\n                    legend: { display: false },\n                    tooltips: { mode: 'index', intersect: false },\n                    plugins: { streaming: false },\n                }\n            });\n\n            HistoryGraph.prototype.changeDatasetColorScheme = changeDatasetColorScheme;\n\n        }\n\n        return HistoryGraph;\n    })();\n\n    hangfire.StatisticsPoller = (function() {\n        function StatisticsPoller(metricsCallback, statisticsUrl, pollInterval) {\n            this._metricsCallback = metricsCallback;\n            this._listeners = [];\n            this._statisticsUrl = statisticsUrl;\n            this._pollInterval = pollInterval;\n            this._timeoutId = null;\n        }\n\n        StatisticsPoller.prototype.start = function () {\n            var self = this;\n\n            var intervalFunc = function() {\n                try {\n                    $.post(self._statisticsUrl, { metrics: self._metricsCallback() })\n                        .done(function (data) {\n                            self._notifyListeners(data);\n                            if (self._timeoutId !== null) {\n                                self._timeoutId = setTimeout(intervalFunc, self._pollInterval);\n                            }\n                        })\n                        .fail(function (xhr) {\n                            var errorAlert = new Hangfire.ErrorAlert(\n                                'Unable to refresh the statistics:',\n                                'the server responded with ' + xhr.status + ' (' + xhr.statusText\n                                + '). Try reloading the page manually, or wait for automatic reload that will happen in a minute.');\n\n                            errorAlert.show();\n                            self._timeoutId = null;\n                            setTimeout(function() { window.location.reload(); }, 60*1000);\n                        });\n                } catch (e) {\n                    console.log(e);\n                }\n            };\n\n            this._timeoutId = setTimeout(intervalFunc, this._pollInterval);\n        };\n\n        StatisticsPoller.prototype.stop = function() {\n            if (this._timeoutId !== null) {\n                clearTimeout(this._timeoutId);\n                this._timeoutId = null;\n            }\n        };\n\n        StatisticsPoller.prototype.addListener = function(listener) {\n            this._listeners.push(listener);\n        };\n\n        StatisticsPoller.prototype._notifyListeners = function(statistics) {\n            var length = this._listeners.length;\n            var i;\n            \n            for (i = 0; i < length; i++) {\n                this._listeners[i](statistics);\n            }\n        };\n\n        return StatisticsPoller;\n    })();\n\n    hangfire.Page = (function() {\n        function Page(config) {\n            this._metrics = new Hangfire.Metrics();\n\n            var self = this;\n            this._poller = new Hangfire.StatisticsPoller(\n                function () { return self._metrics.getNames(); },\n                config.pollUrl,\n                config.pollInterval);\n\n            this._initialize(config.locale);\n\n            this.realtimeGraph = this._createRealtimeGraph('realtimeGraph', config.pollInterval);\n            this.historyGraph = this._createHistoryGraph('historyGraph');\n\n            this._poller.start();\n        };\n\n        Page.prototype._createRealtimeGraph = function(elementId, pollInterval) {\n            var realtimeElement = document.getElementById(elementId);\n            if (realtimeElement) {\n                var succeeded = parseInt($(realtimeElement).data('succeeded'));\n                var failed = parseInt($(realtimeElement).data('failed'));\n                var deleted = parseInt($(realtimeElement).data('deleted'));\n\n                var succeededStr = $(realtimeElement).data('succeeded-string');\n                var failedStr = $(realtimeElement).data('failed-string');\n                var deletedStr = $(realtimeElement).data('deleted-string');\n                var realtimeGraph = new Hangfire.RealtimeGraph(realtimeElement, succeeded, failed, deleted, succeededStr, failedStr, deletedStr, pollInterval);\n\n                this._poller.addListener(function (data) {\n                    realtimeGraph.appendHistory(data);\n                });\n\n                return realtimeGraph;\n            }\n\n            return null;\n        };\n\n        Page.prototype._createHistoryGraph = function(elementId) {\n            var historyElement = document.getElementById(elementId);\n            if (historyElement) {\n                var createSeries = function (obj) {\n                    var series = [];\n                    for (var date in obj) {\n                        if (obj.hasOwnProperty(date)) {\n                            var value = obj[date];\n                            var point = { x: Date.parse(date), y: value };\n                            series.unshift(point);\n                        }\n                    }\n                    return series;\n                };\n\n                var succeeded = createSeries($(historyElement).data(\"succeeded\"));\n                var failed = createSeries($(historyElement).data(\"failed\"));\n                var deleted = createSeries($(historyElement).data(\"deleted\"));\n\n                var succeededStr = $(historyElement).data('succeeded-string');\n                var failedStr = $(historyElement).data('failed-string');\n                var deletedStr = $(historyElement).data('deleted-string');\n\n                return new Hangfire.HistoryGraph(historyElement, succeeded, failed, deleted, succeededStr, failedStr, deletedStr);\n            }\n\n            return null;\n        };\n\n        Page.prototype._initialize = function (locale) {\n            moment.locale(locale);\n            var updateRelativeDates = function () {\n                $('*[data-moment]').each(function () {\n                    var $this = $(this);\n                    var timestamp = $this.data('moment');\n\n                    if (timestamp) {\n                        var time = moment(timestamp, 'X');\n                        $this.html(time.fromNow())\n                            .attr('title', time.format('llll'))\n                            .attr('data-container', 'body');\n                    }\n                });\n\n                $('*[data-moment-title]').each(function () {\n                    var $this = $(this);\n                    var timestamp = $this.data('moment-title');\n\n                    if (timestamp) {\n                        var time = moment(timestamp, 'X');\n                        $this.prop('title', time.format('llll'))\n                            .attr('data-container', 'body');\n                    }\n                });\n\n                $('*[data-moment-local]').each(function () {\n                    var $this = $(this);\n                    var timestamp = $this.data('moment-local');\n\n                    if (timestamp) {\n                        var time = moment(timestamp, 'X');\n                        $this.html(time.format('l LTS'));\n                    }\n                });\n            };\n\n            updateRelativeDates();\n            setInterval(updateRelativeDates, 30 * 1000);\n\n            $('*[title]').tooltip();\n\n            var self = this;\n            $('*[data-metric]').each(function () {\n                var name = $(this).data('metric');\n                self._metrics.addElement(name, this);\n            });\n\n            this._poller.addListener(function (metrics) {\n                for (var name in metrics) {\n                    var elements = self._metrics.getElements(name);\n                    for (var i = 0; i < elements.length; i++) {\n                        var metric = metrics[name];\n                        var metricClass = metric ? \"metric-\" + metric.style : \"metric-null\";\n                        var highlighted = metric && metric.highlighted ? \"highlighted\" : null;\n                        var value = metric ? metric.value : null;\n\n                        $(elements[i])\n                            .text(value)\n                            .closest('.metric')\n                            .removeClass()\n                            .addClass([\"metric\", metricClass, highlighted].join(' '));\n                    }\n                }\n            });\n\n            var csrfHeader = $('meta[name=\"csrf-header\"]').attr('content');\n            var csrfToken = $('meta[name=\"csrf-token\"]').attr('content');\n\n            if (csrfToken && csrfHeader) {\n                var headers = {};\n                headers[csrfHeader] = csrfToken;\n\n                $.ajaxSetup({ headers: headers });\n            }\n\n            $(document).on('click', '*[data-ajax]', function (e) {\n                var $this = $(this);\n                var confirmText = $this.data('confirm');\n\n                if (!confirmText || confirm(confirmText)) {\n                    $this.prop('disabled');\n                    var loadingDelay = setTimeout(function() {\n                        $this.button('loading');\n                    }, 100);\n\n                    $.post($this.data('ajax'), function() {\n                        clearTimeout(loadingDelay);\n                        window.location.reload();\n                    });\n                }\n\n                e.preventDefault();\n            });\n\n            $(document).on('click', '.expander', function (e) {\n                var $expander = $(this),\n                    $expandable = $expander.closest('tr').next().find('.expandable');\n\n                if (!$expandable.is(':visible')) {\n                    $expander.text('Fewer details...');\n                }\n\n\t\t\t\t$expandable.slideToggle(\n\t\t\t\t\t150, \n\t\t\t\t\tfunction() {\n\t\t\t\t\t    if (!$expandable.is(':visible')) {\n\t\t\t\t\t        $expander.text('More details...');\n\t\t\t\t\t    }\n\t\t\t\t\t});\n                e.preventDefault();\n            });\n\n            $('.js-jobs-list').each(function () {\n                var container = this;\n\n                var selectRow = function(row, isSelected) {\n                    var $checkbox = $('.js-jobs-list-checkbox', row);\n                    if ($checkbox.length > 0) {\n                        $checkbox.prop('checked', isSelected);\n                        $(row).toggleClass('highlight', isSelected);\n                    }\n                };\n\n                var toggleRowSelection = function(row) {\n                    var $checkbox = $('.js-jobs-list-checkbox', row);\n                    if ($checkbox.length > 0) {\n                        var isSelected = $checkbox.is(':checked');\n                        selectRow(row, !isSelected);\n                    }\n                };\n\n                var setListState = function (state) {\n                    $('.js-jobs-list-select-all', container)\n                        .prop('checked', state === 'all-selected')\n                        .prop('indeterminate', state === 'some-selected');\n                    \n                    $('.js-jobs-list-command', container)\n                        .prop('disabled', state === 'none-selected');\n                };\n\n                var updateListState = function() {\n                    var selectedRows = $('.js-jobs-list-checkbox', container).map(function() {\n                        return $(this).prop('checked');\n                    }).get();\n\n                    var state = 'none-selected';\n\n                    if (selectedRows.length > 0) {\n                        state = 'some-selected';\n\n                        if ($.inArray(false, selectedRows) === -1) {\n                            state = 'all-selected';\n                        } else if ($.inArray(true, selectedRows) === -1) {\n                            state = 'none-selected';\n                        }\n                    }\n\n                    setListState(state);\n                };\n\n                $(this).on('click', '.js-jobs-list-checkbox', function(e) {\n                    selectRow(\n                        $(this).closest('.js-jobs-list-row').first(),\n                        $(this).is(':checked'));\n\n                    updateListState();\n\n                    e.stopPropagation();\n                });\n\n                $(this).on('click', '.js-jobs-list-row', function (e) {\n                    if ($(e.target).is('a')) return;\n\n                    toggleRowSelection(this);\n                    updateListState();\n                });\n\n                $(this).on('click', '.js-jobs-list-select-all', function() {\n                    var selectRows = $(this).is(':checked');\n\n                    $('.js-jobs-list-row', container).each(function() {\n                        selectRow(this, selectRows);\n                    });\n\n                    updateListState();\n                });\n\n                $(this).on('click', '.js-jobs-list-command', function(e) {\n                    var $this = $(this);\n                    var confirmText = $this.data('confirm');\n\n                    var jobs = $(\"input[name='jobs[]']:checked\", container).map(function() {\n                        return $(this).val();\n                    }).get();\n\n                    if (!confirmText || confirm(confirmText)) {\n                        $this.prop('disabled');\n                        var loadingDelay = setTimeout(function () {\n                            $this.button('loading');\n                        }, 100);\n\n                        $.post($this.data('url'), { 'jobs[]': jobs }, function () {\n                            clearTimeout(loadingDelay);\n                            window.location.reload();\n                        });\n                    }\n\n                    e.preventDefault();\n                });\n\n                updateListState();\n            });\n        };\n\n        return Page;\n    })();\n})(window.Hangfire = window.Hangfire || {});\n\n$(function () {\n    Hangfire.page = new Hangfire.Page(Hangfire.config);\n});\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Resources {\n    using System;\n    using System.Reflection;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"17.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    public class Strings {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Strings() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        public static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"Hangfire.Dashboard.Content.resx.Strings\", typeof(Strings).GetTypeInfo().Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        public static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Don&apos;t worry, continuations are working as expected. But your current job storage does not support some queries required to show this page. Please try to update your storage or wait until the full command set is implemented..\n        /// </summary>\n        public static string AwaitingJobsPage_ContinuationsWarning_Text {\n            get {\n                return ResourceManager.GetString(\"AwaitingJobsPage_ContinuationsWarning_Text\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Continuations are working, but this page can&apos;t be displayed.\n        /// </summary>\n        public static string AwaitingJobsPage_ContinuationsWarning_Title {\n            get {\n                return ResourceManager.GetString(\"AwaitingJobsPage_ContinuationsWarning_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to No jobs found in awaiting state..\n        /// </summary>\n        public static string AwaitingJobsPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"AwaitingJobsPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Options.\n        /// </summary>\n        public static string AwaitingJobsPage_Table_Options {\n            get {\n                return ResourceManager.GetString(\"AwaitingJobsPage_Table_Options\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Parent.\n        /// </summary>\n        public static string AwaitingJobsPage_Table_Parent {\n            get {\n                return ResourceManager.GetString(\"AwaitingJobsPage_Table_Parent\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Since.\n        /// </summary>\n        public static string AwaitingJobsPage_Table_Since {\n            get {\n                return ResourceManager.GetString(\"AwaitingJobsPage_Table_Since\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Awaiting Jobs.\n        /// </summary>\n        public static string AwaitingJobsPage_Title {\n            get {\n                return ResourceManager.GetString(\"AwaitingJobsPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Can not find the target method..\n        /// </summary>\n        public static string Common_CannotFindTargetMethod {\n            get {\n                return ResourceManager.GetString(\"Common_CannotFindTargetMethod\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Condition.\n        /// </summary>\n        public static string Common_Condition {\n            get {\n                return ResourceManager.GetString(\"Common_Condition\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Continuations.\n        /// </summary>\n        public static string Common_Continuations {\n            get {\n                return ResourceManager.GetString(\"Common_Continuations\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Created.\n        /// </summary>\n        public static string Common_Created {\n            get {\n                return ResourceManager.GetString(\"Common_Created\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Delete.\n        /// </summary>\n        public static string Common_Delete {\n            get {\n                return ResourceManager.GetString(\"Common_Delete\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Do you really want to DELETE ALL selected jobs?.\n        /// </summary>\n        public static string Common_DeleteConfirm {\n            get {\n                return ResourceManager.GetString(\"Common_DeleteConfirm\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Delete selected.\n        /// </summary>\n        public static string Common_DeleteSelected {\n            get {\n                return ResourceManager.GetString(\"Common_DeleteSelected\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Deleting....\n        /// </summary>\n        public static string Common_Deleting {\n            get {\n                return ResourceManager.GetString(\"Common_Deleting\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Disabled.\n        /// </summary>\n        public static string Common_Disabled {\n            get {\n                return ResourceManager.GetString(\"Common_Disabled\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Enqueue jobs.\n        /// </summary>\n        public static string Common_EnqueueButton_Text {\n            get {\n                return ResourceManager.GetString(\"Common_EnqueueButton_Text\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Enqueued.\n        /// </summary>\n        public static string Common_Enqueued {\n            get {\n                return ResourceManager.GetString(\"Common_Enqueued\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Enqueueing....\n        /// </summary>\n        public static string Common_Enqueueing {\n            get {\n                return ResourceManager.GetString(\"Common_Enqueueing\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Error.\n        /// </summary>\n        public static string Common_Error {\n            get {\n                return ResourceManager.GetString(\"Common_Error\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Fetched.\n        /// </summary>\n        public static string Common_Fetched {\n            get {\n                return ResourceManager.GetString(\"Common_Fetched\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Id.\n        /// </summary>\n        public static string Common_Id {\n            get {\n                return ResourceManager.GetString(\"Common_Id\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Job.\n        /// </summary>\n        public static string Common_Job {\n            get {\n                return ResourceManager.GetString(\"Common_Job\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Job expired..\n        /// </summary>\n        public static string Common_JobExpired {\n            get {\n                return ResourceManager.GetString(\"Common_JobExpired\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Job&apos;s state has been changed while fetching data..\n        /// </summary>\n        public static string Common_JobStateChanged_Text {\n            get {\n                return ResourceManager.GetString(\"Common_JobStateChanged_Text\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Fewer details....\n        /// </summary>\n        public static string Common_LessDetails {\n            get {\n                return ResourceManager.GetString(\"Common_LessDetails\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to More details....\n        /// </summary>\n        public static string Common_MoreDetails {\n            get {\n                return ResourceManager.GetString(\"Common_MoreDetails\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to No state.\n        /// </summary>\n        public static string Common_NoState {\n            get {\n                return ResourceManager.GetString(\"Common_NoState\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to N/A.\n        /// </summary>\n        public static string Common_NotAvailable {\n            get {\n                return ResourceManager.GetString(\"Common_NotAvailable\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Day.\n        /// </summary>\n        public static string Common_PeriodDay {\n            get {\n                return ResourceManager.GetString(\"Common_PeriodDay\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Week.\n        /// </summary>\n        public static string Common_PeriodWeek {\n            get {\n                return ResourceManager.GetString(\"Common_PeriodWeek\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Reason.\n        /// </summary>\n        public static string Common_Reason {\n            get {\n                return ResourceManager.GetString(\"Common_Reason\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Requeue jobs.\n        /// </summary>\n        public static string Common_RequeueJobs {\n            get {\n                return ResourceManager.GetString(\"Common_RequeueJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Retry.\n        /// </summary>\n        public static string Common_Retry {\n            get {\n                return ResourceManager.GetString(\"Common_Retry\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Server.\n        /// </summary>\n        public static string Common_Server {\n            get {\n                return ResourceManager.GetString(\"Common_Server\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to State.\n        /// </summary>\n        public static string Common_State {\n            get {\n                return ResourceManager.GetString(\"Common_State\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Unknown.\n        /// </summary>\n        public static string Common_Unknown {\n            get {\n                return ResourceManager.GetString(\"Common_Unknown\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to No deleted jobs found..\n        /// </summary>\n        public static string DeletedJobsPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"DeletedJobsPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Deleted.\n        /// </summary>\n        public static string DeletedJobsPage_Table_Deleted {\n            get {\n                return ResourceManager.GetString(\"DeletedJobsPage_Table_Deleted\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Exception.\n        /// </summary>\n        public static string DeletedJobsPage_Table_Exception {\n            get {\n                return ResourceManager.GetString(\"DeletedJobsPage_Table_Exception\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Deleted Jobs.\n        /// </summary>\n        public static string DeletedJobsPage_Title {\n            get {\n                return ResourceManager.GetString(\"DeletedJobsPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to The queue is empty..\n        /// </summary>\n        public static string EnqueuedJobsPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"EnqueuedJobsPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Enqueued Jobs.\n        /// </summary>\n        public static string EnqueuedJobsPage_Title {\n            get {\n                return ResourceManager.GetString(\"EnqueuedJobsPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to &lt;strong&gt;Failed jobs do not become expired&lt;/strong&gt; to allow you to re-queue them without any\n        ///                time pressure. You should re-queue or delete them manually, or apply &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt;\n        ///                attribute to delete them automatically..\n        /// </summary>\n        public static string FailedJobsPage_FailedJobsNotExpire_Warning_Html {\n            get {\n                return ResourceManager.GetString(\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to You have no failed jobs at the moment..\n        /// </summary>\n        public static string FailedJobsPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"FailedJobsPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Failed.\n        /// </summary>\n        public static string FailedJobsPage_Table_Failed {\n            get {\n                return ResourceManager.GetString(\"FailedJobsPage_Table_Failed\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Failed Jobs.\n        /// </summary>\n        public static string FailedJobsPage_Title {\n            get {\n                return ResourceManager.GetString(\"FailedJobsPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to The queue is empty..\n        /// </summary>\n        public static string FetchedJobsPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"FetchedJobsPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Fetched Jobs.\n        /// </summary>\n        public static string FetchedJobsPage_Title {\n            get {\n                return ResourceManager.GetString(\"FetchedJobsPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Failed.\n        /// </summary>\n        public static string HomePage_GraphHover_Failed {\n            get {\n                return ResourceManager.GetString(\"HomePage_GraphHover_Failed\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Succeeded.\n        /// </summary>\n        public static string HomePage_GraphHover_Succeeded {\n            get {\n                return ResourceManager.GetString(\"HomePage_GraphHover_Succeeded\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to History Graph.\n        /// </summary>\n        public static string HomePage_HistoryGraph {\n            get {\n                return ResourceManager.GetString(\"HomePage_HistoryGraph\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Realtime Graph.\n        /// </summary>\n        public static string HomePage_RealtimeGraph {\n            get {\n                return ResourceManager.GetString(\"HomePage_RealtimeGraph\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Overview.\n        /// </summary>\n        public static string HomePage_Title {\n            get {\n                return ResourceManager.GetString(\"HomePage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Created.\n        /// </summary>\n        public static string JobDetailsPage_Created {\n            get {\n                return ResourceManager.GetString(\"JobDetailsPage_Created\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Do you really want to delete this job?.\n        /// </summary>\n        public static string JobDetailsPage_DeleteConfirm {\n            get {\n                return ResourceManager.GetString(\"JobDetailsPage_DeleteConfirm\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to &lt;strong&gt;The job was aborted&lt;/strong&gt; – it is processed by server\n        ///                        &lt;code&gt;{0}&lt;/code&gt; which is not in the \n        ///                        &lt;a href=&quot;{1}&quot;&gt;active servers&lt;/a&gt; list for now.\n        ///                        It will be retried automatically after invisibility timeout, but you can\n        ///                        also re-queue or delete it manually..\n        /// </summary>\n        public static string JobDetailsPage_JobAbortedNotActive_Warning_Html {\n            get {\n                return ResourceManager.GetString(\"JobDetailsPage_JobAbortedNotActive_Warning_Html\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to &lt;strong&gt;Looks like the job was aborted&lt;/strong&gt; – it is processed by server\n        ///                        &lt;code&gt;{0}&lt;/code&gt;, which reported its heartbeat more than 1 minute ago.\n        ///                        It will be retried automatically after invisibility timeout, but you can\n        ///                        also re-queue or delete it manually..\n        /// </summary>\n        public static string JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html {\n            get {\n                return ResourceManager.GetString(\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Background job &apos;{0}&apos; has expired or could not be found on the server..\n        /// </summary>\n        public static string JobDetailsPage_JobExpired {\n            get {\n                return ResourceManager.GetString(\"JobDetailsPage_JobExpired\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to &lt;strong&gt;The job is finished&lt;/strong&gt;.\n        ///                    It will be removed automatically &lt;em&gt;&lt;abbr data-moment=&quot;{0}&quot;&gt;{1}&lt;/abbr&gt;&lt;/em&gt;..\n        /// </summary>\n        public static string JobDetailsPage_JobFinished_Warning_Html {\n            get {\n                return ResourceManager.GetString(\"JobDetailsPage_JobFinished_Warning_Html\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Id.\n        /// </summary>\n        public static string JobDetailsPage_JobId {\n            get {\n                return ResourceManager.GetString(\"JobDetailsPage_JobId\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Parameters.\n        /// </summary>\n        public static string JobDetailsPage_Parameters {\n            get {\n                return ResourceManager.GetString(\"JobDetailsPage_Parameters\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Requeue.\n        /// </summary>\n        public static string JobDetailsPage_Requeue {\n            get {\n                return ResourceManager.GetString(\"JobDetailsPage_Requeue\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to State.\n        /// </summary>\n        public static string JobDetailsPage_State {\n            get {\n                return ResourceManager.GetString(\"JobDetailsPage_State\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Awaiting.\n        /// </summary>\n        public static string JobsSidebarMenu_Awaiting {\n            get {\n                return ResourceManager.GetString(\"JobsSidebarMenu_Awaiting\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Deleted.\n        /// </summary>\n        public static string JobsSidebarMenu_Deleted {\n            get {\n                return ResourceManager.GetString(\"JobsSidebarMenu_Deleted\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Enqueued.\n        /// </summary>\n        public static string JobsSidebarMenu_Enqueued {\n            get {\n                return ResourceManager.GetString(\"JobsSidebarMenu_Enqueued\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Failed.\n        /// </summary>\n        public static string JobsSidebarMenu_Failed {\n            get {\n                return ResourceManager.GetString(\"JobsSidebarMenu_Failed\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Processing.\n        /// </summary>\n        public static string JobsSidebarMenu_Processing {\n            get {\n                return ResourceManager.GetString(\"JobsSidebarMenu_Processing\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Scheduled.\n        /// </summary>\n        public static string JobsSidebarMenu_Scheduled {\n            get {\n                return ResourceManager.GetString(\"JobsSidebarMenu_Scheduled\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Succeeded.\n        /// </summary>\n        public static string JobsSidebarMenu_Succeeded {\n            get {\n                return ResourceManager.GetString(\"JobsSidebarMenu_Succeeded\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Back to site.\n        /// </summary>\n        public static string LayoutPage_Back {\n            get {\n                return ResourceManager.GetString(\"LayoutPage_Back\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Generated: {0}ms.\n        /// </summary>\n        public static string LayoutPage_Footer_Generatedms {\n            get {\n                return ResourceManager.GetString(\"LayoutPage_Footer_Generatedms\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Storage Time:.\n        /// </summary>\n        public static string LayoutPage_Footer_StorageTime {\n            get {\n                return ResourceManager.GetString(\"LayoutPage_Footer_StorageTime\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Application Time:.\n        /// </summary>\n        public static string LayoutPage_Footer_Time {\n            get {\n                return ResourceManager.GetString(\"LayoutPage_Footer_Time\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Application time is out of sync with storage time.\n        /// </summary>\n        public static string LayoutPage_Footer_TimeIsOutOfSync {\n            get {\n                return ResourceManager.GetString(\"LayoutPage_Footer_TimeIsOutOfSync\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Active Connections.\n        /// </summary>\n        public static string Metrics_ActiveConnections {\n            get {\n                return ResourceManager.GetString(\"Metrics_ActiveConnections\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Awaiting.\n        /// </summary>\n        public static string Metrics_AwaitingCount {\n            get {\n                return ResourceManager.GetString(\"Metrics_AwaitingCount\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Deleted Jobs.\n        /// </summary>\n        public static string Metrics_DeletedJobs {\n            get {\n                return ResourceManager.GetString(\"Metrics_DeletedJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Enqueued.\n        /// </summary>\n        public static string Metrics_EnqueuedCountOrNull {\n            get {\n                return ResourceManager.GetString(\"Metrics_EnqueuedCountOrNull\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Enqueued / Queues.\n        /// </summary>\n        public static string Metrics_EnqueuedQueuesCount {\n            get {\n                return ResourceManager.GetString(\"Metrics_EnqueuedQueuesCount\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to {0} failed job(s) found. Retry or delete them manually..\n        /// </summary>\n        public static string Metrics_FailedCountOrNull {\n            get {\n                return ResourceManager.GetString(\"Metrics_FailedCountOrNull\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Failed Jobs.\n        /// </summary>\n        public static string Metrics_FailedJobs {\n            get {\n                return ResourceManager.GetString(\"Metrics_FailedJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Processing Jobs.\n        /// </summary>\n        public static string Metrics_ProcessingJobs {\n            get {\n                return ResourceManager.GetString(\"Metrics_ProcessingJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Recurring Jobs.\n        /// </summary>\n        public static string Metrics_RecurringJobs {\n            get {\n                return ResourceManager.GetString(\"Metrics_RecurringJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Retries.\n        /// </summary>\n        public static string Metrics_Retries {\n            get {\n                return ResourceManager.GetString(\"Metrics_Retries\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Scheduled Jobs.\n        /// </summary>\n        public static string Metrics_ScheduledJobs {\n            get {\n                return ResourceManager.GetString(\"Metrics_ScheduledJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Servers.\n        /// </summary>\n        public static string Metrics_Servers {\n            get {\n                return ResourceManager.GetString(\"Metrics_Servers\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Active Transactions.\n        /// </summary>\n        public static string Metrics_SQLServer_ActiveTransactions {\n            get {\n                return ResourceManager.GetString(\"Metrics_SQLServer_ActiveTransactions\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Data File(s) Used (MB).\n        /// </summary>\n        public static string Metrics_SQLServer_DataFilesSize {\n            get {\n                return ResourceManager.GetString(\"Metrics_SQLServer_DataFilesSize\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Log File(s) Used (MB).\n        /// </summary>\n        public static string Metrics_SQLServer_LogFilesSize {\n            get {\n                return ResourceManager.GetString(\"Metrics_SQLServer_LogFilesSize\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Schema Version.\n        /// </summary>\n        public static string Metrics_SQLServer_SchemaVersion {\n            get {\n                return ResourceManager.GetString(\"Metrics_SQLServer_SchemaVersion\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Succeeded Jobs.\n        /// </summary>\n        public static string Metrics_SucceededJobs {\n            get {\n                return ResourceManager.GetString(\"Metrics_SucceededJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Total Connections.\n        /// </summary>\n        public static string Metrics_TotalConnections {\n            get {\n                return ResourceManager.GetString(\"Metrics_TotalConnections\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Jobs.\n        /// </summary>\n        public static string NavigationMenu_Jobs {\n            get {\n                return ResourceManager.GetString(\"NavigationMenu_Jobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Recurring Jobs.\n        /// </summary>\n        public static string NavigationMenu_RecurringJobs {\n            get {\n                return ResourceManager.GetString(\"NavigationMenu_RecurringJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Retries.\n        /// </summary>\n        public static string NavigationMenu_Retries {\n            get {\n                return ResourceManager.GetString(\"NavigationMenu_Retries\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Servers.\n        /// </summary>\n        public static string NavigationMenu_Servers {\n            get {\n                return ResourceManager.GetString(\"NavigationMenu_Servers\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Next.\n        /// </summary>\n        public static string Paginator_Next {\n            get {\n                return ResourceManager.GetString(\"Paginator_Next\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Prev.\n        /// </summary>\n        public static string Paginator_Prev {\n            get {\n                return ResourceManager.GetString(\"Paginator_Prev\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Total items.\n        /// </summary>\n        public static string Paginator_TotalItems {\n            get {\n                return ResourceManager.GetString(\"Paginator_TotalItems\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Items per page.\n        /// </summary>\n        public static string PerPageSelector_ItemsPerPage {\n            get {\n                return ResourceManager.GetString(\"PerPageSelector_ItemsPerPage\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Looks like the job was aborted.\n        /// </summary>\n        public static string ProcessingJobsPage_Aborted {\n            get {\n                return ResourceManager.GetString(\"ProcessingJobsPage_Aborted\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to No jobs are being processed right now..\n        /// </summary>\n        public static string ProcessingJobsPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"ProcessingJobsPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Started.\n        /// </summary>\n        public static string ProcessingJobsPage_Table_Started {\n            get {\n                return ResourceManager.GetString(\"ProcessingJobsPage_Table_Started\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Processing Jobs.\n        /// </summary>\n        public static string ProcessingJobsPage_Title {\n            get {\n                return ResourceManager.GetString(\"ProcessingJobsPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to No jobs queued..\n        /// </summary>\n        public static string QueuesPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"QueuesPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to No queued jobs found. Try to enqueue a job..\n        /// </summary>\n        public static string QueuesPage_NoQueues {\n            get {\n                return ResourceManager.GetString(\"QueuesPage_NoQueues\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Length.\n        /// </summary>\n        public static string QueuesPage_Table_Length {\n            get {\n                return ResourceManager.GetString(\"QueuesPage_Table_Length\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Next jobs.\n        /// </summary>\n        public static string QueuesPage_Table_NextsJobs {\n            get {\n                return ResourceManager.GetString(\"QueuesPage_Table_NextsJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Queue.\n        /// </summary>\n        public static string QueuesPage_Table_Queue {\n            get {\n                return ResourceManager.GetString(\"QueuesPage_Table_Queue\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Queues.\n        /// </summary>\n        public static string QueuesPage_Title {\n            get {\n                return ResourceManager.GetString(\"QueuesPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Canceled.\n        /// </summary>\n        public static string RecurringJobsPage_Canceled {\n            get {\n                return ResourceManager.GetString(\"RecurringJobsPage_Canceled\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to No recurring jobs found..\n        /// </summary>\n        public static string RecurringJobsPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"RecurringJobsPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Cron expression is invalid or don&apos;t have any occurrences over the next 100 years.\n        /// </summary>\n        public static string RecurringJobsPage_RecurringJobDisabled_Tooltip {\n            get {\n                return ResourceManager.GetString(\"RecurringJobsPage_RecurringJobDisabled_Tooltip\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Cron.\n        /// </summary>\n        public static string RecurringJobsPage_Table_Cron {\n            get {\n                return ResourceManager.GetString(\"RecurringJobsPage_Table_Cron\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Last execution.\n        /// </summary>\n        public static string RecurringJobsPage_Table_LastExecution {\n            get {\n                return ResourceManager.GetString(\"RecurringJobsPage_Table_LastExecution\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Next execution.\n        /// </summary>\n        public static string RecurringJobsPage_Table_NextExecution {\n            get {\n                return ResourceManager.GetString(\"RecurringJobsPage_Table_NextExecution\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Time zone.\n        /// </summary>\n        public static string RecurringJobsPage_Table_TimeZone {\n            get {\n                return ResourceManager.GetString(\"RecurringJobsPage_Table_TimeZone\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Recurring Jobs.\n        /// </summary>\n        public static string RecurringJobsPage_Title {\n            get {\n                return ResourceManager.GetString(\"RecurringJobsPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Triggering....\n        /// </summary>\n        public static string RecurringJobsPage_Triggering {\n            get {\n                return ResourceManager.GetString(\"RecurringJobsPage_Triggering\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Trigger now.\n        /// </summary>\n        public static string RecurringJobsPage_TriggerNow {\n            get {\n                return ResourceManager.GetString(\"RecurringJobsPage_TriggerNow\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to All is OK – you have no retries..\n        /// </summary>\n        public static string RetriesPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"RetriesPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Retries.\n        /// </summary>\n        public static string RetriesPage_Title {\n            get {\n                return ResourceManager.GetString(\"RetriesPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to &lt;h4&gt;Retries are working, but this page can&apos;t be displayed&lt;/h4&gt;\n        ///        &lt;p&gt;\n        ///            Don&apos;t worry, retries are working as expected. Your current job storage does not support\n        ///            some queries required to show this page. Please try to update your storage or wait until\n        ///            the full command set is implemented.\n        ///        &lt;/p&gt;\n        ///        &lt;p&gt;\n        ///            Please go to the &lt;a href=&quot;{0}&quot;&gt;Scheduled jobs&lt;/a&gt; page to see all the\n        ///            scheduled jobs including retries.\n        ///        &lt;/p&gt;.\n        /// </summary>\n        public static string RetriesPage_Warning_Html {\n            get {\n                return ResourceManager.GetString(\"RetriesPage_Warning_Html\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Enqueue now.\n        /// </summary>\n        public static string ScheduledJobsPage_EnqueueNow {\n            get {\n                return ResourceManager.GetString(\"ScheduledJobsPage_EnqueueNow\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to There are no scheduled jobs..\n        /// </summary>\n        public static string ScheduledJobsPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"ScheduledJobsPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Enqueue.\n        /// </summary>\n        public static string ScheduledJobsPage_Table_Enqueue {\n            get {\n                return ResourceManager.GetString(\"ScheduledJobsPage_Table_Enqueue\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Scheduled.\n        /// </summary>\n        public static string ScheduledJobsPage_Table_Scheduled {\n            get {\n                return ResourceManager.GetString(\"ScheduledJobsPage_Table_Scheduled\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Scheduled Jobs.\n        /// </summary>\n        public static string ScheduledJobsPage_Title {\n            get {\n                return ResourceManager.GetString(\"ScheduledJobsPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Active.\n        /// </summary>\n        public static string ServersPage_Active {\n            get {\n                return ResourceManager.GetString(\"ServersPage_Active\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to There are no active servers. Background tasks will not be processed..\n        /// </summary>\n        public static string ServersPage_NoServers {\n            get {\n                return ResourceManager.GetString(\"ServersPage_NoServers\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Some of the servers don&apos;t have heartbeat reported within the last minute and may be aborted. If they don&apos;t report heartbeat in the near future, they will be removed automatically after timeout is exceeded, no manual action is required.\n        ///                    Incomplete background jobs running on those servers will be re-queued automatically, but you can speed up the process by checking the &lt;a href=&quot;{0}&quot;&gt;Processing Jobs&lt;/a&gt; page..\n        /// </summary>\n        public static string ServersPage_Note_Text {\n            get {\n                return ResourceManager.GetString(\"ServersPage_Note_Text\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Aborted servers will be removed automatically.\n        /// </summary>\n        public static string ServersPage_Note_Title {\n            get {\n                return ResourceManager.GetString(\"ServersPage_Note_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Possibly aborted.\n        /// </summary>\n        public static string ServersPage_Possibly_Aborted {\n            get {\n                return ResourceManager.GetString(\"ServersPage_Possibly_Aborted\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Heartbeat.\n        /// </summary>\n        public static string ServersPage_Table_Heartbeat {\n            get {\n                return ResourceManager.GetString(\"ServersPage_Table_Heartbeat\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Name.\n        /// </summary>\n        public static string ServersPage_Table_Name {\n            get {\n                return ResourceManager.GetString(\"ServersPage_Table_Name\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Queues.\n        /// </summary>\n        public static string ServersPage_Table_Queues {\n            get {\n                return ResourceManager.GetString(\"ServersPage_Table_Queues\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Started.\n        /// </summary>\n        public static string ServersPage_Table_Started {\n            get {\n                return ResourceManager.GetString(\"ServersPage_Table_Started\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Workers.\n        /// </summary>\n        public static string ServersPage_Table_Workers {\n            get {\n                return ResourceManager.GetString(\"ServersPage_Table_Workers\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Servers.\n        /// </summary>\n        public static string ServersPage_Title {\n            get {\n                return ResourceManager.GetString(\"ServersPage_Title\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to No succeeded jobs found..\n        /// </summary>\n        public static string SucceededJobsPage_NoJobs {\n            get {\n                return ResourceManager.GetString(\"SucceededJobsPage_NoJobs\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Duration.\n        /// </summary>\n        public static string SucceededJobsPage_Table_Duration {\n            get {\n                return ResourceManager.GetString(\"SucceededJobsPage_Table_Duration\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Latency.\n        /// </summary>\n        public static string SucceededJobsPage_Table_Latency {\n            get {\n                return ResourceManager.GetString(\"SucceededJobsPage_Table_Latency\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Succeeded.\n        /// </summary>\n        public static string SucceededJobsPage_Table_Succeeded {\n            get {\n                return ResourceManager.GetString(\"SucceededJobsPage_Table_Succeeded\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Total Duration.\n        /// </summary>\n        public static string SucceededJobsPage_Table_TotalDuration {\n            get {\n                return ResourceManager.GetString(\"SucceededJobsPage_Table_TotalDuration\", resourceCulture);\n            }\n        }\n        \n        /// <summary>\n        ///   Looks up a localized string similar to Succeeded Jobs.\n        /// </summary>\n        public static string SucceededJobsPage_Title {\n            get {\n                return ResourceManager.GetString(\"SucceededJobsPage_Title\", resourceCulture);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.ca.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>Les continuacions funcionen correctament, però el teu sistema d'emmagatzematge de tasques actual no suporta algunes consultes necessàries per a mostrar aquesta pàgina. Actualitzeu el vostre sistema d'emmagatzematge de tasques o espereu que aquest suporti aquesta funcionalitat</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>Les continuacions funcionen, però aquesta pàgina no es pot mostrar.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No s'han trobat tasques en espera</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>Opcions</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>Pare</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tasques en espera</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>Creat</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>Eliminar</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>¿Esteu segur que voleu ELIMINAR TOTES les tasques seleccionades?</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>Eliminant...</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>Eliminar seleccionats</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>Posar tasques a la cua</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>Posant a la cua...</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>Obtingut</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>Id</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>Tasca</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>Tasca expirada</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>L'estat de la tasca ha canviat mentre s'obtenia informació.</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>Menys detalls</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>Més detalls</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>N/D</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>Dia</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>Setmana</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>Raó</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>Tornar a posar les tasques a la cua</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>Tornar a intentar</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>Servidor</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>Estat</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>Desconegut</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No s'han trobat tasques eliminades.</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>Eliminat</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tasques eliminades</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>La cua esta buida.</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tasques a la cua</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Les tasques fallides no expiren&lt;/strong&gt; per a permetre-us tornar a afegir-les a la cua sense cap pressió. Podeu afegir-les de nou a la cua, eliminar-les manualment o aplicar l'atribut &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt; per a eliminar-les automàticament.</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No hi han tasques amb errors.</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>Amb errors</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tasques amb errors</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>La cua és buida</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tasques obtingudes</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>Gràfic històric</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>Gràfic en temps real</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>Panell</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>Creat</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>¿Esteu segur que voleu eliminar aquesta tasca?</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>Estat</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;La tasca ha estat avortada&lt;/strong&gt; - ha estat processada pel servidor &lt;code&gt;{0}&lt;/code&gt; que no és a la llista de &lt;a href=\"{1}\"&gt;servidors actius&lt;/a&gt; per ara. Serà recuperat automàticament després del període d'invisibilitat, però podeu tornar-la a posar en la cua o eliminar-la manualment.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;La tasca ha estat avortada&lt;/strong&gt; - ha estat processada pel servido &lt;code&gt;{0}&lt;/code&gt; que ha reportat un batec fa més d'un minut. Serà recuperat automàticament després del període d'invisibilitat, però podeu tornar-la a posar en la cua o eliminar-la manualment.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>La tasca amb id '{0}' ha expirat o no s'ha trobat al servidor</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;La tasca ha finalitzat&lt;/strong&gt;. S'eliminarà automàticament en &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>ID Tasca</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>Tornar a posar a la cua</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>Tornar</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>Generat: {0}ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>Hora:</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>Següent</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>Anterior</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>Total elements</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>Elements per pàgina</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>Sembla que la tasca ha estat cancel·lada</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No hi han tasques en procés.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>Iniciat</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tasques en procés</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>No hi han tasques a la cua.</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>No s'han trobat tasques a la cua. Proveu a posar alguna.</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>Longitud</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>Properes tasques</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>Cua</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>Cues</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>Cancel·lat</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No s'han trobat tasques recurrents</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>Darrera execució</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>Propera execució</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>Zona horària</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tasques recurrents</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>Activant...</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>Activar ara</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Tot va bé - no hi ha cap reintent.</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>Reintents</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;Els reintents estan funcionant, però aquesta pàgina no es pot mostrar&lt;/h4&gt;\n        &lt;p&gt;\n            No us preocupeu, els reintents funcionen como haurien, però el teu sistema d'emmagatzematge de tasques actual no suporta algunes consultes necessàries per a mostrar aquesta pàgina. Actualitzeu el vostre sistema d'emmagatzematge o espereu fins que aquest suporti aquesta funcionalitat\n        &lt;/p&gt;\n        &lt;p&gt;\n           Visiteu la pàgina &lt;a href=\"{0}\"&gt;Tasques programades&lt;/a&gt; per a veure totes les tasques programades, inclosos els reintents.\n        &lt;/p&gt; </value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>Posar a la cua ara</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No hi han tasques programades.</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>Posar a la cua</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>Programades</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tasques programades</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>No hi ha cap servidor actiu. Les tasques en segon pla no seran processades.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>Batec</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>Nom</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>Cues</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>Iniciada</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>Treballadors</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>Servidors</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No s'han trobat tasques completades</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>Completades</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>Durada total</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tasques completades</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>A l'espera</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>Eliminat</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>Amb errors</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>Processant</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>Programades</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>Completades</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>Tasques</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>Tasques recurrents</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>Reintents</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>Servidors</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>No es pot trobar el mètode destí</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>A la cua</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>Sense estat</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>A la cua</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>Connexions actives</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>Tasques eliminades</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>Tasques fallides</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>Tasques en procés</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>Tasques recurrents</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>Reintents</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>Tasques programades</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>Servidors</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>Tasques completades</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>Connexions totals</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>Condició</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>Continuacions</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>A l'espera</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>A la cua</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>A la cua / Cues</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>Amb errors</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>Completats</value>\n  </data>\n</root>\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.de.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>Erstellt</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>Keine Sorge, die Ausführungen funktionieren wie erwartet. Die aktuelle Job Speicherung unterstützt jedoch einige Abfragen welche zum Anzeigen dieser Seite erforderlich sind NICHT. Bitte versuchen den Speicher zu aktualisieren oder warten bis der vollständige Ablauf implementiert ist.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>Weitere Ausführungen funktionieren. Nur diese Seite kann nicht angezeigt werden</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Keine Jobs im wartenden Zustand gefunden.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>Optionen</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>Eltern</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Warten auf Jobs</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>Löschen</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Wirklich ALLE ausgewählten Jobs LÖSCHEN?</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>Löschen...</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>Ausgewählte löschen</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>In die Warteschlange</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>Warteschlange abarbeiten...</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>Abgerufen</value>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>ID</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>Job</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>Job abgelaufen.</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>Der Jobstatus wurde beim Abrufen von Daten geändert.</value>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>Weniger Details...</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>Weitere Details...</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>N/A</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>Tag</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>Woche</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>Grund</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>Wieder in die Warteschlange</value>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>Wiederholen</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>Server</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>Status</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>Unbekannt</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Keine gelöschten Jobs gefunden.</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>Gelöscht</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Gelöschte Jobs</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Die Warteschlange ist leer.</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Jobs in Warteschlange</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Zur Zeit gibt es keine fehlgeschlagenen Jobs.</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>Fehler</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Fehlerhafte Jobs</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Die Warteschlange ist leer.</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Jobs abgerufen</value>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>Zeigt für jede CPU-Auslastung ein Verlaufsdiagramm an.</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>Echtzeitdiagramm</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>Übersicht</value>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>Erstellt</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Möchten Sie diesen Job wirklich löschen?</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>Status</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Der Job wurde anscheinend abgebrochen&lt;/strong&gt; - er wird vom Server verarbeitet &lt;code&gt;{0}&lt;/code&gt;, welcher sich vor mehr als 1 Minute gemeldet hat. Es wird nach dem Timeout automatisch wiederholt. Alternativ kann der Job kann auch manuell in die Warteschlange gestellt oder löscht werden.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>Der Warteschlangen Job '{0}' ist abgelaufen oder wurde auf dem Server nicht gefunden.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Der Job ist beendet&lt;/strong&gt;. Er wird automatisch entfernt &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>Job ID</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>Erneut in Warteschlange einreihen</value>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>Zurück zur Website</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>Generiert: {0} ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>Zeit:</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>Weiter</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>Zurück</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>Gesamtzahl der Elemente</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>Elemente pro Seite</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>Der Job wurde anscheinend abgebrochen</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Derzeit werden keine Jobs verarbeitet.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>Gestartet</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Jobs bearbeiten</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Keine Jobs in der Warteschlange.</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>Es wurden keine Jobs in der Warteschlange gefunden. Versuche einen Job in die Warteschlange zu stellen.</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>Länge</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>Nächster Job</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>Warteschlange</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>Warteschlangen</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>Abgebrochen</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Keine wiederkehrenden Jobs gefunden.</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>CRON-Ausdruck</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>Zeitpunkt der letzten Ausführung</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>Nächste Ausführung</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>Zeitzone</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>Wiederholte Jobs</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>Auslöser</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>Jetzt auslösen</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Ausführung abgeschlossen - Wiederholungsversuche notwendig.</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>Erneute Versuche</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>Jetzt in Warteschlange einfügen</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Es sind keine Jobs geplant.</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>In der Warteschlange </value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>Geplant </value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>Geplante Jobs</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>Es sind keine Server aktiv. Warteschlange kann nicht nicht verarbeitet werden.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>Rückmeldung</value>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>Name</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>Warteschlangen</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>Gestartet</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>Worker</value>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>Server</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Es wurden keine erfolgreichen Jobs gefunden.</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>Erfolgreich</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>Gesamtdauer</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>Erfolgreiche Jobs</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>Warten</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>Gelöscht</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>Fehler</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>Verarbeiten...</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>Geplant</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>Erfolgreich</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>Aufträge</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>Wiederholte Aufträge</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>Erneute Versuche</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>Server</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>Die Zielmethode konnte nicht gefunden wurde. </value>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>Zeit in Warteschlange</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>Kein Zustand</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>Zeit in Warteschlange</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>Aktive Verbindungen</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>Gelöschte Jobs</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>Fehlerhafte Aufträge</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>Warehouseverarbeitungsaufträge werden aktualisiert.</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>Wiederholte Aufträge</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>Erneute Versuche</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>Geplante Aufträge</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>Server</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>Erfolgreiche Jobs</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>Verbindungen gesamt</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>Bedingung</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>Ausführungen</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>Warten</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>in Warteschlange</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>Warteschlangen</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0} fehlgeschlagene Jobs gefunden. Manuell wiederholen oder löschen erforderlich.</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>Fehler</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>Erfolgreich</value>\n  </data>\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\n    <value>Deaktiviert</value>\n  </data>\n  <data name=\"RecurringJobsPage_RecurringJobDisabled_Tooltip\" xml:space=\"preserve\">\n    <value>Cron Ausdruck ist ungültig</value>\n  </data>\n  <data name=\"ServersPage_Note_Text\" xml:space=\"preserve\">\n    <value>Einige der Server haben innerhalb der letzten Minute keine Rückmeldung zurück gemeldet und werden möglicherweise abgebrochen. Wenn die Server in naher Zukunft keine Rückmeldung geben, werden sie automatisch entfernt, nachdem das Zeitlimit überschritten wurde. Es sind keine manuellen Maßnahmen erforderlich.</value>\n  </data>\n  <data name=\"ServersPage_Note_Title\" xml:space=\"preserve\">\n    <value>Nicht aktive Server werden automatisch entfernt</value>\n  </data>\n  <data name=\"ServersPage_Active\" xml:space=\"preserve\">\n    <value>Aktiv</value>\n  </data>\n  <data name=\"ServersPage_Possibly_Aborted\" xml:space=\"preserve\">\n    <value>Möglicherweise abgebrochen</value>\n  </data>\n  <data name=\"Common_Error\" xml:space=\"preserve\">\n    <value>Fehler</value>\n  </data>\n  <data name=\"JobDetailsPage_Parameters\" xml:space=\"preserve\">\n    <value>Parameter</value>\n  </data>\n</root>\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.es.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>Las continuaciones funcionan correctamente, pero tu sistema de almacenamiento de tareas actual no soporta algunas consultas necesarias para mostrar esta página. Actualiza tu sistema de almacenamiento o espera hasta que éste soporte esta funcionalidad</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>Las continuaciones funcionan, pero esta página no se puede mostrar.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No se han encontrado tareas en espera</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>Opciones</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>Padre</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tareas en espera</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>Creado</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>Eliminar</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>¿Estás seguro que quieres ELIMINAR TODAS las tareas seleccionadas?</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>Eliminando...</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>Eliminar seleccionados</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>Poner tareas en la cola</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>Poniendo en la cola...</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>Obtenido</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>Id</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>Tarea</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>Tarea expirada</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>El estado de la tarea ha cambiado misentras se obtenia información.</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>Menos detalles</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>Más detalles</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>N/D</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>Día</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>Semana</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>Razón</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>Volver a poner a la cola las tareas</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>Reintentar</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>Servidor</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>Estado</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>Desconocido</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No se han encontrado tareas eliminadas.</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>Eliminado</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tareas eliminadas</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>La cola esta vacía.</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tareas en la cola</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Las tareas fallidas no expiran&lt;/strong&gt; para permititrte volver a añadirlas a la cola sin ninguna presión. Puedes añadirlas de nuevo a la cola, eliminarlas manualmente o aplicar el atributo &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt; para eliminarlas automáticamente.</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No hay tareas con errores.</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>Con errores</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tareas con errores</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>La cola está vacía</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tareas obtenidas</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>Gráfico histórico</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>Gráfico en tiempo real</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>Panel</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>Creado</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>¿Estás seguro que quieres eliminar esta tarea?</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>Estado</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;La tarea ha sido abortada&lt;/strong&gt; - ha sido procesada por el servidor &lt;code&gt;{0}&lt;/code&gt; que no está en la lista de &lt;a href=\"{1}\"&gt;servidores activos&lt;/a&gt; por ahora. Se recuperará automáticamente despues del periodo de invisibilidad, pero puedes volverla a poner en la cola o eliminarla manualmente.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;La tarea ha sido abortada&lt;/strong&gt; - ha sido procesada por el servidor &lt;code&gt;{0}&lt;/code&gt; que ha reportado un latido hace más de un minuto. Se recuperará automáticamente despues del periodo de invisibilidad, pero puedes volverla a poner en la cola o eliminarla manualmente.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>La tarea con id '{0}' ha expirado o no se ha encontrado en el servidor</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;La tarea ha finalizado&lt;/strong&gt;. Se va a eliminar automáticamente en &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>ID Tarea</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>Volver a poner en la cola</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>Volver</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>Generado: {0}ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>Hora:</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>Siguiente</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>Anterior</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>Total elementos</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>Elementos por página</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>Parece que la tarea ha sido cancelada</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No hay tareas en proceso.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>Iniciado</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tareas en proceso</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>No hay tareas en la cola.</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>No se han encontrado tareas en la cola. Prueba a poner alguna.</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>Longitud</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>Próximas tareas</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>Cola</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>Colas</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>Cancelado</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No se han encontrado tareas recurrentes</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>Última ejecución</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>Próxima ejecución</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>Zona horaria</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tareas recurrentes</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>Lanzando...</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>Lanzar ahora</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Todo va bien - no hay ningún reintento.</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>Reintentos</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;Los reintentos están funcionando, pero esta página no se puede mostrar&lt;/h4&gt;\n        &lt;p&gt;\n            No te preocupes, los reintentos fucionan como deberian, pero tu sistema de almacenamiento de tareas actual no soporta algunas consultas necesarias para mostrar esta página. Actualiza tu sistema de almacenamiento o espera hasta que éste soporte esta funcionalidad\n        &lt;/p&gt;\n        &lt;p&gt;\n           Visita la página &lt;a href=\"{0}\"&gt;Tareas programadas&lt;/a&gt; para ver todas las tareas programadas, incluidos los reintentos.\n        &lt;/p&gt; </value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>Poner en la cola ahora</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No hay tareas programadas.</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>Poner en la cola</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>Programadas</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tareas programadas</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>No hay ningún servidor activo. Las tareas en segundo plano no serán procesadas.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>Latido</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>Nombre</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>Colas</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>Iniciada</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>Trabajadores</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>Servidores</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No se han encontrado tareas completadas</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>Completadas</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>Duración total</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tareas completadas</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>En espera</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>Eliminado</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>Con errores</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>Procesando</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>Programadas</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>Completadas</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>Tareas</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>Tareas recurrentes</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>Reintentos</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>Servidores</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>No se puede encontrar el método destino</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>En la cola</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>Sin estado</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>En la cola</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>Conexiones activas</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>Tareas eliminadas</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>Tareas fallidas</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>Tareas en proceso</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>Tareas recurrentes</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>Reintentos</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>Tareas programadas</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>Servidores</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>Tareas completadas</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>Conexiones totales</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>Condición</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>Continuaciones</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>En espera</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>En la cola</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>En la cola / Colas</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>Con errores</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>Completados</value>\n  </data>\n</root>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.fa.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>نگران نباشید. کارهای \"ادامه دار\"، مطابق انتظار در حال اجرا هستند. اما محل ذخیره سازی شما از برخی کوئری های مورد نیاز جهت نمایش این صفحه پشتیبانی نمیکند. لطفاً یا محل ذخیره سازی خود را به روز رسانی کنید یا تا اتمام عملیات منتظر بمانید.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>کارهای \"ادامه دار\" در حال اجرا هستند، اما این صفحه قابلیت نمایش ندارد</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>هیچ کاری در وضعیت انتظار برای اجرا وجود ندارد</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>گزینه ها</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>والد</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>کارهای در حال انتظار</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>ایجاد شده</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>حذف شده</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>آیا از حذف تمامی موارد انتخاب شده مطمئن هستید؟</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>در حال حذف...</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>حذف انتخاب شد</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>وارد کردن کارها به صف</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>در حال ورود به صف</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>دریافت شده</value>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>شناسه</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>کار</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>کارهای منقضی شده</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>در حالی که داده دریافت میشد، وضعیت کار تغییر کرده</value>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>جزئیات کمتر</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>جزئیات بیشتر</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>خارج از دسترس</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>روز</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>هفته</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>علت</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>ورود مجدد به صف</value>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>تلاش مجدد</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>سرویس دهنده</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>وضعیت</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>نامشخص</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>هیچ کار حذف شده ای پیدا نشد</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>حذف شده</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>کارهای حذف شده</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>این کوئری خالی است</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>کارهای وارد شده به صف</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;کارهای ناموفق منقضی نخواهند شد&lt;/strong&gt; تا شما در زمان مناسب مجدداً آنها را اجرا نمایید. باید آنها را بصورت دستی حذف کرده یا دوباره به صف وارد نمایید, یا اینکه اتریبیوت &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt;\n              را جهت حذف خودکار آنها اعمال نمایید.</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>در حال حاضر هیچ کار ناموفقی وجود ندارد</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>ناموفق ها</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>کارهای ناموفق</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>صف خالی است</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>کارهای دریافت شده</value>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>نمودار تاریخچه</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>نمودار لحظه ای</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>داشبورد</value>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>ایجاد شده</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>آیا از حذف این کار اطمینان دارید؟</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>وضعیت</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;کار ناتمام مانده&lt;/strong&gt; – توسط سرویس دهنده \n                        &lt;code&gt;{0}&lt;/code&gt; پردازش شده که اکنون در فهرست\n                        &lt;a href=\"{1}\"&gt;سرویس دهنده های فعال&lt;/a&gt; نیست.\n                      این کار بعد از سپری شدن مدت زمانی دوباره اجرا خواهد شد, اما شما میتوانید آن را بصورت دستی حذف یا مجدداً اجرا نمایید..</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;بنظر این کار ناتمام مانده&lt;/strong&gt; – توسط سرویس دهنده\n                        &lt;code&gt;{0}&lt;/code&gt;, پردازش شده که بیش از یک دقیقه  ی پیش اجرا شده.\n                           این کار بعد از سپری شدن مدت زمانی دوباره اجرا خواهد شد, اما شما میتوانید آن را بصورت دستی حذف یا مجدداً اجرا نمایید.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>کار پس زمینه '{0}' منقضی شد یا در سرور یافت نشد.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;این کار به اتمام رسیده&lt;/strong&gt;.\n                    بصورت خودکار حذف خواهد شد &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>شناسه کار</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>ورود مجدد به صف</value>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>برگشت به سایت</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>ایجاد شد{0}میلی ثانیه</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>زمان:</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>بعدی</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>قبلی</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>کل</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>تعداد در هر صفحه</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>بنظر کار مد نظر شما ناتمام مانده</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>کار هم اکنون در حال اجرا است</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>شروع شده</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>کارهای در حال پردازش</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>هیچ کاری وارد صف نشده</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>هیچ کاری موجود نیست. ابتدا وارد صف کنید</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>طول</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>کار بعدی</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>صف</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>صف ها</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>انصراف داده شده</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>هیچ کار مکرری یافت نشد</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>آخرین اجرا</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>اجرای بعدی</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>منطقه زمانی</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>کارهای مکرر</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>در حال راه اندازی</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>راه اندازی</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>همه چیز صحیح کار میکند- نیاز به اجرای مجدد نیست</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>اجرای مجدد</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;کارهای نیازمند اجرای مجدد دچار هیچ مشکلی نشده اند, اما  این صفحه قابل نمایش نیست&lt;/h4&gt;\n        &lt;p&gt;\n           نگران نشوید، کارهای اجرای مجدد مطابق انتظار در حال اجرا هستند اما برخی از کوئری های مورد نیاز جهت نمایش این صفحه توسط سیستم دخیره سازی شما پشتیبانی نمیشوند. . لطفا سیستم ذخیره سازی خود را به روز رسانی کرده یا تا اتمام دستورات منتظر بمانید.\n        &lt;/p&gt;\n        &lt;p&gt;\n          لطفا به آدرس &lt;a href=\"{0}\"&gt;کارهای زمان بندی شده&lt;/a&gt; رجوع کرده تا همه ی کارهای زمان بندی شده از جمله کارهای نیازمند اجرای مجدد را مشاهده نمایید.\n        &lt;/p&gt;</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>وارد صف کردن</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>هیچ کار زمانبندی شده ای وجود ندارد</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>وارد کردن به صف</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>زمان بندی شده</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>کارهای زمان بندی شده</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>هیچ سرویس دهنده ی فعالی وجود ندارد. کار های پس زمینه پردازش نخواهند شد.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>ضربان</value>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>نام</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>صف</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>شروع شده</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>اجرا کننده ها</value>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>سرویس دهنده ها</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>هیچ کار موفق شده ای وجود ندارد</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>موفق شده</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>زمان کل اجرا</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>کارهای موفق شده</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>در حال انتظار</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>حذف شده</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>ناموفق</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>در حال پردازش</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>زمانبندی شده</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>موفق شده</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>کارها</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>کارهای مکرر</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>اجرای مجدد</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>سرویس دهنده</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>متد مقصد یافت نشد</value>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>وارد شده به صف</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>وضعیت خالی است</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>وارد صف شده</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>اتصال های فعال</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>کارهای حذف شده</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>کارهای ناموفق</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>کارهای در حال پردازش</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>کاراهای مکرر</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>اجرای مجدد</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>کارهای زمانبندی شده</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>سرویس دهنده ها</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>کارهای موفق</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>تعداد کل اتصال ها</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>شرط</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>کارهای \"ادامه دار\"</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>در حال انتظار</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>وارد صف شده</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>وارد صف شده/ صف ها</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0}کار(ها)ی ناموفق found. مجدداً اجرا کرده یا بصورت دستی حذف نمایید.</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>ناموفق ها</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>موفقیت آمیز</value>\n  </data>\n</root>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.fr.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>Ne vous inquiétez pas, les prolongations marchent comme prévu. Votre système de stockage ne supporte pas certaines requêtes nécessaires pour afficher cette page. Merci de mettre à jour votre système de stockage ou d'attendre le support complet de celui-ci.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>Les prolongations fonctionnent, mais cette page ne peut pas être affichée</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Aucune tâche en attente trouvée.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>Options</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>Parent</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tâches en attente</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>Créée</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>Supprimer</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Voulez-vous vraiment SUPPRIMER TOUTES les tâches sélectionnées ?</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>Suppression…</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>Supprimer la sélection</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>Mettre en file d'attente</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>Mise en file d'attente…</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>Récupérées</value>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>Id</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>Tâche</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>Tâche expirée.</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>L'état de la tâche a changé pendant la récupération des données.</value>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>Moins de détails…</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>Plus de détails…</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>N/A</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>Jour</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>Semaine</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>Raison</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>Remettre en file d'attente</value>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>Retenter</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>Serveur</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>État</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>Inconnu</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Aucune tâche supprimée trouvée.</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>Supprimée</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tâches supprimées</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>La file d'attente est vide.</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tâches en file d'attente</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Les tâches échouées n'expirent pas&lt;/strong&gt; pour vous permettre de les remettre en file d'attente sans pression. \nVous devriez les remettre en file d'attente ou les supprimer manuellement, ou appliquer l'attribut &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt; pour les supprimer automatiquement.</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Vous n'avez aucune tâche échouée pour le moment.</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>Échouée</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tâches échouées</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>La file d'attente est vide.</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tâches récupérées</value>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>Historique</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>Graphique temps réel</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>Aperçu</value>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>Créée</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Voulez-vous vraiment supprimer cette tâche ?</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>État</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;La tâche a été annulée&lt;/strong&gt; – elle est en cours de traitement par le serveur\n                        &lt;code&gt;{0}&lt;/code&gt; qui n'est pas dans la liste des\n                        &lt;a href=\"{1}\"&gt;serveurs actifs&lt;/a&gt; pour le moment.\n                        Elle sera retentée automatiquement après un temps d'attente d'invisibilité, mais vous pouvez aussi la remettre en file d'attente ou la supprimer manuellement.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Il semble que la tâche ait été annulée&lt;/strong&gt; – elle est en cours de traitement par le serveur \n                        &lt;code&gt;{0}&lt;/code&gt;, qui a répondu il y a moins d'une minute.\n                        Elle sera retentée automatiquement après un temps d'attente d'invisibilité, mais vous pouvez aussi la remettre en file d'attente ou la supprimer manuellement.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>La tâche '{0}' a expirée ou ne peut pas être trouvée sur le serveur.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;La tâche est terminée&lt;/strong&gt;.\n                    Elle sera retirée automatiquement &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>ID Tâche</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>Remettre en file d'attente</value>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>Retour au site</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>Généré : {0}ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>Temps :</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>Suiv.</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>Prec.</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>Total</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>Éléments par page</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>La tâche semble avoir été annulée</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Aucune tâche en cours actuellement.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>Démarrée</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tâches en cours</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Aucune tâche en file d'attente.</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>Aucune tâche trouvée en file d'attente. Essayez de mettre une tâche en file d'attente.</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>Longueur</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>Prochaines tâches</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>File d'attente</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>Files d'attente</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>Annulée</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Aucune tâche récurrente trouvée.</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>Dernière exécution</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>Prochaine exécution</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>Fuseau horaire</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tâches récurrentes</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>Déclenchement</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>Déclencher maintenant</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Tout est OK — aucune nouvelle tentative.</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>Tentatives</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;Les tentatives fonctionnent, mais cette page ne peut être affichée&lt;/h4&gt;\n        &lt;p&gt;\n            Ne vous inquiétez pas, les tentatives fonctionnent comme prévu. Votre système de stockage ne supporte pas certaines requêtes nécessaires pour afficher cette page. Merci de mettre à jour votre système de stockage ou d'attendre le support complet de celui-ci.\n        &lt;/p&gt;\n        &lt;p&gt;\n            Allez sur la page &lt;a href=\"{0}\"&gt;Tâches programmées&lt;/a&gt; pour voir toutes les tâches programmées (y compris les nouvelles tentatives).\n        &lt;/p&gt;</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>Mettre en file d'attente maintenant</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Aucune tâche programmée.</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>Mettre en file d'attente</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>Programmée</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tâches programmées</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>Aucun serveur actif. Les tâches de fond ne seront pas exécutées.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>Pulsation</value>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>Nom</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>Files d'attente</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>Démarré</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>Unités de travail</value>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>Serveurs</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Aucune tâche réussie trouvée.</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>Réussie</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>Durée totale</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tâches réussies</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>En attente</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>Supprimées</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>Échouées</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>En cours</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>Programmées</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>Réussies</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>Tâches</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>Tâches récurrentes</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>Nouvelles tentatives</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>Serveurs</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>Impossible de trouver la méthode de destination.</value>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>En file d'attente</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>Pas d'état</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>En file d'attente</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>Connexions actives</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>Tâches supprimées</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>Tâches échouées</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>Tâches en cours</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>Tâches récurrentes</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>Nouvelles tentatives</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>Tâches programmées</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>Serveurs</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>Tâches réussies</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>Connexions totales</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>Condition</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>Prolongations</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>En attente</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>En file d'attente</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>En file d'attente / Files d'attente</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0} tâche(s) supprimé(e)s. Réessayez ou supprimez-les manuellement.</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>Échouée</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>Réussies</value>\n  </data>\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\n    <value>Désactivée</value>\n  </data>\n  <data name=\"RecurringJobsPage_RecurringJobDisabled_Tooltip\" xml:space=\"preserve\">\n    <value>L'expression Cron est invalide ou n'a pas d'occurrence dans les 100 prochaines années.</value>\n  </data>\n  <data name=\"ServersPage_Note_Text\" xml:space=\"preserve\">\n    <value>Certains serveurs n'ont pas eu de pulsation dans la dernière minute et pourraient être abandonnés. S'ils ne reportent pas de pulsation dans un futur proche ils seront automatiquement retirés une fois le temps d'attente dépassé, aucune action manuelle n'est requise. Les tâches de fond incomplètes de ces serveurs seront remises en file d'attente automatiquement, mais vous pouvez accélérer le processus en vérifiant la page &lt;a href=\"{0}\"&gt;Tâches en cours&lt;/a&gt;.</value>\n  </data>\n  <data name=\"ServersPage_Note_Title\" xml:space=\"preserve\">\n    <value>Les serveurs abandonnés seront retirés automatiquement</value>\n  </data>\n  <data name=\"ServersPage_Active\" xml:space=\"preserve\">\n    <value>Actif</value>\n  </data>\n  <data name=\"ServersPage_Possibly_Aborted\" xml:space=\"preserve\">\n    <value>Potentiellement abandonné</value>\n  </data>\n  <data name=\"Common_Error\" xml:space=\"preserve\">\n    <value>Erreur</value>\n  </data>\n  <data name=\"Arguments\" xml:space=\"preserve\">\n    <value>Arguments</value>\n  </data>\n</root>\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.nb.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\" id=\"root\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\"/>\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\"/>\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\"/>\n              <xsd:attribute name=\"type\" type=\"xsd:string\"/>\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\"/>\n              <xsd:attribute ref=\"xml:space\"/>\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\"/>\n              <xsd:attribute name=\"name\" type=\"xsd:string\"/>\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\"/>\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\"/>\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\"/>\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\"/>\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\"/>\n              <xsd:attribute ref=\"xml:space\"/>\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\"/>\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\"/>\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>Ikke bekymre deg, fortsettelser fungerer som forventet, men din nåværende jobblagring mangler støtte for noen spørringer som er nødvendige for å vise denne siden. Prøv å oppdatere lagringen eller vent til hele kommandosettet er implementert.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>Fortsettelser fungerer, men denne siden kan ikke vises</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Ingen ventende jobber funnet.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>Alternativer</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>Forelder</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Ventende Jobber</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>Opprettet</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>Slett</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Ønsker du virkelig å slette de valgte jobbene?</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>Sletter...</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>Slett valgte</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>Sett jobber i kø</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>Setter i kø...</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>Hentet</value>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>Id</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>Jobb</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>Jobben har utløpt.</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>Jobbens tilstand har blitt endret under uthenting av data.</value>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>Færre detaljer...</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>Flere detaljer...</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>N/A</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>Dag</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>Uke</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>Årsak</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>Sett jobber tilbake i kø</value>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>Kjør på nytt</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>Server</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>Tilstand</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>Ukjent</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Ingen slettede jobber funnet.</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>Slettet</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Exception\" xml:space=\"preserve\">\n    <value>Feil</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Slettede Jobber</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Køen er tom.</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Jobber i kø</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Mislykkede jobber utløper ikke&lt;/strong&gt; for å la deg å sette dem tilbake i køen uten\n                tidspress. Du bør sette dem tilbake i køen, slette dem manuelt eller bruke &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt;\n                -attributtet for å slette dem automatisk.</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Du har ingen mislykkede jobber for øyeblikket.</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>Mislykket</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Mislykkede Jobber</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Køen er tom.</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Hentede jobber</value>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>Historisk graf</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>Sanntidsgraf</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>Oversikt</value>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>Opprettet</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Ønsker du virkelig å slette denne jobben?</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>Tilstand</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Jobben ble avbrutt&lt;/strong&gt; – den blir behandlet av server\n                        &lt;code&gt;{0}&lt;/code&gt; som ikke er i\n                        &lt;a href=\"{1}\"&gt;aktive servere&lt;/a&gt;-listen nå.\n                        Den blir hentet automatisk etter usynlighetstid, men du kan også sette den tilbake i køen\n                        eller slette den manuelt.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Ser ut som jobben ble avbrutt&lt;/strong&gt; - den blir behandlet av server\n                        &lt;code&gt;{0}&lt;/code&gt; som rapporterte om livstegn for over ett minutt siden.\n                        Den vil bli hentet automatisk etter usynlighetstid, men du kan også\n                        sette den tilbake i køen eller slette den manuelt.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>Bakgrunnsjobben '{0}' har utløpt eller finnes ikke på serveren.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Jobben er utført&lt;/strong&gt;.\n                    Den vil bli fjernet automatisk &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>Jobb ID</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>Sett tilbake i kø</value>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>Tilbake til siden</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>Generert på {0}ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_StorageTime\" xml:space=\"preserve\">\n    <value>Lagringstid:</value>\n  </data>\n  <data name=\"LayoutPage_Footer_TimeIsOutOfSync\" xml:space=\"preserve\">\n    <value>Applikasjonstiden er ikke synkronisert med lagringstiden</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>Tidspunkt:</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>Neste</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>Forrige</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>Totalt antall</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>Antall per side</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>Det ser ut som at jobben ble avbrutt</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Ingen jobber blir behandlet akkurat nå.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>Startet</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Jobber til Behandling</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Ingen jobber har bltt lagt i kø.</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>Ingen jobber har blitt lagt i køen. Prøv å sett en jobb i kø.</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>Lengde</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>Neste jobber</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>Kø</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>Køer</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>Avbrutt</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Ingen gjentakende jobber funnet.</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>Siste kjøring</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>Neste kjøring</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>Tidssone</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>Gjentakende jobber</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>Kjører...</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>Kjør nå</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Alt er OK – ingen jobber har blitt gjenforsøkt.</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>Gjenforsøk</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;Gjenforsøk virker, men denne siden kan ikke vises&lt;/h4&gt;\n        &lt;p&gt;\n            Ikke bekymre deg, gjenforsøk av jobber fungerer som forventet. Din nåværende jobblagring mangler støtte\n            for noen spørringer som er nødvendig for å vise denne siden. Prøv å oppdatere lagringen eller vent til\n            hele kommandosettet er implementert.\n        &lt;/p&gt;\n        &lt;p&gt;\n            Vennligst gå til &lt;a href=\"{0}\"&gt;Planlagte Jobber&lt;/a&gt; for å se alle\n            planlagte jobber, inkludert gjenforsøk.\n        &lt;/p&gt;</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>Sett i kø nå</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Det er ingen planlagte jobber.</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>Sett i kø</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>Planlagt</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>Planlagte Jobber</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>Det er ingen aktive servere. Bakgrunnsjobber vil ikke bli behandlet.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>Siste livstegn</value>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>Navn</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>Køer</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>Startet</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>Arbeidere</value>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>Servere</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Ingen vellykkede jobber funnet.</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>Vellykkede</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>Total Varighet</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Latency\" xml:space=\"preserve\">\n    <value>Ventetid</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Duration\" xml:space=\"preserve\">\n    <value>Varighet</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>Vellykkede Jobber</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>Venter</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>Slettet</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>Mislykket</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>Til Behandling</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>Planlagt</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>Vellykkede</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>Jobber</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>Gjentakende Jobber</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>Gjenforsøk</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>Servere</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>Kan ikke finne målmetoden.</value>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>Satt i kø</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>Ingen tilstand</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>Satt i kø</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>Antall aktive tilkoblinger</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>Slettede Jobber</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>Mislykkede Jobber</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>Jobber til Behandling</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>Gjentakende Jobber</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>Gjenforsøk</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>Planlagte Jobber</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>Servere</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>Vellykkede Jobber</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>Totalt antall tilkoblinger</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>Betingelse</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>Fortsettelser</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>Venter</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>Satt i kø</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>Satt i kø / Køer</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0} mislykkede jobber funnet. Sett dem tilbake i køen eller slett dem manuelt.</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>Mislykket</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>Vellykkede</value>\n  </data>\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\n    <value>Deaktivert</value>\n  </data>\n  <data name=\"RecurringJobsPage_RecurringJobDisabled_Tooltip\" xml:space=\"preserve\">\n    <value>Cron-uttrykket er ugyldig eller har ingen forekomster i løpet av de neste 100 årene</value>\n  </data>\n  <data name=\"ServersPage_Note_Text\" xml:space=\"preserve\">\n    <value>Noen av serverne har ikke rapportert livstegn i løpet av det siste minuttet og kan bli avbrutt. Hvis ikke de rapporterer livstegn i nær fremtid, vil de automatisk bli fjernet etter et tidsavbrudd. Det kreves ingen manuell handling.\n                    Ufullstendige bakgrunnsjobber som kjører på disse serverne vil bli satt i kø på nytt automatisk, men du kan fremskynde prosessen ved å sjekke &lt;a href=\"{0}\"&gt;Jobber til Behandling&lt;/a&gt;.</value>\n  </data>\n  <data name=\"ServersPage_Note_Title\" xml:space=\"preserve\">\n    <value>Avbrutte servere fjernes automatisk</value>\n  </data>\n  <data name=\"ServersPage_Active\" xml:space=\"preserve\">\n    <value>Aktive</value>\n  </data>\n  <data name=\"ServersPage_Possibly_Aborted\" xml:space=\"preserve\">\n    <value>Muligens avbrutt</value>\n  </data>\n  <data name=\"Common_Error\" xml:space=\"preserve\">\n    <value>Feil</value>\n  </data>\n  <data name=\"JobDetailsPage_Parameters\" xml:space=\"preserve\">\n    <value>Parametere</value>\n  </data>\n  <data name=\"Metrics_SQLServer_SchemaVersion\" xml:space=\"preserve\">\n    <value>Skjemaversjon</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Since\" xml:space=\"preserve\">\n    <value>Siden</value>\n  </data>\n  <data name=\"Metrics_SQLServer_ActiveTransactions\" xml:space=\"preserve\">\n    <value>Aktive transaksjoner</value>\n  </data>\n  <data name=\"Metrics_SQLServer_DataFilesSize\" xml:space=\"preserve\">\n    <value>Datafil(er) brukt (MB)</value>\n  </data>\n  <data name=\"Metrics_SQLServer_LogFilesSize\" xml:space=\"preserve\">\n    <value>Loggfil(er) brukt (MB)</value>\n  </data>\n</root>\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.nl.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>Geen paniek, voortzettingen werken zoals verwacht. De huidige opslagmethode voor taken ondersteund niet alle functies die nodig zijn om deze pagina te laten zien. Werk de opslagmethode bij, of wacht tot een complete implementatie gereed is.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>Voortzettingen werken zoals verwacht, maar de pagina kan niet worden getoond.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Geen afwachtende taken gevonden.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>Opties</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>Bovenliggende</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Wachtende taken</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>Aangemaakt</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>Verwijderen</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Weet u zeker dat u alle geselecteeerde taken wilt VERWIJDEREN?</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>Verwijderen...</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>Verwijder selectie</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>Taken inplannen</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>Inplannen...</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>Opgehaald</value>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>Id</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>Taak</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>Taak verlopen.</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>De status van de taak is gewijzigd tijdens het ophalen.</value>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>Minder details...</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>Meer details...</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>n.v.t.</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>Dag</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>Week</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>Reden</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>Taken opnieuw inplannen</value>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>Opnieuw</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>Server</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>Status</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>Onbekend</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Geen verwijderde taken gevonden.</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>Verwijderd</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Verwijderde taken</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>De wachtrij is leeg.</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Ingeplande taken</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Mislukte taken verlopen niet&lt;/strong&gt; zodat ze opnieuw ingepland kunnen worden zonder \n                tijdsdruk. Deze taken dienen handmatig opnieuw ingepland of gewist te worden, of het attribuut &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt;\n                kan worden toegevoegd in code om deze taken automatisch te wissen.</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Geen mislukte taken gevonden.</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>Mislukt</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Mislukte taken</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>De lijst met opgehaalde taken is leeg.</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Opgehaalde taken</value>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>Historische grafiek</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>Realtime grafiek</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>Overzicht</value>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>Aangemaakt</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Wilt u deze taak echt verwijderen?</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>Status</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;De taak is afgebroken&lt;/strong&gt; – de taak is verwerkt door server\n                        &lt;code&gt;{0}&lt;/code&gt; welke op dit moment niet voorkomt in de lijst met\n                        &lt;a href=\"{1}\"&gt;actieve servers&lt;/a&gt;.\n                        De taak wordt automatisch herstart na de time-out die is ingesteld voor niet zichtbare taken, maar de taak kan ook handmatig opnieuw worden gestart, of verwijderd.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Het lijkt erop dat de taak is afgebroken&lt;/strong&gt; – de taak is verwerkt door server\n                        &lt;code&gt;{0}&lt;/code&gt;, welke meer dan 1 minuut geleden voor het laatst activiteit heeft gemeld.\n                        De taak wordt automatisch herstart na de time-out die is ingesteld voor niet zichtbare taken, maar de taak kan ook handmatig opnieuw worden gestart, of verwijderd.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>Taak '{0}' is verlopen, of kan niet worden gevonden op de server.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;De taak is afgerond&lt;/strong&gt;.\n                    De taak zal automatisch worden verwijderd op &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>Taak Id</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>Opnieuw inplannen</value>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>Terug naar de site</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>Gegenereerd: {0}ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>Tijd:</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>Volgende</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>Vorige</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>Aantal items</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>Items per pagina</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>Het lijkt erop dat de taak is afgebroken</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Op dit moment worden er geen taken uitgevoerd.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>Gestart</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Uitvoerende taken</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Geen taken in de wachtrij.</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>Geen taken in de wachtrij gevonden. Probeer een taak uit te voeren.</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>Lengte</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>Volgende taken</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>Wachtrij</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>Wachtrijen</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>Geannuleerd</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Geen herhalende taken gevonden.</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>Laatst uitgevoerd</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>Volgende keer</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>Tijdzone</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>Herhalende taken</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>Uitvoeren...</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>Nu uitvoeren</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Alles in orde – er zijn geen taken om opnieuw te proberen.</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>Opnieuw proberen</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;Opnieuw proberen werkt, maar de pagina kan niet worden getoond&lt;/h4&gt;\n        &lt;p&gt;\n            Geen paniek, taken worden wel opnieuw geprobeerd. De huidige opslagmethode voor taken ondersteund\n            niet alle functies die nodig zijn om deze pagina te laten zien. Werk de opslagmethode bij, of\n            wacht tot een complete implementatie gereed is.\n        &lt;/p&gt;\n        &lt;p&gt;\n            Voor een overzicht van alle ingeplande taken, inclusief taken die opnieuw geprobeerd worden, ga naar de &lt;a href=\"{0}\"&gt;Ingeplande taken&lt;/a&gt;.\n        &lt;/p&gt;</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>Nu in wachtrij plaatsen</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Er zijn geen ingeplande taken.</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>In wachtrij</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>Ingepland</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>Ingeplande taken</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>Er zijn geen actieve servers. Achtergrondtaken worden niet uitgevoerd.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>Hartslag</value>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>Naam</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>Wachtrijen</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>Gestart</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>Werkprocessen</value>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>Servers</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Geen geslaagde taken gevonden.</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>Geslaagd</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>Totale duur</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>Geslaagde taken</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>In afwachting</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>Verwijderd</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>Mislukt</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>Verwerken</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>Ingepland</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>Geslaagd</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>Taken</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>Herhalende taken</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>Opnieuw proberen</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>Servers</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>Kan de doelmethode niet vinden.</value>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>Ingepland</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>Geen status</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>In wachtrij</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>Actieve verbindingen</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>Verwijderde taken</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>Mislukte taken</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>Uitvoerende taken</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>Herhalende taken</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>Opnieuw proberen</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>Ingeplande taken</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>Servers</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>Geslaagde taken</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>Totaal aantal verbindingen</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>Voorwaarde</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>Voorzettingen</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>In afwachting</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>In wachtrij</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>In wachtrij/Wachtrijen</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0} mislukte taken gevonden. Herstart of verwijder deze handmatig.</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>Mislukt</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>Geslaagd</value>\n  </data>\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\n    <value>Uitgeschakeld</value>\n  </data>\n  <data name=\"RecurringJobsPage_RecurringJobDisabled_Tooltip\" xml:space=\"preserve\">\n    <value>Cron expressie is ongeldig, of bevat geen gebeurtenis in de komende 100 jaar</value>\n  </data>\n  <data name=\"ServersPage_Note_Text\" xml:space=\"preserve\">\n    <value>Sommige servers hebben geen hartslag gerapporteerd in de afgelopen minuut, en kunnen zijn gestopt. Als er niet spoedig een hartslag wordt gerapporteerd, worden ze automatisch verwijderd. Een handmatige actie is niet nodig.\n\tNiet afgeronde taken die op deze servers worden uitgevoerd zullen automatisch opnieuw in de wachtrij worden geplaatst, maar dit proces kan worden versneld op de &lt;a href=\"{0}\"&gt;Uitvoerende taken&lt;/a&gt; pagina.</value>\n  </data>\n  <data name=\"ServersPage_Note_Title\" xml:space=\"preserve\">\n    <value>Gestopte servers worden automatisch verwijderd</value>\n  </data>\n  <data name=\"ServersPage_Active\" xml:space=\"preserve\">\n    <value>Actief</value>\n  </data>\n  <data name=\"ServersPage_Possibly_Aborted\" xml:space=\"preserve\">\n    <value>Mogelijk afgebroken</value>\n  </data>\n</root>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.pt-BR.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>As sequências estão a funcionar conforme o esperado. O sistema atual de armazenamento não permite algumas consultas necessárias à apresentação da página. Altere o sistema de armazenamento ou aguarde até que sejam implementadas.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>Continuação funciona, mas esta página não pode ser exibida.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Nenhuma tarefa pendente encontrada.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>Opções</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>Pai</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tarefas em espera</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>Criado</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>Remover</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Tem certeza de que deseja REMOVER TODAS as tarefas selecionadas?</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>Removendo...</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>Remover registros selecionados</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>Colocar tarefa na fila</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>Colocando na fila...</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>Obtidos</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>Id</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>Tarefa</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>Tarefa expirada</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>O status da tarefa foi alterado enquanto as informações estavam sendo obtidas.</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>Menos detalhes...</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>Mais detalhes...</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>Indisponível</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>Dia</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>Semana</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>Motivo</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>Reagendar tarefas</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>Tentar novamente</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>Servidor</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>Estado</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>Desconhecido</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Nenhuma tarefa removida encontrada.</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>Removidas</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tarefas removidas</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>A fila está vazia.</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tarefas enfileiradas</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;As tarefas em falha não expiram&lt;/strong&gt; para permitir que sejam colocadas novamente em fila a qualquer momento. Deve recolocá-las em fila, removê-las manualmente ou definir o atributo &lt;code&gt; AutomaticRetry\n                (OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt; para removê-las automaticamente.</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Não existem tarefas em falha neste momento.</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>Falhas</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tarefas em falha</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>A fila está vazia.</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tarefas obtidas</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>Gráfico histórico</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>Gráfico em tempo real</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>Painel de controle</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>Criado</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Tem certeza de que deseja remover esta tarefa?</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>Estado</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;A tarefa foi cancelada&lt;/strong&gt; – processada pelo servidor\n                        &lt;code&gt;{0}&lt;/code&gt; que não está na lista \n                        &lt;a href=\"{1}\"&gt; de servidores ativos&lt;/a&gt; neste momento.\n                        Será repetida automaticamente depois de um intervalo de invisibilidade,\n                        no entanto pode ser recolocá-la em fila ou removê-la manualmente.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;A tarefa foi abortada&lt;/strong&gt; - foi processada pelo servidor &lt;code&gt;{0}&lt;/code&gt; que relatou uma pulsação mais de um minuto atrás. Ela será recuperado automaticamente após o período de invisibilidade, mas você poderá colocá-lo novamente na fila ou excluí-lo manualmente.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>A tarefa com o Id '{0}' expirou ou não foi encontrada no servidor.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;A tarefa terminou&lt;/strong&gt;.\n                    Será removida automaticamente em &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>Id da Tarefa</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>Colocar novamente na fila</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>Voltar</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>Gerado: {0} ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>Hora:</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>Próxima</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>Anterior</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>Total registros</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>Registros por página</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>A tarefa foi aparentemente cancelada</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Nenhuma tarefa em processamento.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>Iniciadas</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tarefas em processamento</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Sem tarefas na fila.</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>Sem tarefas na fila. Coloque uma tarefa em fila.</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>Quantidade</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>Próximas tarefas</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>Fila</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>Filas</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>Cancelado</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Não foram encontradas tarefas recorrentes</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>Última execução</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>Próxima execução</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>Fuso horário</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tarefas recorrentes</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>Acionando...</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>Executar agora</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Tudo OK – não há reagendamentos.</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>Reagendamentos</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;O reagendamento está a funcionar, mas esta página não consegue ser apresentada&lt;/h4&gt;\n        &lt;p&gt;\n            O reagendamento está a funcionar como esperado. O sistema de armazenamento atual não permite algumas consultas necessárias à apresentação da página.  Altere o sistema de armazenamento ou aguarde que as funcionalidades sejam implementadas.\n        &lt;/p&gt;\n        &lt;p&gt;\n            Acesse &lt;a href=\"{0}\"&gt;Tarefas agendadas&lt;/a&gt; para ver todas as tarefas agendadas e reagendadas.\n        &lt;/p&gt;</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>Colocar em fila agora</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Sem tarefas agendadas.</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>Reagendar</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>Agendado</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tarefas Agendadas</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>Não existem servidores activos. As tarefas não serão executadas.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>Sinal</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>Nome</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>Filas</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>Iniciadas</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>Serviços de execução</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>Servidores</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Nenhuma tarefa concluída foi encontrada.</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>Concluídas</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>Duração total</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tarefas concluídas</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>Em espera</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>Removidas</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>Em falha</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>Processando</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>Agendadas</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>Concluídas</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>Tarefas</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>Tarefas Recorrentes</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>Reagendamentos</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>Servidores</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>Não foi possível encontrar o método alvo.</value>\n    <comment>Fuzzy</comment>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>Enfileiradas</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>Sem estado</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>Enfileiradas</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>Conexões ativas</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>Tarefas Removidas</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>Tarefas em falha</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>Tarefas em processamento</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>Tarefas recorrentes</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>Reagendamentos</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>Tarefas Agendadas</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>Servidores</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>Tarefas com sucesso</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>Total de conexões</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>Condição</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>Continuações</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>Em espera</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>Na fila</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>Enfileiradas / Filas</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>Com falha</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>Concluídos</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Exception\" xml:space=\"preserve\">\n    <value>Exceção</value>\n  </data>\n  <data name=\"LayoutPage_Footer_StorageTime\" xml:space=\"preserve\">\n    <value>Tempo de armazenamento:</value>\n  </data>\n  <data name=\"LayoutPage_Footer_TimeIsOutOfSync\" xml:space=\"preserve\">\n    <value>A hora da aplicação está dessincronizada com a hora do sistema de armazenamento</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Latency\" xml:space=\"preserve\">\n    <value>Latência</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Duration\" xml:space=\"preserve\">\n    <value>Duração</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0} tarefa(s) com erro encontradas. Reagendar ou remover manualmente.</value>\n  </data>\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\n    <value>Inativo</value>\n  </data>\n  <data name=\"JobDetailsPage_Parameters\" xml:space=\"preserve\">\n    <value>Parâmetros</value>\n  </data>\n  <data name=\"Metrics_SQLServer_SchemaVersion\" xml:space=\"preserve\">\n    <value>Versão Schema</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Since\" xml:space=\"preserve\">\n    <value>Desde</value>\n  </data>\n  <data name=\"Metrics_SQLServer_ActiveTransactions\" xml:space=\"preserve\">\n    <value>Transações Ativas</value>\n  </data>\n  <data name=\"Metrics_SQLServer_DataFilesSize\" xml:space=\"preserve\">\n    <value>Arquivo(s) Dados Usados (MB)</value>\n  </data>\n  <data name=\"Metrics_SQLServer_LogFilesSize\" xml:space=\"preserve\">\n    <value>Arquivo(s) Log Usados (MB)</value>\n  </data>\n</root>\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.pt-PT.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<root>\r\n  <!-- \r\n    Microsoft ResX Schema \r\n    \r\n    Version 2.0\r\n    \r\n    The primary goals of this format is to allow a simple XML format \r\n    that is mostly human readable. The generation and parsing of the \r\n    various data types are done through the TypeConverter classes \r\n    associated with the data types.\r\n    \r\n    Example:\r\n    \r\n    ... ado.net/XML headers & schema ...\r\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\r\n    <resheader name=\"version\">2.0</resheader>\r\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\r\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\r\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\r\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\r\n    </data>\r\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\r\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r\n        <comment>This is a comment</comment>\r\n    </data>\r\n                \r\n    There are any number of \"resheader\" rows that contain simple \r\n    name/value pairs.\r\n    \r\n    Each data row contains a name, and value. The row also contains a \r\n    type or mimetype. Type corresponds to a .NET class that support \r\n    text/value conversion through the TypeConverter architecture. \r\n    Classes that don't support this are serialized and stored with the \r\n    mimetype set.\r\n    \r\n    The mimetype is used for serialized objects, and tells the \r\n    ResXResourceReader how to depersist the object. This is currently not \r\n    extensible. For a given mimetype the value must be set accordingly:\r\n    \r\n    Note - application/x-microsoft.net.object.binary.base64 is the format \r\n    that the ResXResourceWriter will generate, however the reader can \r\n    read any of the formats listed below.\r\n    \r\n    mimetype: application/x-microsoft.net.object.binary.base64\r\n    value   : The object must be serialized with \r\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r\n            : and then encoded with base64 encoding.\r\n    \r\n    mimetype: application/x-microsoft.net.object.soap.base64\r\n    value   : The object must be serialized with \r\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r\n            : and then encoded with base64 encoding.\r\n\r\n    mimetype: application/x-microsoft.net.object.bytearray.base64\r\n    value   : The object must be serialized into a byte array \r\n            : using a System.ComponentModel.TypeConverter\r\n            : and then encoded with base64 encoding.\r\n    -->\r\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\r\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\r\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\r\n      <xsd:complexType>\r\n        <xsd:choice maxOccurs=\"unbounded\">\r\n          <xsd:element name=\"metadata\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"assembly\">\r\n            <xsd:complexType>\r\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"data\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"resheader\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n        </xsd:choice>\r\n      </xsd:complexType>\r\n    </xsd:element>\r\n  </xsd:schema>\r\n  <resheader name=\"resmimetype\">\r\n    <value>text/microsoft-resx</value>\r\n  </resheader>\r\n  <resheader name=\"version\">\r\n    <value>2.0</value>\r\n  </resheader>\r\n  <resheader name=\"reader\">\r\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <resheader name=\"writer\">\r\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\r\n    <value>As sequências estão a funcionar conforme o esperado. O sistema atual de armazenamento não permite algumas consultas necessárias à apresentação da página. Altere o sistema de armazenamento ou aguarde até que sejam implementadas.</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\r\n    <value>As sequências estão a funcionar, não é possivel apresentar a página</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Nenhuma tarefa pendente encontrada.</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\r\n    <value>Opções</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\r\n    <value>Pai</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas em espera</value>\r\n  </data>\r\n  <data name=\"Common_Created\" xml:space=\"preserve\">\r\n    <value>Criado</value>\r\n  </data>\r\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\r\n    <value>Remover</value>\r\n  </data>\r\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\r\n    <value>Tem certeza de que deseja REMOVER TODAS as tarefas selecionadas?</value>\r\n  </data>\r\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\r\n    <value>Removendo...</value>\r\n  </data>\r\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\r\n    <value>Remover registos seleccionados</value>\r\n  </data>\r\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\r\n    <value>Colocar tarefas em fila</value>\r\n  </data>\r\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\r\n    <value>A colocar em fila...</value>\r\n  </data>\r\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\r\n    <value>Obtidos</value>\r\n  </data>\r\n  <data name=\"Common_Id\" xml:space=\"preserve\">\r\n    <value>Id</value>\r\n  </data>\r\n  <data name=\"Common_Job\" xml:space=\"preserve\">\r\n    <value>Tarefa</value>\r\n  </data>\r\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\r\n    <value>Tarefa expirada.</value>\r\n  </data>\r\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\r\n    <value>O estado da tarefa foi alterado enquanto os dados estavam sendo obtidos.</value>\r\n  </data>\r\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\r\n    <value>Menos detalhes...</value>\r\n  </data>\r\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\r\n    <value>Mais detalhes...</value>\r\n  </data>\r\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\r\n    <value>Indisponível</value>\r\n  </data>\r\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\r\n    <value>Dia</value>\r\n  </data>\r\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\r\n    <value>Semana</value>\r\n  </data>\r\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\r\n    <value>Motivo</value>\r\n  </data>\r\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\r\n    <value>Reagendar tarefas</value>\r\n  </data>\r\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\r\n    <value>Tentar novamente</value>\r\n  </data>\r\n  <data name=\"Common_Server\" xml:space=\"preserve\">\r\n    <value>Servidor</value>\r\n  </data>\r\n  <data name=\"Common_State\" xml:space=\"preserve\">\r\n    <value>Estado</value>\r\n  </data>\r\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\r\n    <value>Desconhecido</value>\r\n  </data>\r\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Nenhuma tarefa removida encontrada.</value>\r\n  </data>\r\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\r\n    <value>Removidas</value>\r\n  </data>\r\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas removidas</value>\r\n  </data>\r\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>A fila está vazia.</value>\r\n  </data>\r\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas em fila</value>\r\n  </data>\r\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\r\n    <value>&lt;strong&gt;As tarefas com erros não expiram&lt;/strong&gt; para permitir que sejam colocadas novamente em fila a qualquer momento. Deve recolocá-las em fila, removê-las manualmente ou definir o atributo &lt;code&gt; AutomaticRetry\r\n                (OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt; para removê-las automaticamente.</value>\r\n  </data>\r\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Não existem tarefas com erro neste momento.</value>\r\n  </data>\r\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\r\n    <value>Erros</value>\r\n  </data>\r\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas com erro</value>\r\n  </data>\r\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>A fila está vazia.</value>\r\n  </data>\r\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas obtidas</value>\r\n  </data>\r\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\r\n    <value>Gráfico histórico</value>\r\n  </data>\r\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\r\n    <value>Gráfico em tempo real</value>\r\n  </data>\r\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\r\n    <value>Painel de controlo</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\r\n    <value>Criado</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\r\n    <value>Tem certeza de que deseja remover esta tarefa?</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\r\n    <value>Estado</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\r\n    <value>&lt;strong&gt;A tarefa foi cancelada&lt;/strong&gt; – processada pelo servidor\r\n                        &lt;code&gt;{0}&lt;/code&gt; que não está na lista \r\n                        &lt;a href=\"{1}\"&gt; de servidores ativos&lt;/a&gt; neste momento.\r\n                        Será repetida automaticamente depois de um intervalo de invisibilidade,\r\n                        no entanto pode ser recolocá-la em fila ou removê-la manualmente.</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\r\n    <value>&lt;strong&gt;A tarefa foi aparentemente cancelada&lt;/strong&gt; – processada pelo servidor\r\n                        &lt;code&gt;{0}&lt;/code&gt;, que reportou um sinal  há mais de 1 minute.\r\n                        O servidor será restaurado automáticamente após um tempo de invisibilidade,\r\n                        no entanto pode ser recolocado em fila ou removido manualmente.</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\r\n    <value>A tarefa com o Id '{0}' expirou ou não foi encontrada no servidor.</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\r\n    <value>&lt;strong&gt;A tarefa terminou&lt;/strong&gt;.\r\n                    Será removida automaticamente em &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\r\n    <value>Id da Tarefa</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\r\n    <value>Colocar novamente na fila</value>\r\n  </data>\r\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\r\n    <value>Voltar</value>\r\n  </data>\r\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\r\n    <value>Gerado: {0}ms</value>\r\n  </data>\r\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\r\n    <value>Hora:</value>\r\n  </data>\r\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\r\n    <value>Próxima</value>\r\n  </data>\r\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\r\n    <value>Anterior</value>\r\n  </data>\r\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\r\n    <value>Total registos</value>\r\n  </data>\r\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\r\n    <value>Registos por página</value>\r\n  </data>\r\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\r\n    <value>A tarefa foi aparentamente cancelada</value>\r\n  </data>\r\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Nenhuma tarefa em processamento.</value>\r\n  </data>\r\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\r\n    <value>Iniciadas</value>\r\n  </data>\r\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas em processamento</value>\r\n  </data>\r\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Sem tarefas em fila.</value>\r\n  </data>\r\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\r\n    <value>Sem tarefas na fila. Coloque uma tarefa em fila.</value>\r\n  </data>\r\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\r\n    <value>Quantidade</value>\r\n  </data>\r\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\r\n    <value>Próximas tarefas</value>\r\n  </data>\r\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\r\n    <value>Fila</value>\r\n  </data>\r\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\r\n    <value>Filas</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\r\n    <value>Cancelado</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Não foram encontradas tarefas recorrentes.</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\r\n    <value>Cron</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\r\n    <value>Última execução</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\r\n    <value>Próxima execução</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\r\n    <value>Fuso horário</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas recorrentes</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\r\n    <value>Acionando...</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\r\n    <value>Executar agora</value>\r\n  </data>\r\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Tudo OK – não há reagendamentos.</value>\r\n  </data>\r\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\r\n    <value>Reagendamentos</value>\r\n  </data>\r\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\r\n    <value>&lt;h4&gt;O reagendamento está a funcionar, mas esta página não consegue ser apresentada&lt;/h4&gt;\r\n        &lt;p&gt;\r\n            O reagendamento está a funcionar como esperado. O sistema de armazenamento atual não permite algumas consultas necessárias à apresentação da página.  Altere o sistema de armazenamento ou aguarde que as funcionalidades sejam implementadas.\r\n        &lt;/p&gt;\r\n        &lt;p&gt;\r\n            Aceda a &lt;a href=\"{0}\"&gt;Tarefas agendadas&lt;/a&gt; para ver todas as tarefas agendadas e reagendadas.\r\n        &lt;/p&gt;</value>\r\n  </data>\r\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\r\n    <value>Colocar em fila agora</value>\r\n  </data>\r\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Sem tarefas agendadas.</value>\r\n  </data>\r\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\r\n    <value>Reagendar</value>\r\n  </data>\r\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\r\n    <value>Agendado</value>\r\n  </data>\r\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas Agendadas</value>\r\n  </data>\r\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\r\n    <value>Não existem servidores activos. As tarefas não serão executadas.</value>\r\n  </data>\r\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\r\n    <value>Sinal</value>\r\n  </data>\r\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\r\n    <value>Nome</value>\r\n  </data>\r\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\r\n    <value>Filas</value>\r\n  </data>\r\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\r\n    <value>Iniciadas</value>\r\n  </data>\r\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\r\n    <value>Workers</value>\r\n  </data>\r\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\r\n    <value>Servidores</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Nenhuma tarefa concluída encontrada.</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\r\n    <value>Concluídas</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\r\n    <value>Duração</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas concluídas</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\r\n    <value>Em espera</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\r\n    <value>Removidas</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\r\n    <value>Com Erro</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\r\n    <value>Processando</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\r\n    <value>Agendadas</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\r\n    <value>Concluídas</value>\r\n  </data>\r\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\r\n    <value>Tarefas</value>\r\n  </data>\r\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas Recorrentes</value>\r\n  </data>\r\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\r\n    <value>Reagendamentos</value>\r\n  </data>\r\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\r\n    <value>Servidores</value>\r\n  </data>\r\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\r\n    <value>Não foi possível encontrar o método alvo.</value>\r\n  </data>\r\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\r\n    <value>Em Fila</value>\r\n  </data>\r\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\r\n    <value>Sem estado</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\r\n    <value>Em Fila</value>\r\n  </data>\r\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\r\n    <value>Ligações activas</value>\r\n  </data>\r\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas Removidas</value>\r\n  </data>\r\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas com erro</value>\r\n  </data>\r\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas em processamento</value>\r\n  </data>\r\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas recorrentes</value>\r\n  </data>\r\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\r\n    <value>Reagendamentos</value>\r\n  </data>\r\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas Agendadas</value>\r\n  </data>\r\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\r\n    <value>Servidores</value>\r\n  </data>\r\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas com sucesso</value>\r\n  </data>\r\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\r\n    <value>Total de Ligações</value>\r\n  </data>\r\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\r\n    <value>Condição</value>\r\n  </data>\r\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\r\n    <value>Sequências</value>\r\n  </data>\r\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\r\n    <value>Em espera</value>\r\n  </data>\r\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\r\n    <value>Em fila</value>\r\n  </data>\r\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\r\n    <value>Em fila / Filas</value>\r\n  </data>\r\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\r\n    <value>{0} tarefa(s) com erro encontradas. Reagendar ou remover manualmente.</value>\r\n  </data>\r\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\r\n    <value>Com erro</value>\r\n  </data>\r\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\r\n    <value>Com sucesso</value>\r\n  </data>\r\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\r\n    <value>Inactivo</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_RecurringJobDisabled_Tooltip\" xml:space=\"preserve\">\r\n    <value>A expressão Cron é inválida ou não tem nenhuma ocorência nos próximos 100 anos</value>\r\n  </data>\r\n  <data name=\"ServersPage_Note_Text\" xml:space=\"preserve\">\r\n    <value>Alguns servidores não reportaram nehum sinal no último minuto e podem ser cancelados. Se não reportarem nenhum sinal brevemente serão cancelados assim que o tempo limite seja ultrapassado, não é necessária nenhuma acção manual.\r\n                    Tarefas incompletas nesses servidores serão repetidas automaticamente, no entanto o processo pode ser acelarado acedendo e verificando a seguinte página &lt;a href=\"{0}\"&gt;Tarefas em Processamento&lt;/a&gt;.</value>\r\n  </data>\r\n  <data name=\"ServersPage_Note_Title\" xml:space=\"preserve\">\r\n    <value>Servidores cancelados serão removidos automaticamente</value>\r\n  </data>\r\n  <data name=\"ServersPage_Active\" xml:space=\"preserve\">\r\n    <value>Activo</value>\r\n  </data>\r\n  <data name=\"ServersPage_Possibly_Aborted\" xml:space=\"preserve\">\r\n    <value>Possivelmente cancelados</value>\r\n  </data>\r\n  <data name=\"Common_Error\" xml:space=\"preserve\">\r\n    <value>Erro</value>\r\n  </data>\r\n  <data name=\"DeletedJobsPage_Table_Exception\" xml:space=\"preserve\">\r\n    <value>Exceção</value>\r\n  </data>\r\n  <data name=\"LayoutPage_Footer_StorageTime\" xml:space=\"preserve\">\r\n    <value>Tempo de armazenamento:</value>\r\n  </data>\r\n  <data name=\"LayoutPage_Footer_TimeIsOutOfSync\" xml:space=\"preserve\">\r\n    <value>A hora da aplicação está dessincronizada com a hora do sistema de armazenamento</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_Table_Latency\" xml:space=\"preserve\">\r\n    <value>Latência</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_Table_Duration\" xml:space=\"preserve\">\r\n    <value>Duração</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_Parameters\" xml:space=\"preserve\">\r\n    <value>Parâmetros</value>\r\n  </data>\r\n  <data name=\"Metrics_SQLServer_SchemaVersion\" xml:space=\"preserve\">\r\n    <value>Versão Schema</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_Table_Since\" xml:space=\"preserve\">\r\n    <value>Desde</value>\r\n  </data>\r\n  <data name=\"Metrics_SQLServer_ActiveTransactions\" xml:space=\"preserve\">\r\n    <value>Transações Ativas</value>\r\n  </data>\r\n  <data name=\"Metrics_SQLServer_DataFilesSize\" xml:space=\"preserve\">\r\n    <value>Ficheiro(s) Dados Usadas (MB)</value>\r\n  </data>\r\n  <data name=\"Metrics_SQLServer_LogFilesSize\" xml:space=\"preserve\">\r\n    <value>Ficheiro(s) Log Usados (MB)</value>\r\n  </data>\r\n</root>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.pt.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<root>\r\n  <!-- \r\n    Microsoft ResX Schema \r\n    \r\n    Version 2.0\r\n    \r\n    The primary goals of this format is to allow a simple XML format \r\n    that is mostly human readable. The generation and parsing of the \r\n    various data types are done through the TypeConverter classes \r\n    associated with the data types.\r\n    \r\n    Example:\r\n    \r\n    ... ado.net/XML headers & schema ...\r\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\r\n    <resheader name=\"version\">2.0</resheader>\r\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\r\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\r\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\r\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\r\n    </data>\r\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\r\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r\n        <comment>This is a comment</comment>\r\n    </data>\r\n                \r\n    There are any number of \"resheader\" rows that contain simple \r\n    name/value pairs.\r\n    \r\n    Each data row contains a name, and value. The row also contains a \r\n    type or mimetype. Type corresponds to a .NET class that support \r\n    text/value conversion through the TypeConverter architecture. \r\n    Classes that don't support this are serialized and stored with the \r\n    mimetype set.\r\n    \r\n    The mimetype is used for serialized objects, and tells the \r\n    ResXResourceReader how to depersist the object. This is currently not \r\n    extensible. For a given mimetype the value must be set accordingly:\r\n    \r\n    Note - application/x-microsoft.net.object.binary.base64 is the format \r\n    that the ResXResourceWriter will generate, however the reader can \r\n    read any of the formats listed below.\r\n    \r\n    mimetype: application/x-microsoft.net.object.binary.base64\r\n    value   : The object must be serialized with \r\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r\n            : and then encoded with base64 encoding.\r\n    \r\n    mimetype: application/x-microsoft.net.object.soap.base64\r\n    value   : The object must be serialized with \r\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r\n            : and then encoded with base64 encoding.\r\n\r\n    mimetype: application/x-microsoft.net.object.bytearray.base64\r\n    value   : The object must be serialized into a byte array \r\n            : using a System.ComponentModel.TypeConverter\r\n            : and then encoded with base64 encoding.\r\n    -->\r\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\r\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\r\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\r\n      <xsd:complexType>\r\n        <xsd:choice maxOccurs=\"unbounded\">\r\n          <xsd:element name=\"metadata\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"assembly\">\r\n            <xsd:complexType>\r\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"data\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\r\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\r\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\r\n              <xsd:attribute ref=\"xml:space\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n          <xsd:element name=\"resheader\">\r\n            <xsd:complexType>\r\n              <xsd:sequence>\r\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\r\n              </xsd:sequence>\r\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\r\n            </xsd:complexType>\r\n          </xsd:element>\r\n        </xsd:choice>\r\n      </xsd:complexType>\r\n    </xsd:element>\r\n  </xsd:schema>\r\n  <resheader name=\"resmimetype\">\r\n    <value>text/microsoft-resx</value>\r\n  </resheader>\r\n  <resheader name=\"version\">\r\n    <value>2.0</value>\r\n  </resheader>\r\n  <resheader name=\"reader\">\r\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <resheader name=\"writer\">\r\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r\n  </resheader>\r\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\r\n    <value>As sequências estão a funcionar conforme o esperado. O sistema atual de armazenamento não permite algumas consultas necessárias à apresentação da página. Altere o sistema de armazenamento ou aguarde até que sejam implementadas.</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\r\n    <value>As sequências estão a funcionar, mas não é possivel apresentar a página</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Nenhuma tarefa pendente encontrada.</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\r\n    <value>Opções</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\r\n    <value>Pai</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas em espera</value>\r\n  </data>\r\n  <data name=\"Common_Created\" xml:space=\"preserve\">\r\n    <value>Criado</value>\r\n  </data>\r\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\r\n    <value>Remover</value>\r\n  </data>\r\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\r\n    <value>Tem certeza de que deseja REMOVER TODAS as tarefas selecionadas?</value>\r\n  </data>\r\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\r\n    <value>Removendo...</value>\r\n  </data>\r\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\r\n    <value>Remover registos seleccionados</value>\r\n  </data>\r\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\r\n    <value>Colocar tarefas em fila</value>\r\n  </data>\r\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\r\n    <value>A colocar em fila...</value>\r\n  </data>\r\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\r\n    <value>Obtidos</value>\r\n  </data>\r\n  <data name=\"Common_Id\" xml:space=\"preserve\">\r\n    <value>Id</value>\r\n  </data>\r\n  <data name=\"Common_Job\" xml:space=\"preserve\">\r\n    <value>Tarefa</value>\r\n  </data>\r\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\r\n    <value>Tarefa expirada.</value>\r\n  </data>\r\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\r\n    <value>O estado da tarefa foi alterado enquanto os dados estavam sendo obtidos.</value>\r\n  </data>\r\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\r\n    <value>Menos detalhes...</value>\r\n  </data>\r\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\r\n    <value>Mais detalhes...</value>\r\n  </data>\r\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\r\n    <value>Indisponível</value>\r\n  </data>\r\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\r\n    <value>Dia</value>\r\n  </data>\r\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\r\n    <value>Semana</value>\r\n  </data>\r\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\r\n    <value>Motivo</value>\r\n  </data>\r\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\r\n    <value>Reagendar tarefas</value>\r\n  </data>\r\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\r\n    <value>Tentar novamente</value>\r\n  </data>\r\n  <data name=\"Common_Server\" xml:space=\"preserve\">\r\n    <value>Servidor</value>\r\n  </data>\r\n  <data name=\"Common_State\" xml:space=\"preserve\">\r\n    <value>Estado</value>\r\n  </data>\r\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\r\n    <value>Desconhecido</value>\r\n  </data>\r\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Nenhuma tarefa removida encontrada.</value>\r\n  </data>\r\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\r\n    <value>Removidas</value>\r\n  </data>\r\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas removidas</value>\r\n  </data>\r\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>A fila está vazia.</value>\r\n  </data>\r\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas em fila</value>\r\n  </data>\r\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\r\n    <value>&lt;strong&gt;As tarefas com erros não expiram&lt;/strong&gt; para permitir que sejam colocadas novamente em fila a qualquer momento. Deve recolocá-las em fila, removê-las manualmente ou definir o atributo &lt;code&gt; AutomaticRetry\r\n                (OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt; para removê-las automaticamente.</value>\r\n  </data>\r\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Não existem tarefas com erro neste momento.</value>\r\n  </data>\r\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\r\n    <value>Erros</value>\r\n  </data>\r\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas com erro</value>\r\n  </data>\r\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>A fila está vazia.</value>\r\n  </data>\r\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas obtidas</value>\r\n  </data>\r\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\r\n    <value>Gráfico histórico</value>\r\n  </data>\r\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\r\n    <value>Gráfico em tempo real</value>\r\n  </data>\r\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\r\n    <value>Painel de controlo</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\r\n    <value>Criado</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\r\n    <value>Tem certeza de que deseja remover esta tarefa?</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\r\n    <value>Estado</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\r\n    <value>&lt;strong&gt;A tarefa foi cancelada&lt;/strong&gt; – processada pelo servidor\r\n                        &lt;code&gt;{0}&lt;/code&gt; que não está na lista \r\n                        &lt;a href=\"{1}\"&gt; de servidores ativos&lt;/a&gt; neste momento.\r\n                        Será repetida automaticamente depois de um intervalo de invisibilidade,\r\n                        no entanto pode ser recolocá-la em fila ou removê-la manualmente.</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\r\n    <value>&lt;strong&gt;A tarefa foi aparentemente cancelada&lt;/strong&gt; – processada pelo servidor\r\n                        &lt;code&gt;{0}&lt;/code&gt;, que reportou um sinal  há mais de 1 minute.\r\n                        O servidor será restaurado automáticamente após um tempo de invisibilidade,\r\n                        no entanto pode ser recolocado em fila ou removido manualmente.</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\r\n    <value>A tarefa com o Id '{0}' expirou ou não foi encontrada no servidor.</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\r\n    <value>&lt;strong&gt;A tarefa terminou&lt;/strong&gt;.\r\n                    Será removida automaticamente em &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\r\n    <value>Id da Tarefa</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\r\n    <value>Colocar novamente na fila</value>\r\n  </data>\r\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\r\n    <value>Voltar</value>\r\n  </data>\r\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\r\n    <value>Gerado: {0}ms</value>\r\n  </data>\r\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\r\n    <value>Hora:</value>\r\n  </data>\r\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\r\n    <value>Próxima</value>\r\n  </data>\r\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\r\n    <value>Anterior</value>\r\n  </data>\r\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\r\n    <value>Total registos</value>\r\n  </data>\r\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\r\n    <value>Registos por página</value>\r\n  </data>\r\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\r\n    <value>A tarefa foi aparentamente cancelada</value>\r\n  </data>\r\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Nenhuma tarefa em processamento.</value>\r\n  </data>\r\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\r\n    <value>Iniciadas</value>\r\n  </data>\r\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas em processamento</value>\r\n  </data>\r\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Sem tarefas na fila.</value>\r\n  </data>\r\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\r\n    <value>Sem tarefas na fila. Coloque uma tarefa em fila.</value>\r\n  </data>\r\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\r\n    <value>Quantidade</value>\r\n  </data>\r\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\r\n    <value>Próximas tarefas</value>\r\n  </data>\r\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\r\n    <value>Fila</value>\r\n  </data>\r\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\r\n    <value>Filas</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\r\n    <value>Cancelado</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Não foram encontradas tarefas recorrentes.</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\r\n    <value>Cron</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\r\n    <value>Última execução</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\r\n    <value>Próxima execução</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\r\n    <value>Fuso horário</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas recorrentes</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\r\n    <value>Acionando...</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\r\n    <value>Executar agora</value>\r\n  </data>\r\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Tudo OK – não há reagendamentos.</value>\r\n  </data>\r\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\r\n    <value>Reagendamentos</value>\r\n  </data>\r\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\r\n    <value>&lt;h4&gt;O reagendamento está a funcionar, mas esta página não consegue ser apresentada&lt;/h4&gt;\r\n        &lt;p&gt;\r\n            O reagendamento está a funcionar como esperado. O sistema de armazenamento atual não permite algumas consultas necessárias à apresentação da página.  Altere o sistema de armazenamento ou aguarde que as funcionalidades sejam implementadas.\r\n        &lt;/p&gt;\r\n        &lt;p&gt;\r\n            Aceda a &lt;a href=\"{0}\"&gt;Tarefas agendadas&lt;/a&gt; para ver todas as tarefas agendadas e reagendadas.\r\n        &lt;/p&gt;</value>\r\n  </data>\r\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\r\n    <value>Colocar em fila agora</value>\r\n  </data>\r\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Sem tarefas agendadas.</value>\r\n  </data>\r\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\r\n    <value>Reagendar</value>\r\n  </data>\r\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\r\n    <value>Agendado</value>\r\n  </data>\r\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas Agendadas</value>\r\n  </data>\r\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\r\n    <value>Não existem servidores activos. As tarefas não serão executadas.</value>\r\n  </data>\r\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\r\n    <value>Sinal</value>\r\n  </data>\r\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\r\n    <value>Nome</value>\r\n  </data>\r\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\r\n    <value>Filas</value>\r\n  </data>\r\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\r\n    <value>Iniciadas</value>\r\n  </data>\r\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\r\n    <value>Workers</value>\r\n  </data>\r\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\r\n    <value>Servidores</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\r\n    <value>Nenhuma tarefa concluída encontrada.</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\r\n    <value>Concluídas</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\r\n    <value>Duração</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\r\n    <value>Tarefas concluídas</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\r\n    <value>Em espera</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\r\n    <value>Removidas</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\r\n    <value>Com Erro</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\r\n    <value>Processando</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\r\n    <value>Agendadas</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\r\n    <value>Concluídas</value>\r\n  </data>\r\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\r\n    <value>Tarefas</value>\r\n  </data>\r\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas Recorrentes</value>\r\n  </data>\r\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\r\n    <value>Reagendamentos</value>\r\n  </data>\r\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\r\n    <value>Servidores</value>\r\n  </data>\r\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\r\n    <value>Não foi possível encontrar o método alvo.</value>\r\n  </data>\r\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\r\n    <value>Em Fila</value>\r\n  </data>\r\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\r\n    <value>Sem estado</value>\r\n  </data>\r\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\r\n    <value>Em Fila</value>\r\n  </data>\r\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\r\n    <value>Ligações activas</value>\r\n  </data>\r\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas Removidas</value>\r\n  </data>\r\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas com erro</value>\r\n  </data>\r\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas em processamento</value>\r\n  </data>\r\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas recorrentes</value>\r\n  </data>\r\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\r\n    <value>Reagendamentos</value>\r\n  </data>\r\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas Agendadas</value>\r\n  </data>\r\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\r\n    <value>Servidores</value>\r\n  </data>\r\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\r\n    <value>Tarefas com sucesso</value>\r\n  </data>\r\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\r\n    <value>Total de Ligações</value>\r\n  </data>\r\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\r\n    <value>Condição</value>\r\n  </data>\r\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\r\n    <value>Sequências</value>\r\n  </data>\r\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\r\n    <value>Em espera</value>\r\n  </data>\r\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\r\n    <value>Em fila</value>\r\n  </data>\r\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\r\n    <value>Em fila / Filas</value>\r\n  </data>\r\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\r\n    <value>{0} tarefa(s) com erro encontradas. Reagendar ou remover manualmente.</value>\r\n  </data>\r\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\r\n    <value>Com erro</value>\r\n  </data>\r\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\r\n    <value>Com sucesso</value>\r\n  </data>\r\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\r\n    <value>Inactivo</value>\r\n  </data>\r\n  <data name=\"RecurringJobsPage_RecurringJobDisabled_Tooltip\" xml:space=\"preserve\">\r\n    <value>A expressão Cron é inválida ou não tem nenhuma ocorência nos próximos 100 anos</value>\r\n  </data>\r\n  <data name=\"ServersPage_Note_Text\" xml:space=\"preserve\">\r\n    <value>Alguns servidores não reportaram nehum sinal no último minuto e podem ser cancelados. Se não reportarem nenhum sinal brevemente serão cancelados assim que o tempo limite seja ultrapassado, não é necessária nenhuma acção manual.\r\n                    Tarefas incompletas nesses servidores serão repetidas automaticamente, no entanto o processo pode ser acelarado acedendo e verificando a seguinte página &lt;a href=\"{0}\"&gt;Tarefas em Processamento&lt;/a&gt;.</value>\r\n  </data>\r\n  <data name=\"ServersPage_Note_Title\" xml:space=\"preserve\">\r\n    <value>Servidores cancelados serão removidos automaticamente</value>\r\n  </data>\r\n  <data name=\"ServersPage_Active\" xml:space=\"preserve\">\r\n    <value>Activo</value>\r\n  </data>\r\n  <data name=\"ServersPage_Possibly_Aborted\" xml:space=\"preserve\">\r\n    <value>Possivelmente cancelados</value>\r\n  </data>\r\n  <data name=\"Common_Error\" xml:space=\"preserve\">\r\n    <value>Erro</value>\r\n  </data>\r\n  <data name=\"DeletedJobsPage_Table_Exception\" xml:space=\"preserve\">\r\n    <value>Exceção</value>\r\n  </data>\r\n  <data name=\"LayoutPage_Footer_StorageTime\" xml:space=\"preserve\">\r\n    <value>Tempo de armazenamento:</value>\r\n  </data>\r\n  <data name=\"LayoutPage_Footer_TimeIsOutOfSync\" xml:space=\"preserve\">\r\n    <value>A hora da aplicação está dessincronizada com a hora do sistema de armazenamento</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_Table_Latency\" xml:space=\"preserve\">\r\n    <value>Latência</value>\r\n  </data>\r\n  <data name=\"SucceededJobsPage_Table_Duration\" xml:space=\"preserve\">\r\n    <value>Duração</value>\r\n  </data>\r\n  <data name=\"JobDetailsPage_Parameters\" xml:space=\"preserve\">\r\n    <value>Parâmetros</value>\r\n  </data>\r\n  <data name=\"Metrics_SQLServer_SchemaVersion\" xml:space=\"preserve\">\r\n    <value>Versão Schema</value>\r\n  </data>\r\n  <data name=\"AwaitingJobsPage_Table_Since\" xml:space=\"preserve\">\r\n    <value>Desde</value>\r\n  </data>\r\n  <data name=\"Metrics_SQLServer_ActiveTransactions\" xml:space=\"preserve\">\r\n    <value>Transações Ativas</value>\r\n  </data>\r\n  <data name=\"Metrics_SQLServer_DataFilesSize\" xml:space=\"preserve\">\r\n    <value>Ficheiro(s) Dados Usadas (MB)</value>\r\n  </data>\r\n  <data name=\"Metrics_SQLServer_LogFilesSize\" xml:space=\"preserve\">\r\n    <value>Ficheiro(s) Log Usados (MB)</value>\r\n  </data>\r\n</root>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>Don't worry, continuations are working as expected. But your current job storage does not support some queries required to show this page. Please try to update your storage or wait until the full command set is implemented.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>Continuations are working, but this page can't be displayed</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No jobs found in awaiting state.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>Options</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>Parent</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Awaiting Jobs</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>Created</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>Delete</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Do you really want to DELETE ALL selected jobs?</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>Deleting...</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>Delete selected</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>Enqueue jobs</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>Enqueueing...</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>Fetched</value>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>Id</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>Job</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>Job expired.</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>Job's state has been changed while fetching data.</value>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>Fewer details...</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>More details...</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>N/A</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>Day</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>Week</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>Reason</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>Requeue jobs</value>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>Retry</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>Server</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>State</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>Unknown</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No deleted jobs found.</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>Deleted</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Exception\" xml:space=\"preserve\">\n    <value>Exception</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Deleted Jobs</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>The queue is empty.</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Enqueued Jobs</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Failed jobs do not become expired&lt;/strong&gt; to allow you to re-queue them without any\n                time pressure. You should re-queue or delete them manually, or apply &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt;\n                attribute to delete them automatically.</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>You have no failed jobs at the moment.</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>Failed</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Failed Jobs</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>The queue is empty.</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Fetched Jobs</value>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>History Graph</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>Realtime Graph</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>Overview</value>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>Created</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Do you really want to delete this job?</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>State</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;The job was aborted&lt;/strong&gt; – it is processed by server\n                        &lt;code&gt;{0}&lt;/code&gt; which is not in the \n                        &lt;a href=\"{1}\"&gt;active servers&lt;/a&gt; list for now.\n                        It will be retried automatically after invisibility timeout, but you can\n                        also re-queue or delete it manually.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Looks like the job was aborted&lt;/strong&gt; – it is processed by server\n                        &lt;code&gt;{0}&lt;/code&gt;, which reported its heartbeat more than 1 minute ago.\n                        It will be retried automatically after invisibility timeout, but you can\n                        also re-queue or delete it manually.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>Background job '{0}' has expired or could not be found on the server.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;The job is finished&lt;/strong&gt;.\n                    It will be removed automatically &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>Id</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>Requeue</value>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>Back to site</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>Generated: {0}ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_StorageTime\" xml:space=\"preserve\">\n    <value>Storage Time:</value>\n  </data>\n    <data name=\"LayoutPage_Footer_TimeIsOutOfSync\" xml:space=\"preserve\">\n    <value>Application time is out of sync with storage time</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>Application Time:</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>Next</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>Prev</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>Total items</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>Items per page</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>Looks like the job was aborted</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No jobs are being processed right now.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>Started</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Processing Jobs</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>No jobs queued.</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>No queued jobs found. Try to enqueue a job.</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>Length</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>Next jobs</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>Queue</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>Queues</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>Canceled</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No recurring jobs found.</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>Last execution</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>Next execution</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>Time zone</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>Recurring Jobs</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>Triggering...</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>Trigger now</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>All is OK – you have no retries.</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>Retries</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;Retries are working, but this page can't be displayed&lt;/h4&gt;\n        &lt;p&gt;\n            Don't worry, retries are working as expected. Your current job storage does not support\n            some queries required to show this page. Please try to update your storage or wait until\n            the full command set is implemented.\n        &lt;/p&gt;\n        &lt;p&gt;\n            Please go to the &lt;a href=\"{0}\"&gt;Scheduled jobs&lt;/a&gt; page to see all the\n            scheduled jobs including retries.\n        &lt;/p&gt;</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>Enqueue now</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>There are no scheduled jobs.</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>Enqueue</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>Scheduled</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>Scheduled Jobs</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>There are no active servers. Background tasks will not be processed.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>Heartbeat</value>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>Name</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>Queues</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>Started</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>Workers</value>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>Servers</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>No succeeded jobs found.</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>Succeeded</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>Total Duration</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Latency\" xml:space=\"preserve\">\n    <value>Latency</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Duration\" xml:space=\"preserve\">\n    <value>Duration</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>Succeeded Jobs</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>Awaiting</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>Deleted</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>Failed</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>Processing</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>Scheduled</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>Succeeded</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>Jobs</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>Recurring Jobs</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>Retries</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>Servers</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>Can not find the target method.</value>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>Enqueued</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>No state</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>Enqueued</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>Active Connections</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>Deleted Jobs</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>Failed Jobs</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>Processing Jobs</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>Recurring Jobs</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>Retries</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>Scheduled Jobs</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>Servers</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>Succeeded Jobs</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>Total Connections</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>Condition</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>Continuations</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>Awaiting</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>Enqueued</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>Enqueued / Queues</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0} failed job(s) found. Retry or delete them manually.</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>Failed</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>Succeeded</value>\n  </data>\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\n    <value>Disabled</value>\n  </data>\n  <data name=\"RecurringJobsPage_RecurringJobDisabled_Tooltip\" xml:space=\"preserve\">\n    <value>Cron expression is invalid or don't have any occurrences over the next 100 years</value>\n  </data>\n  <data name=\"ServersPage_Note_Text\" xml:space=\"preserve\">\n    <value>Some of the servers don't have heartbeat reported within the last minute and may be aborted. If they don't report heartbeat in the near future, they will be removed automatically after timeout is exceeded, no manual action is required.\n                    Incomplete background jobs running on those servers will be re-queued automatically, but you can speed up the process by checking the &lt;a href=\"{0}\"&gt;Processing Jobs&lt;/a&gt; page.</value>\n  </data>\n  <data name=\"ServersPage_Note_Title\" xml:space=\"preserve\">\n    <value>Aborted servers will be removed automatically</value>\n  </data>\n  <data name=\"ServersPage_Active\" xml:space=\"preserve\">\n    <value>Active</value>\n  </data>\n  <data name=\"ServersPage_Possibly_Aborted\" xml:space=\"preserve\">\n    <value>Possibly aborted</value>\n  </data>\n  <data name=\"Common_Error\" xml:space=\"preserve\">\n    <value>Error</value>\n  </data>\n  <data name=\"JobDetailsPage_Parameters\" xml:space=\"preserve\">\n    <value>Parameters</value>\n  </data>\n  <data name=\"Metrics_SQLServer_SchemaVersion\" xml:space=\"preserve\">\n    <value>Schema Version</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Since\" xml:space=\"preserve\">\n    <value>Since</value>\n  </data>\n  <data name=\"Metrics_SQLServer_ActiveTransactions\" xml:space=\"preserve\">\n    <value>Active Transactions</value>\n  </data>\n  <data name=\"Metrics_SQLServer_DataFilesSize\" xml:space=\"preserve\">\n    <value>Data File(s) Used (MB)</value>\n  </data>\n  <data name=\"Metrics_SQLServer_LogFilesSize\" xml:space=\"preserve\">\n    <value>Log File(s) Used (MB)</value>\n  </data>\n</root>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.sv.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>Oroa dig inte, fortsättningar fungerar som dom ska. Din nuvarande lagring för jobb stödjer\n            inte vissa frågor som behövs för att visa den här sidan. Uppdatera till en annan lagringstyp\n            eller vänta och hoppas att den nuvarande får stöd för alla frågetyper.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>Fortsättningar fungerar, men den här sidan kunde inte visas</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Inga jobb hittades i väntande tillstånd.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>Inställningar</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>Förälder</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Since\" xml:space=\"preserve\">\n    <value>Sen</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Väntade jobb</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>Skapad</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>Ta bort</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Vill du verkligen TA BORT ALLA valda jobb?</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>Tar bort...</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>Ta bort valda</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>Köa upp jobb</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>Lägger till i kö...</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>Hämtad</value>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>Id</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>Jobb</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>Utgånget jobb.</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>Jobbets tillstånd ändrades medan informationen hämtades.</value>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>Färre detaljer...</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>Mer detaljer...</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>N/A</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>Dag</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>Vecka</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>Orsak</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>Köa om jobb</value>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>Försök igen</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>Server</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>Tillstånd</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>Okänd</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Inga borttagna jobb hittades.</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>Borttaget</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Exception\" xml:space=\"preserve\">\n    <value>Undantag</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Borttagna jobb</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Kön är tom.</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Köade jobb</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Misslyckade jobb löper inte ut&lt;/strong&gt; så att man inte ska känna sig stressad att köa upp det igen.\n                Köa upp eller ta bort dom manuellt, eller sätt följande attribut i koden för att radera dom automatiskt: &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt;</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Du har inga misslyckade jobb just nu.</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>Misslyckad</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Misslyckade jobb</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Kön är tom.</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Hämtade jobb</value>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>Historisk graf</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>Realtidsgraf</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>Översikt</value>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>Skapad</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Vill du verkligen ta bort det här jobbet?</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>Tillstånd</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Jobbet avbröts&lt;/strong&gt; – det processades av servern\n                        &lt;code&gt;{0}&lt;/code&gt; som just nu inte är med på listan över\n                        &lt;a href=\"{1}\"&gt;aktiva servrar&lt;/a&gt;.\n                        Det kommer att automatiskt försökas igen efter &quot;invisibility timeout&quot;, men du kan\n                        också köa upp eller ta bort det manuellt.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Det verkar som att jobbeet avbröts&lt;/strong&gt; – det processas av servern\n                        &lt;code&gt;{0}&lt;/code&gt;, som gav sitt senaste livstecken för mer än en minut sedan.\n                        Det kommer att automatiskt försökas igen efter &quot;invisibility timeout&quot;, men du kan\n                        också köa upp eller ta bort det manuellt.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>Bakgrundsjobbet '{0}' har löpt ut eller kunde inte hittas på servern.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Jobbet är färdigt&lt;/strong&gt;.\n                    Det kommer att tas bort automatiskt &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>Jobb ID</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>Köa igen</value>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>Tillbaka till webbplats</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>Genererades på: {0}ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_StorageTime\" xml:space=\"preserve\">\n    <value>Lagringstid:</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>Applikationstid:</value>\n  </data>\n  <data name=\"LayoutPage_Footer_TimeIsOutOfSync\" xml:space=\"preserve\">\n    <value>Applikationstiden är inte i synk med lagringstiden</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>Nästa</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>Föregående</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>Totalt antal</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>Antal per sida</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>Det verkar som att jobbet avbröts</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Inga jobb körs just nu.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>Startade</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Processing Jobs</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Inga köade jobb.</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>Inga köade jobb finns. Försök med att köa upp ett jobb.</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>Längd</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>Nästa jobb</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>Kö</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>Köer</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>Avbrutet</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Inga återkommande jobb hittades.</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>Senaste körning</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>Nästa körning</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>Tidszon</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>Återkommande jobb</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>Startar...</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>Starta nu</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Allt är OK – du har inga nya försök.</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>Nya försök</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;Nya försök fungerar, men den här sidan kan inte visas&lt;/h4&gt;\n        &lt;p&gt;\n            Oroa dig inte, nya försök fungerar som dom ska. Din nuvarande lagring för jobb stödjer\n            inte vissa frågor som behövs för att visa den här sidan. Uppdatera till en annan lagringstyp\n            eller vänta och hoppas att den nuvarande får stöd för alla frågetyper.\n        &lt;/p&gt;\n        &lt;p&gt;\n            Gå till &lt;a href=\"{0}\"&gt;Schemalagda jobb&lt;/a&gt;-sidan för att se alla\n            schemalagda jobb inklusive nya försök.\n        &lt;/p&gt;</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>Köa upp nu</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Det finns inga schemalagda jobb.</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>Köa upp</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>Schemalagd</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>Schemalagda jobb</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>Det finns inga aktiva servrar. Bakgrundsjobb kommer inte att köras.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>Livstecken</value>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>Namn</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>Köer</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>Startade</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>Arbetare</value>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>Servrar</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Inga lyckade jobb hittades.</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Duration\" xml:space=\"preserve\">\n    <value>Tidsåtgång</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Latency\" xml:space=\"preserve\">\n    <value>Fördröjning</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>Lyckades</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>Total tidsåtgång</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>Lyckade jobb</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>Väntar</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>Borttagna</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>Misslyckade</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>Kör</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>Schemalagd</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>Lyckade</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>Jobb</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>Återkommande jobb</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>Antal nya försök</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>Servrar</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>Kan inte hitta målmetoden.</value>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>Köad</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>Inget tillstånd</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>Köade</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>Aktiva anslutningar</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>Borttagna jobb</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>Misslyckade jobb</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>Pågående jobb</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>Återkommande jobb</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>Antal nya försök</value>\n  </data>\n  <data name=\"Metrics_SQLServer_ActiveTransactions\" xml:space=\"preserve\">\n    <value>Aktiva transaktioner</value>\n  </data>\n  <data name=\"Metrics_SQLServer_DataFilesSize\" xml:space=\"preserve\">\n    <value>Datafiler (MB)</value>\n  </data>\n  <data name=\"Metrics_SQLServer_LogFilesSize\" xml:space=\"preserve\">\n    <value>Loggfiler (MB)</value>\n  </data>\n  <data name=\"Metrics_SQLServer_SchemaVersion\" xml:space=\"preserve\">\n    <value>Schemaversion</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>Schemalagda jobb</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>Servrar</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>Lyckade jobb</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>Antal anslutningar</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>Tillstånd</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>Fortsättningar</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>Väntande</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>Köade</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>Köade / Kö</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0} misslyckade jobb hittades. Försök igen eller ta bort dom manuellt.</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>Misslyckade</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>Lyckade</value>\n  </data>\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\n    <value>Avtängd</value>\n  </data>\n  <data name=\"RecurringJobsPage_RecurringJobDisabled_Tooltip\" xml:space=\"preserve\">\n    <value>Cron-uttrycket är ogiligt eller inträffar inte dom närmaste hundra åren</value>\n  </data>\n  <data name=\"ServersPage_Note_Text\" xml:space=\"preserve\">\n    <value>Vissa servrar har inte gett några livstecken den senaste minuten och kan komma att avbrytas.\n            Om dom inte ger några livstecken inom en snar framtid så kommer dom att tas bort automatiskt efter en timeout.\n            Pågående bakgundsjobb på dessa servrar kommer att köas upp igen automatiskt, men du kan snabba upp processen genom att kika på sidan &lt;a href=\"{0}\"&gt;Pågående jobb&lt;/a&gt;.</value>\n  </data>\n  <data name=\"ServersPage_Note_Title\" xml:space=\"preserve\">\n    <value>Avbrutna servrar kommer att tas bort automatiskt</value>\n  </data>\n  <data name=\"ServersPage_Active\" xml:space=\"preserve\">\n    <value>Aktiv</value>\n  </data>\n  <data name=\"ServersPage_Possibly_Aborted\" xml:space=\"preserve\">\n    <value>Möjligen avbrutna</value>\n  </data>\n  <data name=\"Common_Error\" xml:space=\"preserve\">\n    <value>Fel</value>\n  </data>\n  <data name=\"JobDetailsPage_Parameters\" xml:space=\"preserve\">\n    <value>Parametrar</value>\n  </data>\n</root>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.tr-TR.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value> \n\t\tEndişelenmeyin, continuations beklendiği gibi çalışıyor. Ancak mevcut iş kaynaklarınız bu sayfayı göstermek için gereken bazı sorguları desteklemiyor. Lütfen iş kaynaklarınızı güncellemeyi deneyin veya tüm entegrasyon tamamlana kadar bekleyin.\n\t</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>Continuations işleriniz çalışıyor ancak sayfa gösterilemiyor.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Bekleyen bir işiniz bulunmamaktadır.</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>Ayarlar</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>Üst</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Bekleyen İşler</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>Oluşturan</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>Sil</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value> TÜM SEÇİLİ işleri silmek istediğine emin misin?</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>Siliniyor...</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>Seçilileri Sil</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>Kuyruklu işler</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>Kuyruğa alınıyor...</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>Getirildi</value>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>Kimlik</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>İş</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>İşin süresi doldu.</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>Veriler alınırken işin durumu değiştirildi.</value>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>Daha az detay...</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>Detay fazla detay...</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>Yok</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>Gün</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>Hafta</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>Neden</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>İşleri yeniden sıraya al</value>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>Tekrar</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>Sunucu</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>Durum</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>Bilinmeyen</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Silinen işler bulunamadı.</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>Silindi</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Silinen İşler</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Kuyruk boş.</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Sıralanmış İşler</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Başarısız olan işlerin süresi dolmaz.&lt;/strong&gt; Bu işleri yeniden sıraya koymanız için zamanı iyi ayarlamalısınız.\n                Bu işleri manuel olarak tekrar kuyruğa alabilir veya silebilirsiniz. İpucu : &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt;\n                attribute'unu ekleyerek otomatik olarak silebilrsiniz. </value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value> Şu anda Başarısız iş bulunmamaktadır. </value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>Başarısız</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>Başarısız İşler</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Kuyruk boş</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>İş Sonuçları</value>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>Geçmiş Grafiği</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>Canlı Grafik</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>Genel Bakış</value>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>Oluşturulan</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>Bu işi gerçekten silmek istediğine emin misin?</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>Durum</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;Bu iş iptal edildi&lt;/strong&gt; – Sunucu tarafından işlendi.\n                        &lt;code&gt;{0}&lt;/code&gt; which is not in the \n                        &lt;a href=\"{1}\"&gt;active servers&lt;/a&gt; list for now.\n                        Zaman aşımı sonrası tekrardan yenilenecektir, ancak manuel olarakta\n                        tekrar kuyruğa alabilir veya silebilirsiniz. </value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;İş iptal edilmiş görüntüleniyor.&lt;/strong&gt; – Sunucu tarafından işlendi.\n                        &lt;code&gt;{0}&lt;/code&gt;, tarafından kontrol zamanın 1 dakikadan fazla olduğunu raporladı.\n                        Zaman aşımı sonrası tekrardan yenilenecektir, ancak manuel olarakta\n                        tekrar kuyruğa alabilir veya silebilirsiniz.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>Arkaplan işi '{0}' zaman aşımına uğradı veya sunucu tarafından bulunamadı.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;İş tamamlandı&lt;/strong&gt;.\n                  Otomatik olarak silinecektir. &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>İş Kimliği</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>Yeniden sıraya al</value>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>Siteye geri dön</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>Oluşturuldu: {0}ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>Zaman:</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>İleri</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>Geri</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>Toplam eleman</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>Görüntülenme adeti</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>İş iptal edildi</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Şuan hiç bir iş işlenemiyor.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>Başlatıldı.</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>Çalışan İşler</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Sıraya alınmış iş yok.</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>Kuyruğa alınmış iş bulunamadı. Bir işi kuyruğa almaya çalışın.</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>Bekleyenler</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>Sonraki işler</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>Kuyruk</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>Kuyruklar</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>İptal edildi</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Tekrarlayan iş bulunamadı.</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>Son Yürütme</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>Sonraki yürütme</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>Saat dilimi</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>Tekrarlayan İşler</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>Tetikleniyor...</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>Şimdi tetikle</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>Her şey tamamlandı. Tekrarlayan iş bulunmamaktadır.</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>Tekrarlayan İşler</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;Tekrarlayan işler çalışıyor, ancak sayfa gösterilemiyor&lt;/h4&gt;\n        &lt;p&gt;\n            Merak etmeyin, yeniden denemeler beklendiği gibi çalışıyor.\n\t\t\tMevcuttaki iş kaynağı bazı sorguları göstermek için desteklemiyor. Lütfen kaynağı güncelleyin veya \n\t\t\ttüm komut seti entegre edilene kadar bekleyin.\n        &lt;/p&gt;\n        &lt;p&gt;\n            Lütfen &lt;a href=\"{0}\"&gt;Planlanmış işler&lt;/a&gt; sayfasına gidip planmış ve yeniden tetiklenmiş işleri görüntüleyiniz.\n        &lt;/p&gt;</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>Sıraya al</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Planlanmış bir iş bulunmamakta.</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>Sıraya al</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>Planlanmış</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>Planlanmış İşler</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>Aktif sunucu bulunamadı Arkaplan işleri işlenemiyor.</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>Kontrol Zamanı</value>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>İsim</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>Kuyruklar</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>Başlatıldı</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>Çalışan Servisler</value>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>Sunucular</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>Tamamlanmış iş bulunamadı.</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>Başarılı</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>Toplam Süre</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>Başarılı işler</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>Beklenenler</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>Silinenler</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>Başarısızlar</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>İşlenenler</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>Planlananlar</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>Başarılılar</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>İşler</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>Tekrarlayan İşler</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>Yeniden Denenenler</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>Sunucular</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>Çalıştırılacak method bulunamadı.</value>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>Kuyruğa alındı</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>Durum yok</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>Sıradakiler</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>Aktif Bağlantılar</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>Silinen İşler</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>Başarısız İşler</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>İşlenen İşler</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>Tekrarlayan İşler</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>Yeniden Denenenler</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>Planlanan İşler</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>Sunucular</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>Başarılı İşler</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>Toplam Bağlantı</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>Durum</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>Sürekli</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>Bekliyor</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>Kuyruğa alındı</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>Kuyruğa Alınan</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0} başarısız iş bulundu. Tekrar deneyin veya manuel silin.</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>Başarısız</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>Başarılı</value>\n  </data>\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\n    <value>Etkisiz</value>\n  </data>\n  <data name=\"RecurringJobsPage_RecurringJobDisabled_Tooltip\" xml:space=\"preserve\">\n    <value>Cron ifadesi geçersiz veya 100 yıldan büyük bir ifade içermektedir.</value>\n  </data>\n  <data name=\"ServersPage_Note_Text\" xml:space=\"preserve\">\n    <value>\n\t \tBazı sunuculardan cevap alınamadığı gözlenlendi. Zaman aşımı içinde cevap vermedikleri taktirde, otomatik olarak silineceklerdir. Bu işlem için herhangi bir müdaheleye gerek yoktur.\n                    Tamamlanmayan arkaplan işleri bu sunucularda çalışmaya devam edecektir. Hızlıca &lt;a href=\"{0}\"&gt;İşlenen İşler&lt;/a&gt; sayfasından ilgili işleri kontrol edebilirsiniz.</value>\n  </data>\n  <data name=\"ServersPage_Note_Title\" xml:space=\"preserve\">\n    <value>İptal edilen sunucular otomatik olarak kaldırılacaktır.</value>\n  </data>\n  <data name=\"ServersPage_Active\" xml:space=\"preserve\">\n    <value>Aktif</value>\n  </data>\n  <data name=\"ServersPage_Possibly_Aborted\" xml:space=\"preserve\">\n    <value>İptal edilmiş.</value>\n  </data>\n  <data name=\"Common_Error\" xml:space=\"preserve\">\n    <value>Hata</value>\n  </data>\n  <data name=\"JobDetailsPage_Parameters\" xml:space=\"preserve\">\n    <value>Parametreler</value>\n  </data>\n</root>\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.zh-TW.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>別擔心，持續性工作正在按預期執行。 但是，您當前的作業存儲空間不支持顯示此頁面所需的一些查詢。 請嘗試更新您的存儲空間，或者等到完整的命令集被執行。</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>工作繼續執行中，但是無法顯示該頁面</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>沒有等待中的工作</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>選項</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>父層</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>等待中的工作</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>建立</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>刪除</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>您確定要刪除所選的全部工作嗎？</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>刪除中…</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>刪除選取</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>佇列工作</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>加入佇列中…</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>獲取到</value>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>編號</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>工作</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>工作過期</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>工作狀態已被變更</value>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>收起…</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>更多…</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>N/A</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>天</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>周</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>原因</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>重新加入佇列</value>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>重試</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>伺服器</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>狀態</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>未知</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>沒有找到任何已經刪除的工作</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>刪除</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>刪除工作</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>沒有任何工作</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>佇列工作</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;不會過期的失敗工作&lt;/strong&gt;&lt;br/&gt;在沒有任何時間限制下，允許你重新執行工作，您應該重新執行或刪除工作或再次加入佇列。&lt;br /&gt;在屬性&lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt;\n                設定可自動刪除</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>沒有失敗的工作</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>失敗</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>失敗的工作</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>沒有任何工作</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>擷取工作</value>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>歷史圖表走勢</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>即時圖表走勢</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>儀表板</value>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>建立</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>確認刪除這個工作嗎？</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>狀態</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;工作被終止&lt;/strong&gt; – 在伺服器\n                        &lt;code&gt;{0}&lt;/code&gt; 被終止，此工作不在目前 \n                        &lt;a href=\"{1}\"&gt;執行中伺服器&lt;/a&gt; 清單\n                        工作在逾時後自動重試執行，也可以重新執行或手動刪除它。</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;工作疑似終止&lt;/strong&gt; – 在伺服器\n                        &lt;code&gt;{0}&lt;/code&gt;被終止，執行時間已超過一分鐘，工作將會自動重試執行，也可以重新執行或手動刪除它。</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>背景工作 '{0}' 逾時或無法在伺服器執行此工作</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;工作結束&lt;/strong&gt;.\n                    工作將被自動移除 &lt;em&gt;&lt;abbr data-moment=\"{0}\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>Job Id</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>重試</value>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>返回</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>耗時：{0}ms</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>時間：</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>下一步</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>上一步</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>總筆數</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>每頁筆數</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>工作疑似終止</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>沒有立即執行的工作</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>執行</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>執行中的工作</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>無工作</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>無佇列工作，嘗試加入工作至佇列</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>長度</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>下一個工作</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>佇列</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>佇列</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>取消</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>無工作</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>最後執行</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>下一個執行</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>時區</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>定期工作</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>執行中…</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>立即執行</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>沒有重試的工作</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>重試</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;重試項目正在執行，但此頁面無法顯示&lt;/h4&gt;\n        &lt;p&gt;\n            別擔心，重試項目正在執行中，目前的工作儲存庫無法支援查詢，請試著更新您的工作儲存庫或等待下一個執行時間點。\n        &lt;/p&gt;\n        &lt;p&gt;\n            請至 &lt;a href=\"{0}\"&gt;計劃&lt;/a&gt;頁查看所有計劃工作及重試項目\n        &lt;/p&gt;</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>加入佇列</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>無工作</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>佇列</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>計劃的</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>計劃工作</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>沒有執行中的伺服器，工作將不會被執行</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>心跳</value>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>名稱</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>佇列</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>執行</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>工作區</value>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>伺服器</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>無工作</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>完成</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>總觸發時間</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>完成的工作</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>等待中</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>刪除</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>失敗</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>執行中</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>計劃</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>完成</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>工作</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>定期工作</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>重試</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>伺服器</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>無法找到目標的函式</value>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>佇列</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>無狀態</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>佇列</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>連線數</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>刪除</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>失敗</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>執行中</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>定期</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>重試</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>計劃</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>伺服器</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>成功的工作</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>總連線數</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>條件</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>持續</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>等待中</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>佇列</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>佇列</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0} 筆失敗的工作，重試或手動刪除它們</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>失敗</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>完成</value>\n  </data>\n</root>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Content/resx/Strings.zh.resx",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Text\" xml:space=\"preserve\">\n    <value>别担心，系统正在按照预定指令执行当前的工作任务。但是，当前的作业存储不支持显示此页所需的一些查询。请尝试更新您的存储，或者等到完整命令集实现为止。</value>\n  </data>\n  <data name=\"AwaitingJobsPage_ContinuationsWarning_Title\" xml:space=\"preserve\">\n    <value>作业继续执行中，但是无法显示该页面</value>\n  </data>\n  <data name=\"AwaitingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有等待中的作业</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Options\" xml:space=\"preserve\">\n    <value>选项</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Table_Parent\" xml:space=\"preserve\">\n    <value>父级</value>\n  </data>\n  <data name=\"AwaitingJobsPage_Title\" xml:space=\"preserve\">\n    <value>等待中的作业</value>\n  </data>\n  <data name=\"Common_Created\" xml:space=\"preserve\">\n    <value>创建</value>\n  </data>\n  <data name=\"Common_Delete\" xml:space=\"preserve\">\n    <value>删除</value>\n  </data>\n  <data name=\"Common_DeleteConfirm\" xml:space=\"preserve\">\n    <value>您确定要删除所选的全部作业吗？</value>\n  </data>\n  <data name=\"Common_Deleting\" xml:space=\"preserve\">\n    <value>删除中...</value>\n  </data>\n  <data name=\"Common_Disabled\" xml:space=\"preserve\">\n    <value>禁用</value>\n  </data>\n  <data name=\"Common_DeleteSelected\" xml:space=\"preserve\">\n    <value>删除选中</value>\n  </data>\n  <data name=\"Common_EnqueueButton_Text\" xml:space=\"preserve\">\n    <value>队列作业</value>\n  </data>\n  <data name=\"Common_Enqueueing\" xml:space=\"preserve\">\n    <value>加入队列中...</value>\n  </data>\n  <data name=\"Common_Error\" xml:space=\"preserve\">\n    <value>错误</value>\n  </data>\n  <data name=\"Common_Fetched\" xml:space=\"preserve\">\n    <value>获取到</value>\n  </data>\n  <data name=\"Common_Id\" xml:space=\"preserve\">\n    <value>编号</value>\n  </data>\n  <data name=\"Common_Job\" xml:space=\"preserve\">\n    <value>作业</value>\n  </data>\n  <data name=\"Common_JobExpired\" xml:space=\"preserve\">\n    <value>作业过期.</value>\n  </data>\n  <data name=\"Common_JobStateChanged_Text\" xml:space=\"preserve\">\n    <value>作业状态已经发生变化</value>\n  </data>\n  <data name=\"Common_LessDetails\" xml:space=\"preserve\">\n    <value>收起...</value>\n  </data>\n  <data name=\"Common_MoreDetails\" xml:space=\"preserve\">\n    <value>更多...</value>\n  </data>\n  <data name=\"Common_NotAvailable\" xml:space=\"preserve\">\n    <value>N/A</value>\n  </data>\n  <data name=\"Common_PeriodDay\" xml:space=\"preserve\">\n    <value>天</value>\n  </data>\n  <data name=\"Common_PeriodWeek\" xml:space=\"preserve\">\n    <value>周</value>\n  </data>\n  <data name=\"Common_Reason\" xml:space=\"preserve\">\n    <value>原因</value>\n  </data>\n  <data name=\"Common_RequeueJobs\" xml:space=\"preserve\">\n    <value>重新加入队列</value>\n  </data>\n  <data name=\"Common_Retry\" xml:space=\"preserve\">\n    <value>重试</value>\n  </data>\n  <data name=\"Common_Server\" xml:space=\"preserve\">\n    <value>服务器</value>\n  </data>\n  <data name=\"Common_State\" xml:space=\"preserve\">\n    <value>状态</value>\n  </data>\n  <data name=\"Common_Unknown\" xml:space=\"preserve\">\n    <value>未知</value>\n  </data>\n  <data name=\"DeletedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有找到任何已经删除的作业</value>\n  </data>\n  <data name=\"DeletedJobsPage_Table_Deleted\" xml:space=\"preserve\">\n    <value>删除</value>\n  </data>\n  <data name=\"DeletedJobsPage_Title\" xml:space=\"preserve\">\n    <value>删除作业</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有任何作业</value>\n  </data>\n  <data name=\"EnqueuedJobsPage_Title\" xml:space=\"preserve\">\n    <value>队列作业</value>\n  </data>\n  <data name=\"FailedJobsPage_FailedJobsNotExpire_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;失败的作业不会过期。&lt;/strong&gt; 允许您在没有任何时间压力的情况下重新排队。你应该重新排队或手动删除它们， 或者添加 &lt;code&gt;AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete)&lt;/code&gt; 属性，系统将会自动删除作业。</value>\n  </data>\n  <data name=\"FailedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有失败的作业</value>\n  </data>\n  <data name=\"FailedJobsPage_Table_Failed\" xml:space=\"preserve\">\n    <value>失败</value>\n  </data>\n  <data name=\"FailedJobsPage_Title\" xml:space=\"preserve\">\n    <value>失败的作业</value>\n  </data>\n  <data name=\"FetchedJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有任何作业</value>\n  </data>\n  <data name=\"FetchedJobsPage_Title\" xml:space=\"preserve\">\n    <value>任务列队</value>\n  </data>\n  <data name=\"HomePage_HistoryGraph\" xml:space=\"preserve\">\n    <value>历史图表走势</value>\n  </data>\n  <data name=\"HomePage_RealtimeGraph\" xml:space=\"preserve\">\n    <value>实时图表走势</value>\n  </data>\n  <data name=\"HomePage_Title\" xml:space=\"preserve\">\n    <value>仪表盘</value>\n  </data>\n  <data name=\"JobDetailsPage_Created\" xml:space=\"preserve\">\n    <value>创建</value>\n  </data>\n  <data name=\"JobDetailsPage_DeleteConfirm\" xml:space=\"preserve\">\n    <value>确认删除这个作业吗？</value>\n  </data>\n  <data name=\"JobDetailsPage_State\" xml:space=\"preserve\">\n    <value>状态</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedNotActive_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;工作被中止&lt;/strong&gt; – 它现在正在由非&lt;a href=\\\"{1}\\\"&gt;活动状态的服务器&lt;/a&gt; &lt;code&gt;{0}&lt;/code&gt;处理任务。 系统将在任务重试超时后自动结束，你也可以手动重新执行或将其删除。</value>\n  </data>\n  <data name=\"JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;作业疑似终止&lt;/strong&gt; – 服务器代码\n                        &lt;code&gt;{0}&lt;/code&gt;, 心跳包超过1分钟请求超时后，作业会自动重试。但也可以重新加入队列或者删除作业。</value>\n  </data>\n  <data name=\"JobDetailsPage_JobExpired\" xml:space=\"preserve\">\n    <value>作业 '{0}' 已经过期或不存在于这台服务器。</value>\n  </data>\n  <data name=\"JobDetailsPage_JobFinished_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;strong&gt;任务完成&lt;/strong&gt;.系统将在&lt;em&gt;&lt;abbr data-moment=\\\"{0}\\\"&gt;{1}&lt;/abbr&gt;&lt;/em&gt;被自动删除这个任务记录.</value>\n  </data>\n  <data name=\"JobDetailsPage_JobId\" xml:space=\"preserve\">\n    <value>任务编号</value>\n  </data>\n  <data name=\"JobDetailsPage_Requeue\" xml:space=\"preserve\">\n    <value>重试</value>\n  </data>\n  <data name=\"LayoutPage_Back\" xml:space=\"preserve\">\n    <value>返回应用</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Generatedms\" xml:space=\"preserve\">\n    <value>耗时: {0}毫秒</value>\n  </data>\n  <data name=\"LayoutPage_Footer_Time\" xml:space=\"preserve\">\n    <value>时间:</value>\n  </data>\n  <data name=\"Paginator_Next\" xml:space=\"preserve\">\n    <value>下一页</value>\n  </data>\n  <data name=\"Paginator_Prev\" xml:space=\"preserve\">\n    <value>上一页</value>\n  </data>\n  <data name=\"Paginator_TotalItems\" xml:space=\"preserve\">\n    <value>总条数</value>\n  </data>\n  <data name=\"PerPageSelector_ItemsPerPage\" xml:space=\"preserve\">\n    <value>每页条数</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Aborted\" xml:space=\"preserve\">\n    <value>作业疑似终止</value>\n  </data>\n  <data name=\"ProcessingJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有立即执行的作业</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Table_Started\" xml:space=\"preserve\">\n    <value>执行</value>\n  </data>\n  <data name=\"ProcessingJobsPage_Title\" xml:space=\"preserve\">\n    <value>执行中作业</value>\n  </data>\n  <data name=\"QueuesPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有作业</value>\n  </data>\n  <data name=\"QueuesPage_NoQueues\" xml:space=\"preserve\">\n    <value>队列中不存在。尝试添加作业到队列</value>\n  </data>\n  <data name=\"QueuesPage_Table_Length\" xml:space=\"preserve\">\n    <value>长度</value>\n  </data>\n  <data name=\"QueuesPage_Table_NextsJobs\" xml:space=\"preserve\">\n    <value>下一个作业</value>\n  </data>\n  <data name=\"QueuesPage_Table_Queue\" xml:space=\"preserve\">\n    <value>队列</value>\n  </data>\n  <data name=\"QueuesPage_Title\" xml:space=\"preserve\">\n    <value>队列</value>\n  </data>\n  <data name=\"RecurringJobsPage_Canceled\" xml:space=\"preserve\">\n    <value>取消</value>\n  </data>\n  <data name=\"RecurringJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有作业</value>\n  </data>\n  <data name=\"RecurringJobsPage_RecurringJobDisabled_Tooltip\" xml:space=\"preserve\">\n    <value>Cron 表达式无效或者在未来 100 年都不会发生</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_Cron\" xml:space=\"preserve\">\n    <value>Cron</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_LastExecution\" xml:space=\"preserve\">\n    <value>最后执行</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_NextExecution\" xml:space=\"preserve\">\n    <value>下一个执行</value>\n  </data>\n  <data name=\"RecurringJobsPage_Table_TimeZone\" xml:space=\"preserve\">\n    <value>时区</value>\n  </data>\n  <data name=\"RecurringJobsPage_Title\" xml:space=\"preserve\">\n    <value>定期作业</value>\n  </data>\n  <data name=\"RecurringJobsPage_Triggering\" xml:space=\"preserve\">\n    <value>执行中...</value>\n  </data>\n  <data name=\"RecurringJobsPage_TriggerNow\" xml:space=\"preserve\">\n    <value>立即执行</value>\n  </data>\n  <data name=\"RetriesPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有重试的作业</value>\n  </data>\n  <data name=\"RetriesPage_Title\" xml:space=\"preserve\">\n    <value>重试</value>\n  </data>\n  <data name=\"RetriesPage_Warning_Html\" xml:space=\"preserve\">\n    <value>&lt;h4&gt;重试的工作，但是这个页面无法显示&lt;/h4&gt;&lt;p&gt;别担心，重试执行工作是正常的情况。您当前的作业存储不支持显示此页所需的一些查询。请更新您的存储或完善相关指令集。&lt;/p&gt;&lt;p&gt;请前往&lt; a href =\\\"{0}\\\"&gt;计划作业&lt;/a&gt;页面查看所有预定的工作及重试任务。&lt;/p&gt;</value>\n  </data>\n  <data name=\"ScheduledJobsPage_EnqueueNow\" xml:space=\"preserve\">\n    <value>加入队列</value>\n  </data>\n  <data name=\"ScheduledJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有作业</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Enqueue\" xml:space=\"preserve\">\n    <value>队列</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Table_Scheduled\" xml:space=\"preserve\">\n    <value>计划的</value>\n  </data>\n  <data name=\"ScheduledJobsPage_Title\" xml:space=\"preserve\">\n    <value>计划作业</value>\n  </data>\n  <data name=\"ServersPage_Active\" xml:space=\"preserve\">\n    <value>Active</value>\n  </data>\n  <data name=\"ServersPage_NoServers\" xml:space=\"preserve\">\n    <value>没有活动服务器。后台作业将不会被执行。</value>\n  </data>\n  <data name=\"ServersPage_Note_Text\" xml:space=\"preserve\">\n    <value>没有在最近一分钟内报告心跳的服务器可能会被终止。如果这些服务器不尽快报告心跳信号，会在超时后被自动移除，这不需要人工操作。\n\t在这些服务器上的未完成作业也会被自动重新排入作业队列，但是你也可以通过 &lt;a href=\"{0}\"&gt;执行中作业&lt;/a&gt; 页面加速这个过程。</value>\n  </data>\n  <data name=\"ServersPage_Note_Title\" xml:space=\"preserve\">\n    <value>终止运行的服务器会被自动移除</value>\n  </data>\n  <data name=\"ServersPage_Possibly_Aborted\" xml:space=\"preserve\">\n    <value>可能已终止</value>\n  </data>\n  <data name=\"ServersPage_Table_Heartbeat\" xml:space=\"preserve\">\n    <value>心跳</value>\n  </data>\n  <data name=\"ServersPage_Table_Name\" xml:space=\"preserve\">\n    <value>名称</value>\n  </data>\n  <data name=\"ServersPage_Table_Queues\" xml:space=\"preserve\">\n    <value>队列</value>\n  </data>\n  <data name=\"ServersPage_Table_Started\" xml:space=\"preserve\">\n    <value>执行</value>\n  </data>\n  <data name=\"ServersPage_Table_Workers\" xml:space=\"preserve\">\n    <value>工作区</value>\n  </data>\n  <data name=\"ServersPage_Title\" xml:space=\"preserve\">\n    <value>服务器</value>\n  </data>\n  <data name=\"SucceededJobsPage_NoJobs\" xml:space=\"preserve\">\n    <value>没有作业</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_Succeeded\" xml:space=\"preserve\">\n    <value>完成</value>\n  </data>\n  <data name=\"SucceededJobsPage_Table_TotalDuration\" xml:space=\"preserve\">\n    <value>消耗时间</value>\n  </data>\n  <data name=\"SucceededJobsPage_Title\" xml:space=\"preserve\">\n    <value>完成的作业</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Awaiting\" xml:space=\"preserve\">\n    <value>等待中</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Deleted\" xml:space=\"preserve\">\n    <value>删除</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Failed\" xml:space=\"preserve\">\n    <value>失败</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Processing\" xml:space=\"preserve\">\n    <value>执行中</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Scheduled\" xml:space=\"preserve\">\n    <value>计划</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Succeeded\" xml:space=\"preserve\">\n    <value>完成</value>\n  </data>\n  <data name=\"NavigationMenu_Jobs\" xml:space=\"preserve\">\n    <value>作业</value>\n  </data>\n  <data name=\"NavigationMenu_RecurringJobs\" xml:space=\"preserve\">\n    <value>周期性作业</value>\n  </data>\n  <data name=\"NavigationMenu_Retries\" xml:space=\"preserve\">\n    <value>重试</value>\n  </data>\n  <data name=\"NavigationMenu_Servers\" xml:space=\"preserve\">\n    <value>服务器</value>\n  </data>\n  <data name=\"Common_CannotFindTargetMethod\" xml:space=\"preserve\">\n    <value>找不到目标方法。</value>\n  </data>\n  <data name=\"Common_Enqueued\" xml:space=\"preserve\">\n    <value>队列</value>\n  </data>\n  <data name=\"Common_NoState\" xml:space=\"preserve\">\n    <value>无状态</value>\n  </data>\n  <data name=\"JobsSidebarMenu_Enqueued\" xml:space=\"preserve\">\n    <value>队列</value>\n  </data>\n  <data name=\"Metrics_ActiveConnections\" xml:space=\"preserve\">\n    <value>活动连接</value>\n  </data>\n  <data name=\"Metrics_DeletedJobs\" xml:space=\"preserve\">\n    <value>已删除任务</value>\n  </data>\n  <data name=\"Metrics_FailedJobs\" xml:space=\"preserve\">\n    <value>失败</value>\n  </data>\n  <data name=\"Metrics_ProcessingJobs\" xml:space=\"preserve\">\n    <value>执行中</value>\n  </data>\n  <data name=\"Metrics_RecurringJobs\" xml:space=\"preserve\">\n    <value>定时</value>\n  </data>\n  <data name=\"Metrics_Retries\" xml:space=\"preserve\">\n    <value>重试</value>\n  </data>\n  <data name=\"Metrics_ScheduledJobs\" xml:space=\"preserve\">\n    <value>计划</value>\n  </data>\n  <data name=\"Metrics_Servers\" xml:space=\"preserve\">\n    <value>服务器</value>\n  </data>\n  <data name=\"Metrics_SucceededJobs\" xml:space=\"preserve\">\n    <value>成功的作业</value>\n  </data>\n  <data name=\"Metrics_TotalConnections\" xml:space=\"preserve\">\n    <value>总连接数</value>\n  </data>\n  <data name=\"Common_Condition\" xml:space=\"preserve\">\n    <value>条件</value>\n  </data>\n  <data name=\"Common_Continuations\" xml:space=\"preserve\">\n    <value>持续</value>\n  </data>\n  <data name=\"Metrics_AwaitingCount\" xml:space=\"preserve\">\n    <value>等待中</value>\n  </data>\n  <data name=\"Metrics_EnqueuedCountOrNull\" xml:space=\"preserve\">\n    <value>队列</value>\n  </data>\n  <data name=\"Metrics_EnqueuedQueuesCount\" xml:space=\"preserve\">\n    <value>队列</value>\n  </data>\n  <data name=\"Metrics_FailedCountOrNull\" xml:space=\"preserve\">\n    <value>{0} 没有发现工作任务。请手动重试或删除它们。</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Failed\" xml:space=\"preserve\">\n    <value>失败</value>\n  </data>\n  <data name=\"HomePage_GraphHover_Succeeded\" xml:space=\"preserve\">\n    <value>完成</value>\n  </data>\n</root>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/DashboardContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Text.RegularExpressions;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\n\nnamespace Hangfire.Dashboard\n{\n    /// <summary>\n    /// Provides the context for the Dashboard UI. This class serves as a base class for specific web application frameworks,\n    /// such as ASP.NET Core or OWIN, and is accessible from different Dashboard UI request dispatchers (please see\n    /// <see cref=\"IDashboardDispatcher\"/>), like pages or other endpoints.\n    /// </summary>\n    /// <remarks>\n    /// The <see cref=\"DashboardContext\"/> class encapsulates the HTTP request and response details,\n    /// along with settings and services necessary to process dashboard requests.\n    /// It provides an abstraction that allows easy integration with various web frameworks by inheriting from this class\n    /// and implementing the specific behavior required for those frameworks.\n    /// </remarks>\n    public abstract class DashboardContext\n    {\n        private readonly Lazy<bool> _isReadOnlyLazy;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DashboardContext\"/> class.\n        /// </summary>\n        /// <param name=\"storage\">The job storage used by the Dashboard UI.</param>\n        /// <param name=\"options\">The options for configuring the Dashboard UI.</param>\n        /// <exception cref=\"ArgumentNullException\">\n        /// Thrown when <paramref name=\"storage\"/> or <paramref name=\"options\"/> is null.\n        /// </exception>\n        protected DashboardContext([NotNull] JobStorage storage, [NotNull] DashboardOptions options)\n        {\n            Storage = storage ?? throw new ArgumentNullException(nameof(storage));\n            Options = options ?? throw new ArgumentNullException(nameof(options));\n\n            _isReadOnlyLazy = new Lazy<bool>(() => Options.IsReadOnlyFunc(this));\n        }\n\n        /// <summary>\n        /// Gets the <see cref=\"JobStorage\"/> instance used by the Dashboard UI.\n        /// </summary>\n        public JobStorage Storage { get; }\n\n        /// <summary>\n        /// Gets the <see cref=\"DashboardOptions\"/> for configuring the Dashboard UI.\n        /// </summary>\n        public DashboardOptions Options { get; }\n\n        /// <summary>\n        /// Gets or sets the URI match information passed from the configured <c>pathTemplate</c>\n        /// when defining a route in the <see cref=\"DashboardRoutes\"/> class.\n        /// </summary>\n        public Match UriMatch { get; set; }\n\n        /// <summary>\n        /// Gets the <see cref=\"DashboardRequest\"/> metadata.\n        /// Used by request dispatchers (please see <see cref=\"IDashboardDispatcher\"/>) to provide request information.\n        /// </summary>\n        public DashboardRequest Request { get; protected set; }\n\n        /// <summary>\n        /// Gets the <see cref=\"DashboardResponse\"/> metadata.\n        /// Used by request dispatchers (please see <see cref=\"IDashboardDispatcher\"/>) to send response information.\n        /// </summary>\n        public DashboardResponse Response { get; protected set; }\n\n        /// <summary>\n        /// Gets a value indicating whether the Dashboard UI is in read-only mode to possibly\n        /// hide elements that modify the <see cref=\"JobStorage\"/> instance's data.\n        /// </summary>\n        public bool IsReadOnly => _isReadOnlyLazy.Value;\n\n        /// <summary>\n        /// Gets or sets the anti-forgery header value.\n        /// </summary>\n        public string AntiforgeryHeader { get; set; }\n\n        /// <summary>\n        /// Gets or sets the anti-forgery token value.\n        /// </summary>\n        public string AntiforgeryToken { get; set; }\n\n        /// <summary>\n        /// Gets the background job client for the current <see cref=\"JobStorage\"/> instance.\n        /// </summary>\n        /// <returns>An instance of <see cref=\"IBackgroundJobClient\"/>.</returns>\n        public virtual IBackgroundJobClient GetBackgroundJobClient()\n        {\n            return new BackgroundJobClient(Storage);\n        }\n\n        /// <summary>\n        /// Gets the recurring job manager for the current <see cref=\"JobStorage\"/> instance.\n        /// </summary>\n        /// <returns>An instance of <see cref=\"IRecurringJobManager\"/>.</returns>\n        public virtual IRecurringJobManager GetRecurringJobManager()\n        {\n            return new RecurringJobManager(\n                Storage,\n                JobFilterProviders.Providers,\n                Options.TimeZoneResolver ?? new DefaultTimeZoneResolver());\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/DashboardMetric.cs",
    "content": "// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.Dashboard\n{\n    public class DashboardMetric\n    {\n        public DashboardMetric(string name, Func<RazorPage, Metric> func) \n            : this(name, name, func)\n        {\n        }\n\n        public DashboardMetric(string name, string title, Func<RazorPage, Metric> func)\n        {\n            Name = name;\n            Title = title;\n            Func = func;\n        }\n\n        public string Name { get; }\n        public Func<RazorPage, Metric> Func { get; }\n\n        public string Title { get; set; }\n        public string Url { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/DashboardMetrics.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing Hangfire.Annotations;\nusing Hangfire.Dashboard.Resources;\nusing Hangfire.Storage;\n\nnamespace Hangfire.Dashboard\n{\n    public static class DashboardMetrics\n    {\n        private static readonly Dictionary<string, DashboardMetric> Metrics = new Dictionary<string, DashboardMetric>();\n\n        static DashboardMetrics()\n        {\n            AddMetric(ServerCount);\n            AddMetric(RecurringJobCount);\n            AddMetric(RetriesCount);\n            AddMetric(EnqueuedCountOrNull);\n            AddMetric(FailedCountOrNull);\n            AddMetric(EnqueuedAndQueueCount);\n            AddMetric(ScheduledCount);\n            AddMetric(ProcessingCount);\n            AddMetric(SucceededCount);\n            AddMetric(FailedCount);\n            AddMetric(DeletedCount);\n            AddMetric(AwaitingCount);\n        }\n\n        public static void AddMetric([NotNull] DashboardMetric metric)\n        {\n            if (metric == null) throw new ArgumentNullException(nameof(metric));\n\n            lock (Metrics)\n            {\n                Metrics[metric.Name] = metric;\n            }\n        }\n\n        public static IEnumerable<DashboardMetric> GetMetrics()\n        {\n            lock (Metrics)\n            {\n                return Metrics.Values.ToList();\n            }\n        }\n\n        public static readonly DashboardMetric ServerCount = new DashboardMetric(\n            \"servers:count\", \n            \"Metrics_Servers\",\n            static page => new Metric(page.Statistics.Servers)\n            {\n                Style = page.Statistics.Servers == 0 ? MetricStyle.Warning : MetricStyle.Default,\n                Highlighted = page.Statistics.Servers == 0,\n                Title = page.Statistics.Servers == 0\n                    ? \"No active servers found. Jobs will not be processed.\"\n                    : null\n            });\n\n        public static readonly DashboardMetric RecurringJobCount = new DashboardMetric(\n            \"recurring:count\",\n            \"Metrics_RecurringJobs\",\n            static page => new Metric(page.Statistics.Recurring));\n\n        public static readonly DashboardMetric RetriesCount = new DashboardMetric(\n            \"retries:count\",\n            \"Metrics_Retries\",\n            static page =>\n            {\n                long retryCount;\n\n                if (page.Statistics.Retries.HasValue)\n                {\n                    retryCount = page.Statistics.Retries.Value;\n                }\n                else\n                {\n                    using (var connection = page.Storage.GetReadOnlyConnection())\n                    {\n                        if (!(connection is JobStorageConnection storageConnection))\n                        {\n                            return null;\n                        }\n\n                        retryCount = storageConnection.GetSetCount(\"retries\");\n                    }\n                }\n\n                return new Metric(retryCount)\n                {\n                    Style = retryCount > 0 ? MetricStyle.Warning : MetricStyle.Default\n                };\n            });\n\n        public static readonly DashboardMetric EnqueuedCountOrNull = new DashboardMetric(\n            \"enqueued:count-or-null\",\n            \"Metrics_EnqueuedCountOrNull\",\n            static page => page.Statistics.Enqueued > 0 || page.Statistics.Failed == 0\n                ? new Metric(page.Statistics.Enqueued > 0 ? page.Statistics.Enqueued : page.Statistics.Scheduled)\n                {\n                    Style = page.Statistics.Enqueued + page.Statistics.Scheduled > 0 ? MetricStyle.Info : MetricStyle.Default,\n                    Highlighted = page.Statistics.Enqueued > 0 && page.Statistics.Failed == 0\n                }\n                : null);\n\n        public static readonly DashboardMetric FailedCountOrNull = new DashboardMetric(\n            \"failed:count-or-null\",\n            \"Metrics_FailedJobs\",\n            static page => page.Statistics.Failed > 0\n                ? new Metric(page.Statistics.Failed)\n                {\n                    Style = MetricStyle.Danger,\n                    Highlighted = true,\n                    Title = string.Format(CultureInfo.CurrentCulture, Strings.Metrics_FailedCountOrNull, page.Statistics.Failed)\n                }\n                : null);\n\n        public static readonly DashboardMetric EnqueuedAndQueueCount = new DashboardMetric(\n            \"enqueued-queues:count\",\n            \"Metrics_EnqueuedQueuesCount\",\n            static page => new Metric($\"{page.Statistics.Enqueued:N0} / {page.Statistics.Queues:N0}\")\n            {\n                IntValue = page.Statistics.Enqueued,\n                Style = page.Statistics.Enqueued > 0 ? MetricStyle.Info : MetricStyle.Default,\n                Highlighted = page.Statistics.Enqueued > 0\n            });\n\n        public static readonly DashboardMetric ScheduledCount = new DashboardMetric(\n            \"scheduled:count\",\n            \"Metrics_ScheduledJobs\",\n            static page => new Metric(page.Statistics.Scheduled)\n            {\n                Style = page.Statistics.Scheduled > 0 ? MetricStyle.Info : MetricStyle.Default\n            });\n\n        public static readonly DashboardMetric ProcessingCount = new DashboardMetric(\n            \"processing:count\",\n            \"Metrics_ProcessingJobs\",\n            static page => new Metric(page.Statistics.Processing)\n            {\n                Style = page.Statistics.Processing > 0 ? MetricStyle.Warning : MetricStyle.Default\n            });\n\n        public static readonly DashboardMetric SucceededCount = new DashboardMetric(\n            \"succeeded:count\",\n            \"Metrics_SucceededJobs\",\n            static page => new Metric(page.Statistics.Succeeded));\n\n        public static readonly DashboardMetric FailedCount = new DashboardMetric(\n            \"failed:count\",\n            \"Metrics_FailedJobs\",\n            static page => new Metric(page.Statistics.Failed)\n            {\n                Style = page.Statistics.Failed > 0 ? MetricStyle.Danger : MetricStyle.Default,\n                Highlighted = page.Statistics.Failed > 0\n            });\n\n        public static readonly DashboardMetric DeletedCount = new DashboardMetric(\n            \"deleted:count\",\n            \"Metrics_DeletedJobs\",\n            static page => new Metric(page.Statistics.Deleted));\n\n        public static readonly DashboardMetric AwaitingCount = new DashboardMetric(\n            \"awaiting:count\",\n            \"Metrics_AwaitingCount\",\n            static page =>\n            {\n                long awaitingCount = -1;\n\n                if (page.Statistics.Awaiting.HasValue)\n                {\n                    awaitingCount = page.Statistics.Awaiting.Value;\n                }\n                else\n                {\n                    using (var connection = page.Storage.GetReadOnlyConnection())\n                    {\n                        if (connection is JobStorageConnection storageConnection)\n                        {\n                            awaitingCount = storageConnection.GetSetCount(\"awaiting\");\n                        }\n                    }\n                }\n\n                return new Metric(awaitingCount)\n                {\n                    Style = awaitingCount > 0 ? MetricStyle.Info : MetricStyle.Default\n                };\n            });\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/DashboardRequest.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace Hangfire.Dashboard\n{\n    /// <summary>\n    /// Provides the request details for the Dashboard UI. This class serves as an abstraction for HTTP requests\n    /// and is used within <see cref=\"IDashboardDispatcher\"/> implementations to access request information.\n    /// </summary>\n    /// <remarks>\n    /// The <see cref=\"DashboardRequest\"/> class encapsulates the HTTP request details, providing properties to access\n    /// the method, path, base path, IP addresses, and query or form values.\n    /// This allows for a consistent way to handle requests across different web frameworks.\n    /// </remarks>\n    public abstract class DashboardRequest\n    {\n        /// <summary>\n        /// Gets the HTTP method of the request like <c>\"GET\"</c> or <c>\"POST\"</c>, that can\n        /// be checked for equality by using the <see cref=\"System.StringComparison.OrdinalIgnoreCase\"/> comparer. \n        /// </summary>\n        public abstract string Method { get; }\n\n        /// <summary>\n        /// Gets the request path for the current request that doesn't include the <see cref=\"DashboardOptions.PrefixPath\"/>,\n        /// like <c>\"/jobs/enqueued\"</c>.\n        /// </summary>\n        public abstract string Path { get; }\n\n        /// <summary>\n        /// Gets the base path for the request configured in the request middleware, usually useful\n        /// to reconstruct full URIs like for link generation.\n        /// </summary>\n        public abstract string PathBase { get; }\n\n        /// <summary>\n        /// Gets the local IP address from which the request originated.\n        /// </summary>\n        public abstract string LocalIpAddress { get; }\n\n        /// <summary>\n        /// Gets the remote IP address from which the request originated.\n        /// </summary>\n        public abstract string RemoteIpAddress { get; }\n\n        /// <summary>\n        /// Gets the value of a specific query string parameter.\n        /// </summary>\n        /// <param name=\"key\">The key of the query string parameter.</param>\n        /// <returns>The value of the query string parameter.</returns>\n        public abstract string GetQuery(string key);\n\n        /// <summary>\n        /// Gets the values of a specific form parameter asynchronously, reading the request body if it's a form.\n        /// </summary>\n        /// <param name=\"key\">The key of the form parameter.</param>\n        /// <returns>A task that represents the asynchronous operation. The task result contains the list of values for the form parameter.</returns>\n        public abstract Task<IList<string>> GetFormValuesAsync(string key);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/DashboardResponse.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.IO;\nusing System.Threading.Tasks;\n\nnamespace Hangfire.Dashboard\n{\n    /// <summary>\n    /// Provides the response details for the Dashboard UI. This class serves as an abstraction for HTTP responses\n    /// and is used within <see cref=\"IDashboardDispatcher\"/> implementations to send response information.\n    /// </summary>\n    /// <remarks>\n    /// The <see cref=\"DashboardResponse\"/> class encapsulates the HTTP response details, providing properties and methods\n    /// to set the content type, status code, body, and expiration of the response.\n    /// This allows for a consistent way to handle responses across different web frameworks.\n    /// </remarks>\n    public abstract class DashboardResponse\n    {\n        /// <summary>\n        /// Gets or sets the content type of the response like <c>\"application/json\"</c>.\n        /// </summary>\n        /// <value>The content type of the response.</value>\n        public abstract string ContentType { get; set; }\n\n        /// <summary>\n        /// Gets or sets the HTTP status code of the response like <c>200</c>.\n        /// </summary>\n        /// <value>The status code of the response.</value>\n        public abstract int StatusCode { get; set; }\n\n        /// <summary>\n        /// Gets the response body stream, most of the time it's better to use the\n        /// <see cref=\"WriteAsync\"/> method instead for text data.\n        /// </summary>\n        /// <value>The response body stream.</value>\n        public abstract Stream Body { get; }\n\n        /// <summary>\n        /// Sets the expiration time for the response.\n        /// </summary>\n        /// <param name=\"value\">The expiration time, or <c>null</c> to remove the expiration header.</param>\n        public abstract void SetExpire(DateTimeOffset? value);\n\n        /// <summary>\n        /// Writes the specified text to the response body asynchronously.\n        /// </summary>\n        /// <param name=\"text\">The text to write to the response body.</param>\n        /// <returns>A task that represents the asynchronous operation.</returns>\n        public abstract Task WriteAsync(string text);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/DashboardRoutes.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing Hangfire.Annotations;\nusing Hangfire.Dashboard.Pages;\nusing Hangfire.States;\n\nnamespace Hangfire.Dashboard\n{\n    /// <summary>\n    /// Provides the routing mechanisms for the Dashboard UI. This class is used to register custom\n    /// request dispatchers, allowing developers to write extensions for the Dashboard UI, such as\n    /// custom pages, API endpoints or adding custom JavaScript or CSS files.\n    /// </summary>\n    /// <remarks>\n    /// The <see cref=\"DashboardRoutes\"/> class contains a collection of routes that the Dashboard UI uses to dispatch requests to handlers.\n    /// Developers can use this class to add custom scripts, stylesheets, and register custom routes for extending the dashboard functionality.\n    /// \n    /// To add a custom route, use the <see cref=\"DashboardRoutes.Routes\"/> property which is an instance of <see cref=\"RouteCollection\"/>.\n    /// </remarks>\n    /// <seealso cref=\"RouteCollection\"/>\n    /// <seealso cref=\"IDashboardDispatcher\"/>\n    public static class DashboardRoutes\n    {\n        private static readonly List<Tuple<Assembly, string>> JavaScripts = new List<Tuple<Assembly, string>>();\n        private static readonly List<Tuple<Assembly, string>> Stylesheets = new List<Tuple<Assembly, string>>();\n        private static readonly List<Tuple<Assembly, string>> StylesheetsDarkMode = new List<Tuple<Assembly, string>>();\n\n        internal static volatile int JavaScriptsHashCode;\n        internal static volatile int StylesheetsHashCode;\n        internal static volatile int StylesheetsDarkModeHashCode;\n\n        static DashboardRoutes()\n        {\n            Routes = new RouteCollection();\n            Routes.AddRazorPage(\"/\", static _ => new HomePage());\n            Routes.Add(\"/stats\", new JsonStats());\n\n            var executingAssembly = typeof(DashboardRoutes).GetTypeInfo().Assembly;\n\n            AddStylesheet(executingAssembly, GetContentResourceName(\"css\", \"bootstrap.min.css\"));\n            AddStylesheet(executingAssembly, GetContentResourceName(\"css\", \"Chart.min.css\"));\n            AddStylesheet(executingAssembly, GetContentResourceName(\"css\", \"hangfire.css\"));\n\n            AddStylesheetDarkMode(executingAssembly, GetContentResourceName(\"css\", \"hangfire-dark.css\"));\n\n            AddJavaScript(executingAssembly, GetContentResourceName(\"js\", \"jquery-3.7.1.min.js\"));\n            AddJavaScript(executingAssembly, GetContentResourceName(\"js\", \"bootstrap.min.js\"));\n            AddJavaScript(executingAssembly, GetContentResourceName(\"js\", \"moment-with-locales.min.js\"));\n            AddJavaScript(executingAssembly, GetContentResourceName(\"js\", \"Chart.min.js\"));\n            AddJavaScript(executingAssembly, GetContentResourceName(\"js\", \"chartjs-plugin-streaming.min.js\"));\n            AddJavaScript(executingAssembly, GetContentResourceName(\"js\", \"hangfire.js\"));\n\n            #region Embedded static content\n\n            Routes.Add(\"/js[0-9]+\", new CombinedResourceDispatcher(\"application/javascript\", JavaScripts));\n            Routes.Add(\"/css[0-9]+\", new CombinedResourceDispatcher(\"text/css\", Stylesheets));\n            Routes.Add(\"/css-dark[0-9]+\", new CombinedResourceDispatcher(\"text/css\", StylesheetsDarkMode));\n\n            Routes.Add(\"/fonts/glyphicons-halflings-regular/eot\", new EmbeddedResourceDispatcher(\n                \"application/vnd.ms-fontobject\",\n                executingAssembly,\n                GetContentResourceName(\"fonts\", \"glyphicons-halflings-regular.eot\")));\n\n            Routes.Add(\"/fonts/glyphicons-halflings-regular/svg\", new EmbeddedResourceDispatcher(\n                \"image/svg+xml\",\n                executingAssembly,\n                GetContentResourceName(\"fonts\", \"glyphicons-halflings-regular.svg\")));\n\n            Routes.Add(\"/fonts/glyphicons-halflings-regular/ttf\", new EmbeddedResourceDispatcher(\n                \"application/octet-stream\",\n                executingAssembly,\n                GetContentResourceName(\"fonts\", \"glyphicons-halflings-regular.ttf\")));\n\n            Routes.Add(\"/fonts/glyphicons-halflings-regular/woff\", new EmbeddedResourceDispatcher(\n                \"font/woff\",\n                executingAssembly,\n                GetContentResourceName(\"fonts\", \"glyphicons-halflings-regular.woff\")));\n\n            Routes.Add(\"/fonts/glyphicons-halflings-regular/woff2\", new EmbeddedResourceDispatcher(\n                \"font/woff2\",\n                executingAssembly,\n                GetContentResourceName(\"fonts\", \"glyphicons-halflings-regular.woff2\")));\n\n            #endregion\n\n            #region Razor pages and commands\n\n            Routes.AddRazorPage(\"/jobs/enqueued\", static _ => new QueuesPage());\n            Routes.AddRazorPage(\n                \"/jobs/enqueued/fetched/(?<Queue>.+)\",\n                static x => new FetchedJobsPage(x.Groups[\"Queue\"].Value));\n\n            Routes.AddClientBatchCommand(\"/jobs/enqueued/delete\", static (client, jobId) => client.ChangeState(jobId, CreateDeletedState()));\n            Routes.AddClientBatchCommand(\"/jobs/enqueued/requeue\", static (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState()));\n\n            Routes.AddRazorPage(\n                \"/jobs/enqueued/(?<Queue>.+)\",\n                static x => new EnqueuedJobsPage(x.Groups[\"Queue\"].Value));\n\n            Routes.AddRazorPage(\"/jobs/processing\", static _ => new ProcessingJobsPage());\n            Routes.AddClientBatchCommand(\n                \"/jobs/processing/delete\", \n                static (client, jobId) => client.ChangeState(jobId, CreateDeletedState(), ProcessingState.StateName));\n\n            Routes.AddClientBatchCommand(\n                \"/jobs/processing/requeue\",\n                static (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState(), ProcessingState.StateName));\n\n            Routes.AddRazorPage(\"/jobs/scheduled\", static _ => new ScheduledJobsPage());\n\n            Routes.AddClientBatchCommand(\n                \"/jobs/scheduled/enqueue\", \n                static (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState(), ScheduledState.StateName));\n\n            Routes.AddClientBatchCommand(\n                \"/jobs/scheduled/delete\",\n                static (client, jobId) => client.ChangeState(jobId, CreateDeletedState(), ScheduledState.StateName));\n\n            Routes.AddRazorPage(\"/jobs/succeeded\", static _ => new SucceededJobs());\n            Routes.AddClientBatchCommand(\n                \"/jobs/succeeded/requeue\",\n                static (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState(), SucceededState.StateName));\n\n            Routes.AddRazorPage(\"/jobs/failed\", static _ => new FailedJobsPage());\n\n            Routes.AddClientBatchCommand(\n                \"/jobs/failed/requeue\",\n                static (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState(), FailedState.StateName));\n\n            Routes.AddClientBatchCommand(\n                \"/jobs/failed/delete\",\n                static (client, jobId) => client.ChangeState(jobId, CreateDeletedState(), FailedState.StateName));\n\n            Routes.AddRazorPage(\"/jobs/deleted\", static _ => new DeletedJobsPage());\n\n            Routes.AddClientBatchCommand(\n                \"/jobs/deleted/requeue\",\n                static (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState(), DeletedState.StateName));\n\n            Routes.AddRazorPage(\"/jobs/awaiting\", static _ => new AwaitingJobsPage());\n            Routes.AddClientBatchCommand(\"/jobs/awaiting/enqueue\", static (client, jobId) => client.ChangeState(\n                jobId, CreateEnqueuedState(), AwaitingState.StateName));\n            Routes.AddClientBatchCommand(\"/jobs/awaiting/delete\", static (client, jobId) => client.ChangeState(\n                jobId, CreateDeletedState(), AwaitingState.StateName));\n\n            Routes.AddCommand(\n                \"/jobs/actions/requeue/(?<JobId>.+)\",\n                static context =>\n                {\n                    var client = context.GetBackgroundJobClient();\n                    return client.ChangeState(context.UriMatch.Groups[\"JobId\"].Value, CreateEnqueuedState());\n                });\n\n            Routes.AddCommand(\n                \"/jobs/actions/delete/(?<JobId>.+)\",\n                static context =>\n                {\n                    var client = context.GetBackgroundJobClient();\n                    return client.ChangeState(context.UriMatch.Groups[\"JobId\"].Value, CreateDeletedState());\n                });\n\n            Routes.AddRazorPage(\"/jobs/details/(?<JobId>.+)\", static x => new JobDetailsPage(x.Groups[\"JobId\"].Value));\n\n            Routes.AddRazorPage(\"/recurring\", x => new RecurringJobsPage());\n            Routes.AddRecurringBatchCommand(\n                \"/recurring/remove\", \n                static (manager, jobId) => manager.RemoveIfExists(jobId));\n\n            Routes.AddRecurringBatchCommand(\n                \"/recurring/trigger\", \n                static (manager, jobId) => manager.Trigger(jobId));\n\n            Routes.AddRazorPage(\"/servers\", static _ => new ServersPage());\n            Routes.AddRazorPage(\"/retries\", static _ => new RetriesPage());\n\n            #endregion\n        }\n\n        /// <summary>\n        /// Gets the collection of routes for the Dashboard UI. Use this property to register\n        /// custom request dispatchers.\n        /// </summary>\n        public static RouteCollection Routes { get; }\n\n        /// <summary>\n        /// Adds a stylesheet resource embedded into the given assembly to be included in the dashboard.\n        /// </summary>\n        /// <remarks>\n        /// The specified resource should be an embedded resource file within the referenced assembly.\n        /// You can discover embedded resource names by calling the <c>assembly.GetManifestResourceNames()</c> method.\n        /// </remarks>\n        /// <param name=\"assembly\">The assembly containing the embedded stylesheet resource.</param>\n        /// <param name=\"resource\">The name of the stylesheet embedded resource.</param>\n        /// <exception cref=\"ArgumentNullException\">Thrown if <paramref name=\"assembly\"/> or <paramref name=\"resource\"/> is <c>null</c>.</exception>\n        public static void AddStylesheet([NotNull] Assembly assembly, [NotNull] string resource)\n        {\n            if (assembly == null) throw new ArgumentNullException(nameof(assembly));\n            if (resource == null) throw new ArgumentNullException(nameof(resource));\n\n            lock (Stylesheets)\n            {\n                Stylesheets.Add(Tuple.Create(assembly, resource));\n                StylesheetsHashCode ^= resource.GetHashCode();\n            }\n        }\n\n        /// <summary>\n        /// Adds a resource embedded into the given assembly that will only be included in the dashboard\n        /// when the <see cref=\"DashboardOptions.DarkModeEnabled\"/> is set to <c>true</c>.\n        /// </summary>\n        /// <remarks>\n        /// The specified resource should be an embedded resource file within the referenced assembly.\n        /// You can discover embedded resource names by calling the <c>assembly.GetManifestResourceNames()</c> method.\n        /// </remarks>\n        /// <param name=\"assembly\">The assembly containing the dark-mode stylesheet embedded resource.</param>\n        /// <param name=\"resource\">The name of the dark-mode stylesheet embedded resource.</param>\n        /// <exception cref=\"ArgumentNullException\">Thrown if <paramref name=\"assembly\"/> or <paramref name=\"resource\"/> is <c>null</c>.</exception>\n        public static void AddStylesheetDarkMode([NotNull] Assembly assembly, [NotNull] string resource)\n        {\n            if (assembly == null) throw new ArgumentNullException(nameof(assembly));\n            if (resource == null) throw new ArgumentNullException(nameof(resource));\n\n            lock (StylesheetsDarkMode)\n            {\n                StylesheetsDarkMode.Add(Tuple.Create(assembly, resource));\n                StylesheetsDarkModeHashCode ^= resource.GetHashCode();\n            }\n        }\n\n        /// <summary>\n        /// Adds a JavaScript resource embedded into the given assembly to be included in the dashboard.\n        /// </summary>\n        /// <remarks>\n        /// The specified resource should be an embedded resource file within the referenced assembly.\n        /// You can discover embedded resource names by calling the <c>assembly.GetManifestResourceNames()</c> method.\n        /// </remarks>\n        /// <param name=\"assembly\">The assembly containing the JavaScript embedded resource.</param>\n        /// <param name=\"resource\">The name of the JavaScript embedded resource.</param>\n        /// <exception cref=\"ArgumentNullException\">Thrown if <paramref name=\"assembly\"/> or <paramref name=\"resource\"/> is <c>null</c>.</exception>\n        public static void AddJavaScript([NotNull] Assembly assembly, [NotNull] string resource)\n        {\n            if (assembly == null) throw new ArgumentNullException(nameof(assembly));\n            if (resource == null) throw new ArgumentNullException(nameof(resource));\n\n            lock (JavaScripts)\n            {\n                JavaScripts.Add(Tuple.Create(assembly, resource));\n                JavaScriptsHashCode ^= resource.GetHashCode();\n            }\n        }\n\n        internal static string GetContentFolderNamespace(string contentFolder)\n        {\n            return $\"{typeof (DashboardRoutes).Namespace}.Content.{contentFolder}\";\n        }\n\n        internal static string GetContentResourceName(string contentFolder, string resourceName)\n        {\n            return $\"{GetContentFolderNamespace(contentFolder)}.{resourceName}\";\n        }\n\n        private static DeletedState CreateDeletedState()\n        {\n            return new DeletedState { Reason = \"Triggered via Dashboard UI\" };\n        }\n\n        private static EnqueuedState CreateEnqueuedState()\n        {\n            return new EnqueuedState { Reason = \"Triggered via Dashboard UI\" };\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/EmbeddedResourceDispatcher.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Reflection;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard\n{\n    internal class EmbeddedResourceDispatcher : IDashboardDispatcher\n    {\n        private readonly Assembly _assembly;\n        private readonly string _resourceName;\n        private readonly string _contentType;\n\n        public EmbeddedResourceDispatcher(\n            [NotNull] string contentType,\n            [CanBeNull] Assembly assembly, \n            [CanBeNull] string resourceName)\n        {\n            if (contentType == null) throw new ArgumentNullException(nameof(contentType));\n            \n            _assembly = assembly;\n            _resourceName = resourceName;\n            _contentType = contentType;\n        }\n\n        public async Task Dispatch(DashboardContext context)\n        {\n            context.Response.ContentType = _contentType;\n            context.Response.SetExpire(DateTimeOffset.Now.AddYears(1));\n\n            await WriteResponse(context.Response).ConfigureAwait(false);\n        }\n\n        protected virtual Task WriteResponse(DashboardResponse response)\n        {\n            return WriteResource(response, _assembly, _resourceName);\n        }\n\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Performance\", \"CA1822:Mark members as static\", Justification = \"Context can be potentially accessed in derived classes.\")]\n        protected async Task WriteResource(DashboardResponse response, Assembly assembly, string resourceName)\n        {\n            using (var inputStream = assembly.GetManifestResourceStream(resourceName))\n            {\n                if (inputStream == null)\n                {\n                    throw new ArgumentException($@\"Resource with name {resourceName} not found in assembly {assembly}.\");\n                }\n\n                await inputStream.CopyToAsync(response.Body).ConfigureAwait(false);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/HtmlHelper.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Net;\nusing System.Text;\nusing Hangfire.Common;\nusing System.ComponentModel;\nusing System.Globalization;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing System.Text.RegularExpressions;\nusing Hangfire.Annotations;\nusing Hangfire.Dashboard.Pages;\nusing Hangfire.Dashboard.Resources;\n\nnamespace Hangfire.Dashboard\n{\n    [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Performance\", \"CA1822:Mark members as static\", Justification = \"We use instance methods in this class for better observability.\")]\n    public class HtmlHelper\n    {\n        private static readonly Type DisplayNameType;\n        private static readonly Func<object, string> GetDisplayName;\n\n        private readonly RazorPage _page;\n\n        static HtmlHelper()\n        {\n            try\n            {\n#if !NETSTANDARD1_3\n                DisplayNameType = typeof(DisplayNameAttribute);\n#else\n                DisplayNameType = Type.GetType(\"System.ComponentModel.DisplayNameAttribute, System.ComponentModel.Primitives\");\n#endif\n                if (DisplayNameType == null) return;\n\n                var p = Expression.Parameter(typeof(object));\n                var converted = Expression.Convert(p, DisplayNameType);\n\n                GetDisplayName = Expression.Lambda<Func<object, string>>(Expression.Call(converted, \"get_DisplayName\", null), p).Compile();\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                // Ignoring\n            }\n        }\n\n        public HtmlHelper([NotNull] RazorPage page)\n        {\n            if (page == null) throw new ArgumentNullException(nameof(page));\n            _page = page;\n        }\n\n        public RazorPage Page => _page;\n\n        public NonEscapedString Breadcrumbs(string title, [NotNull] IDictionary<string, string> items)\n        {\n            if (items == null) throw new ArgumentNullException(nameof(items));\n            return RenderPartial(new Breadcrumbs(title, items));\n        }\n\n        public NonEscapedString JobsSidebar()\n        {\n            return RenderPartial(new SidebarMenu(JobsSidebarMenu.Items));\n        }\n\n        public NonEscapedString SidebarMenu([NotNull] IEnumerable<Func<RazorPage, MenuItem>> items)\n        {\n            if (items == null) throw new ArgumentNullException(nameof(items));\n            return RenderPartial(new SidebarMenu(items));\n        }\n\n        public NonEscapedString BlockMetric([NotNull] DashboardMetric metric)\n        {\n            if (metric == null) throw new ArgumentNullException(nameof(metric));\n            return RenderPartial(new BlockMetric(metric));\n        }\n\n        public NonEscapedString InlineMetric([NotNull] DashboardMetric metric)\n        {\n            if (metric == null) throw new ArgumentNullException(nameof(metric));\n            return RenderPartial(new InlineMetric(metric));\n        }\n\n        public NonEscapedString Paginator([NotNull] Pager pager)\n        {\n            if (pager == null) throw new ArgumentNullException(nameof(pager));\n            return RenderPartial(new Paginator(pager));\n        }\n\n        public NonEscapedString PerPageSelector([NotNull] Pager pager)\n        {\n            if (pager == null) throw new ArgumentNullException(nameof(pager));\n            return RenderPartial(new PerPageSelector(pager));\n        }\n\n        public NonEscapedString RenderPartial(RazorPage partialPage)\n        {\n            partialPage.Assign(_page);\n            return new NonEscapedString(partialPage.ToString());\n        }\n\n        public NonEscapedString Raw(string value)\n        {\n            return new NonEscapedString(value);\n        }\n\n        public NonEscapedString JobId(string jobId, bool shorten = true)\n        {\n            Guid guid;\n            return new NonEscapedString(HtmlEncode(Guid.TryParse(jobId, out guid)\n                ? (shorten ? jobId.Substring(0, 8) + \"…\" : jobId)\n                : $\"#{jobId}\"));\n        }\n\n        public string JobName(Job job)\n        {\n            return JobName(job, includeQueue: true);\n        }\n\n        public string JobName(Job job, bool includeQueue)\n        {\n            if (job == null)\n            {\n                return Strings.Common_CannotFindTargetMethod;\n            }\n\n            var jobDisplayNameAttribute = job.Method.GetCustomAttribute<JobDisplayNameAttribute>();\n            if (jobDisplayNameAttribute != null)\n            {\n                try\n                {\n                    return jobDisplayNameAttribute.Format(_page.Context, job);\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    return jobDisplayNameAttribute.DisplayName;\n                }\n            }\n\n            if (DisplayNameType != null && GetDisplayName != null)\n            {\n                var attribute = job.Method.GetCustomAttribute(DisplayNameType);\n                if (attribute != null)\n                {\n                    try\n                    {\n                        return String.Format(\n                            CultureInfo.CurrentCulture,\n                            GetDisplayName(attribute),\n                            job.Args.ToArray());\n                    }\n                    catch (FormatException)\n                    {\n                        return GetDisplayName(attribute);\n                    }\n                }\n            }\n\n            var displayNameProvider = _page.DashboardOptions.DisplayNameFunc;\n            if (displayNameProvider != null)\n            {\n                try\n                {\n                    return displayNameProvider(_page.Context, job);\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    // Ignoring exceptions\n                }\n            }\n\n            return job.ToString(includeQueue);\n        }\n\n        public NonEscapedString StateLabel(string stateName)\n        {\n            return StateLabel(stateName, stateName);\n        }\n\n        public NonEscapedString StateLabel(string stateName, string text, bool hover = false)\n        {\n            if (String.IsNullOrWhiteSpace(stateName))\n            {\n                return Raw($\"<em>{HtmlEncode(Strings.Common_NoState)}</em>\");\n            }\n\n            var style = $\"background-color: {JobHistoryRenderer.GetForegroundStateColor(stateName)};\";\n            var cssSuffix = JobHistoryRenderer.GetStateCssSuffix(stateName);\n            var cssHover = hover ? \"label-hover\" : null;\n\n            if (cssSuffix != null)\n            {\n                return Raw($\"<span class=\\\"label label-default {cssHover} label-state-{HtmlEncode(cssSuffix)}\\\">{HtmlEncode(text)}</span>\");\n            }\n\n            return Raw($\"<span class=\\\"label label-default {cssHover}\\\" style=\\\"{HtmlEncode(style)}\\\">{HtmlEncode(text)}</span>\");\n        }\n\n        public NonEscapedString JobIdLink(string jobId)\n        {\n            return Raw($\"<a href=\\\"{HtmlEncode(_page.Url.JobDetails(jobId))}\\\">{JobId(jobId)}</a>\");\n        }\n\n        public NonEscapedString JobNameLink(string jobId, Job job)\n        {\n            return JobNameLink(jobId, job, includeQueue: true);\n        }\n\n        public NonEscapedString JobNameLink(string jobId, Job job, bool includeQueue)\n        {\n            return Raw($\"<a class=\\\"job-method\\\" href=\\\"{HtmlEncode(_page.Url.JobDetails(jobId))}\\\">{HtmlEncode(JobName(job, includeQueue))}</a>\");\n        }\n\n        public NonEscapedString RelativeTime(DateTime value)\n        {\n            return Raw($\"<span data-moment=\\\"{HtmlEncode(JobHelper.ToTimestamp(value).ToString(CultureInfo.InvariantCulture))}\\\">{HtmlEncode(value.ToString(CultureInfo.CurrentCulture))}</span>\");\n        }\n\n        public NonEscapedString MomentTitle(DateTime time, string value)\n        {\n            return Raw($\"<span data-moment-title=\\\"{HtmlEncode(JobHelper.ToTimestamp(time).ToString(CultureInfo.InvariantCulture))}\\\">{HtmlEncode(value)}</span>\");\n        }\n\n        public NonEscapedString LocalTime(DateTime value)\n        {\n            return Raw($\"<span data-moment-local=\\\"{HtmlEncode(JobHelper.ToTimestamp(value).ToString(CultureInfo.InvariantCulture))}\\\">{HtmlEncode(value.ToString(CultureInfo.CurrentCulture))}</span>\");\n        }\n\n        public string ToHumanDuration(TimeSpan? duration, bool displaySign = true)\n        {\n            if (duration == null) return null;\n\n            var builder = new StringBuilder();\n            if (displaySign)\n            {\n                builder.Append(duration.Value.TotalMilliseconds < 0 ? \"-\" : \"+\");\n            }\n\n            duration = duration.Value.Duration();\n\n            if (duration.Value.Days > 0)\n            {\n                builder.Append($\"{duration.Value.Days}d \");\n            }\n\n            if (duration.Value.Hours > 0)\n            {\n                builder.Append($\"{duration.Value.Hours}h \");\n            }\n\n            if (duration.Value.Minutes > 0)\n            {\n                builder.Append($\"{duration.Value.Minutes}m \");\n            }\n\n            if (duration.Value.TotalHours < 1)\n            {\n                if (duration.Value.Seconds > 0)\n                {\n                    builder.Append(duration.Value.Seconds);\n                    if (duration.Value.Milliseconds > 0)\n                    {\n                        builder.Append($\".{duration.Value.Milliseconds.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0')}\");\n                    }\n\n                    builder.Append(\"s \");\n                }\n                else\n                {\n                    if (duration.Value.Milliseconds > 0)\n                    {\n                        builder.Append($\"{duration.Value.Milliseconds}ms \");\n                    }\n                }\n            }\n\n            if (builder.Length <= 1)\n            {\n                builder.Append(\" <1ms \");\n            }\n\n            builder.Remove(builder.Length - 1, 1);\n\n            return builder.ToString();\n        }\n\n        [Obsolete(\"This method is unused and will be removed in 2.0.0.\")]\n        public string FormatProperties(IDictionary<string, string> properties)\n        {\n            return String.Join(\", \", properties.Select(static x => $\"{x.Key}: \\\"{x.Value}\\\"\"));\n        }\n\n        public NonEscapedString QueueLabel(string queue)\n        {\n            var label = queue != null \n                ? $\"<a href=\\\"{HtmlEncode(_page.Url.Queue(queue))}\\\">{HtmlEncode(queue)}</a>\" \n                : $\"<span class=\\\"label label-danger\\\"><i>{HtmlEncode(Strings.Common_Unknown)}</i></span>\";\n\n            return new NonEscapedString(label);\n        }\n\n        public NonEscapedString ServerId(string serverId)\n        {\n            var parts = serverId.Split(':');\n            var shortenedId = parts.Length > 1\n                ? String.Join(\":\", parts.Take(parts.Length - 1))\n                : serverId;\n\n            return new NonEscapedString(\n                $\"<span class=\\\"labe label-defult text-uppercase\\\" title=\\\"{HtmlEncode(serverId)}\\\">{HtmlEncode(shortenedId)}</span>\");\n        }\n\n        private static readonly StackTraceHtmlFragments StackTraceHtmlFragments = new StackTraceHtmlFragments\n        {\n            BeforeFrame         = \"<span class='st-frame'>\"                            , AfterFrame         = \"</span>\",\n            BeforeType          = \"<span class='st-type'>\"                             , AfterType          = \"</span>\",\n            BeforeMethod        = \"<span class='st-method'>\"                           , AfterMethod        = \"</span>\",\n            BeforeParameters    = \"<span class='st-params'>\"                           , AfterParameters    = \"</span>\",\n            BeforeParameterType = \"<span class='st-param'><span class='st-param-type'>\", AfterParameterType = \"</span>\",\n            BeforeParameterName = \"<span class='st-param-name'>\"                       , AfterParameterName = \"</span></span>\",\n            BeforeFile          = \"<span class='st-file'>\"                             , AfterFile          = \"</span>\",\n            BeforeLine          = \"<span class='st-line'>\"                             , AfterLine          = \"</span>\",\n        };\n\n        public NonEscapedString StackTrace(string stackTrace)\n        {\n            try\n            {\n                return new NonEscapedString(StackTraceFormatter.FormatHtml(stackTrace, StackTraceHtmlFragments));\n            }\n            catch (RegexMatchTimeoutException)\n            {\n                return new NonEscapedString(HtmlEncode(stackTrace));\n            }\n        }\n\n        public string HtmlEncode(string text)\n        {\n            return WebUtility.HtmlEncode(text);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/IDashboardAsyncAuthorizationFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2021 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard\n{\n    public interface IDashboardAsyncAuthorizationFilter\n    {\n        Task<bool> AuthorizeAsync([NotNull] DashboardContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/IDashboardAuthorizationFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard\n{\n    public interface IDashboardAuthorizationFilter\n    {\n        bool Authorize([NotNull] DashboardContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/IDashboardDispatcher.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard\n{\n    /// <summary>\n    /// Defines the method for dispatching requests within the Dashboard UI.\n    /// Implementations of this interface handle incoming requests to the dashboard and produce appropriate responses.\n    /// </summary>\n    /// <remarks>\n    /// The <see cref=\"IDashboardDispatcher\"/> interface is used to process requests in the Dashboard UI.\n    /// Implement this interface to handle custom routes and manage request processing logic.\n    /// \n    /// To register custom dispatchers, use the <see cref=\"DashboardRoutes\"/> class. The <c>DashboardRoutes.Routes</c>\n    /// property allows for adding new routes that the dashboard will use to dispatch requests to custom handlers.\n    /// </remarks>\n    /// <seealso cref=\"DashboardContext\"/>\n    /// <seealso cref=\"DashboardRoutes\"/>\n    public interface IDashboardDispatcher\n    {\n        /// <summary>\n        /// Processes the request within the provided <see cref=\"DashboardContext\"/>.\n        /// </summary>\n        /// <param name=\"context\">The context for the current dashboard request, containing information about the request and response.</param>\n        /// <returns>A task that represents the asynchronous dispatch operation.</returns>\n        Task Dispatch([NotNull] DashboardContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/JobDetailsRenderer.cs",
    "content": "// This file is part of Hangfire.\n// Copyright © 2020 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Storage.Monitoring;\n\nnamespace Hangfire.Dashboard\n{\n    public sealed class JobDetailsRendererDto\n    {\n        public JobDetailsRendererDto([NotNull] RazorPage page, [NotNull] string jobId, [NotNull] JobDetailsDto jobDetails)\n        {\n            Page = page ?? throw new ArgumentNullException(nameof(page));\n            JobId = jobId ?? throw new ArgumentNullException(nameof(jobId));\n            JobDetails = jobDetails ?? throw new ArgumentNullException(nameof(jobDetails));\n        }\n        \n        public RazorPage Page { get; }\n        public string JobId { get; }\n        public JobDetailsDto JobDetails { get; }\n    }\n\n    internal static class JobDetailsRenderer\n    {\n        private static readonly object SyncRoot = new object();\n        private static readonly List<Tuple<int, Func<JobDetailsRendererDto, NonEscapedString>>> Renderers = \n            new List<Tuple<int, Func<JobDetailsRendererDto, NonEscapedString>>>();\n\n        public static IEnumerable<Tuple<int, Func<JobDetailsRendererDto, NonEscapedString>>> GetRenderers()\n        {\n            lock (SyncRoot)\n            {\n                return Renderers.AsReadOnly();\n            }\n        }\n\n        public static void AddRenderer(int order, Func<JobDetailsRendererDto, NonEscapedString> renderer)\n        {\n            lock (SyncRoot)\n            {\n                Renderers.Add(Tuple.Create(order, renderer));\n                Renderers.Sort(static (x, y) => x.Item1.CompareTo(y.Item1));\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/JobHistoryRenderer.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Text;\nusing Hangfire.Common;\nusing Hangfire.States;\n\nnamespace Hangfire.Dashboard\n{\n    public static class JobHistoryRenderer\n    {\n        private static readonly IDictionary<string, Func<HtmlHelper, IDictionary<string, string>, NonEscapedString>>\n            Renderers = new Dictionary<string, Func<HtmlHelper, IDictionary<string, string>, NonEscapedString>>();\n\n        private static readonly IDictionary<string, string> BackgroundStateColors\n            = new Dictionary<string, string>();\n        private static readonly IDictionary<string, string> ForegroundStateColors\n            = new Dictionary<string, string>();\n        private static readonly IDictionary<string, string> StateCssSuffixes\n            = new Dictionary<string, string>();\n\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Performance\", \"CA1810:InitializeReferenceTypeStaticFieldsInline\")]\n        static JobHistoryRenderer()\n        {\n            Register(SucceededState.StateName, SucceededRenderer);\n            Register(FailedState.StateName, FailedRenderer);\n            Register(ProcessingState.StateName, ProcessingRenderer);\n            Register(EnqueuedState.StateName, EnqueuedRenderer);\n            Register(ScheduledState.StateName, ScheduledRenderer);\n            Register(DeletedState.StateName, DeletedRenderer);\n            Register(AwaitingState.StateName, AwaitingRenderer);\n\n            BackgroundStateColors.Add(EnqueuedState.StateName, \"#F5F5F5\");\n            BackgroundStateColors.Add(SucceededState.StateName, \"#EDF7ED\");\n            BackgroundStateColors.Add(FailedState.StateName, \"#FAEBEA\");\n            BackgroundStateColors.Add(ProcessingState.StateName, \"#FCEFDC\");\n            BackgroundStateColors.Add(ScheduledState.StateName, \"#E0F3F8\");\n            BackgroundStateColors.Add(DeletedState.StateName, \"#ddd\");\n            BackgroundStateColors.Add(AwaitingState.StateName, \"#E0F3F8\");\n\n            ForegroundStateColors.Add(EnqueuedState.StateName, \"#999\");\n            ForegroundStateColors.Add(SucceededState.StateName, \"#5cb85c\");\n            ForegroundStateColors.Add(FailedState.StateName, \"#d9534f\");\n            ForegroundStateColors.Add(ProcessingState.StateName, \"#f0ad4e\");\n            ForegroundStateColors.Add(ScheduledState.StateName, \"#5bc0de\");\n            ForegroundStateColors.Add(DeletedState.StateName, \"#777\");\n            ForegroundStateColors.Add(AwaitingState.StateName, \"#5bc0de\");\n\n            StateCssSuffixes.Add(EnqueuedState.StateName, \"active\");\n            StateCssSuffixes.Add(SucceededState.StateName, \"success\");\n            StateCssSuffixes.Add(FailedState.StateName, \"danger\");\n            StateCssSuffixes.Add(ProcessingState.StateName, \"warning\");\n            StateCssSuffixes.Add(ScheduledState.StateName, \"info\");\n            StateCssSuffixes.Add(DeletedState.StateName, \"inactive\");\n            StateCssSuffixes.Add(AwaitingState.StateName, \"info\");\n        }\n\n        [Obsolete(\"Use `AddStateCssSuffix` method's logic instead. Will be removed in 2.0.0.\")]\n        public static void AddBackgroundStateColor(string stateName, string color)\n        {\n            BackgroundStateColors.Add(stateName, color);\n        }\n\n        public static string GetBackgroundStateColor(string stateName)\n        {\n            if (stateName == null || !BackgroundStateColors.TryGetValue(stateName, out var color))\n            {\n                return \"inherit\";\n            }\n\n            return color;\n        }\n\n        [Obsolete(\"Use `AddStateCssSuffix` method's logic instead. Will be removed in 2.0.0.\")]\n        public static void AddForegroundStateColor(string stateName, string color)\n        {\n            ForegroundStateColors.Add(stateName, color);\n        }\n\n        public static string GetForegroundStateColor(string stateName)\n        {\n            if (stateName == null || !ForegroundStateColors.TryGetValue(stateName, out var color))\n            {\n                return \"inherit\";\n            }\n\n            return color;\n        }\n\n        public static void AddStateCssSuffix(string stateName, string color)\n        {\n            StateCssSuffixes.Add(stateName, color);\n        }\n\n        public static string GetStateCssSuffix(string stateName)\n        {\n            if (stateName == null || !StateCssSuffixes.TryGetValue(stateName, out var suffix))\n            {\n                return \"inherit\";\n            }\n\n            return suffix;\n        }\n\n        public static void Register(string state, Func<HtmlHelper, IDictionary<string, string>, NonEscapedString> renderer)\n        {\n            if (!Renderers.ContainsKey(state))\n            {\n                Renderers.Add(state, renderer);\n            }\n            else\n            {\n                Renderers[state] = renderer;\n            }\n        }\n\n        public static bool Exists(string state)\n        {\n            return Renderers.ContainsKey(state);\n        }\n\n        public static NonEscapedString RenderHistory(\n            this HtmlHelper helper,\n            string state, IDictionary<string, string> properties)\n        {\n            var renderer = Renderers.TryGetValue(state, out var value)\n                ? value\n                : DefaultRenderer;\n\n            return renderer?.Invoke(helper, properties);\n        }\n\n        public static NonEscapedString NullRenderer(HtmlHelper helper, IDictionary<string, string> properties)\n        {\n            return null;\n        }\n\n        public static NonEscapedString DefaultRenderer(HtmlHelper helper, IDictionary<string, string> stateData)\n        {\n            if (stateData == null || stateData.Count == 0) return null;\n\n            var builder = new StringBuilder();\n            builder.Append(\"<dl class=\\\"dl-horizontal\\\">\");\n\n            foreach (var item in stateData)\n            {\n                builder.Append($\"<dt>{helper.HtmlEncode(item.Key)}</dt>\");\n                builder.Append($\"<dd>{helper.HtmlEncode(item.Value)}</dd>\");\n            }\n\n            builder.Append(\"</dl>\");\n\n            return new NonEscapedString(builder.ToString());\n        }\n\n        public static NonEscapedString SucceededRenderer(HtmlHelper html, IDictionary<string, string> stateData)\n        {\n            var builder = new StringBuilder();\n            builder.Append(\"<dl class=\\\"dl-horizontal\\\">\");\n\n            var itemsAdded = false;\n\n            if (stateData.TryGetValue(\"Latency\", out var latencyString))\n            {\n                var latency = TimeSpan.FromMilliseconds(long.Parse(latencyString, CultureInfo.InvariantCulture));\n\n                builder.Append($\"<dt>Latency:</dt><dd>{html.HtmlEncode(html.ToHumanDuration(latency, false))}</dd>\");\n\n                itemsAdded = true;\n            }\n\n            if (stateData.TryGetValue(\"PerformanceDuration\", out var durationString))\n            {\n                var duration = TimeSpan.FromMilliseconds(long.Parse(durationString, CultureInfo.InvariantCulture));\n                builder.Append($\"<dt>Duration:</dt><dd>{html.HtmlEncode(html.ToHumanDuration(duration, false))}</dd>\");\n\n                itemsAdded = true;\n            }\n\n\n            if (stateData.TryGetValue(\"Result\", out var resultString) && !String.IsNullOrWhiteSpace(resultString))\n            {\n                var result = stateData[\"Result\"];\n                builder.Append($\"<dt>Result:</dt><dd>{html.HtmlEncode(result)}</dd>\");\n\n                itemsAdded = true;\n            }\n\n            builder.Append(\"</dl>\");\n\n            if (!itemsAdded) return null;\n\n            return new NonEscapedString(builder.ToString());\n        }\n\n        private static NonEscapedString FailedRenderer(HtmlHelper html, IDictionary<string, string> stateData)\n        {\n            var builder = new StringBuilder();\n            var serverId = stateData.TryGetValue(\"ServerId\", out var value) ? $\" ({html.ServerId(value)})\" : null;\n\n            builder.Append(\n                $\"<h4 class=\\\"exception-type\\\">{html.HtmlEncode(stateData[\"ExceptionType\"])}{serverId}</h4><p class=\\\"text-muted\\\">{html.HtmlEncode(stateData[\"ExceptionMessage\"])}</p>\");\n\n            if (stateData.TryGetValue(\"ExceptionDetails\", out var details) && !String.IsNullOrWhiteSpace(details))\n            {\n                var stackTrace = html.StackTrace(details).ToString();\n                builder.Append($\"<pre class=\\\"stack-trace\\\">{stackTrace}</pre>\");\n            }\n\n            return new NonEscapedString(builder.ToString());\n        }\n\n        private static NonEscapedString ProcessingRenderer(HtmlHelper helper, IDictionary<string, string> stateData)\n        {\n            var builder = new StringBuilder();\n            builder.Append(\"<dl class=\\\"dl-horizontal\\\">\");\n\n            if (!stateData.TryGetValue(\"ServerId\", out var serverId))\n            {\n                stateData.TryGetValue(\"ServerName\", out serverId);\n            }\n\n            if (serverId != null)\n            {\n                builder.Append(\"<dt>Server:</dt>\");\n                builder.Append($\"<dd>{helper.ServerId(serverId)}</dd>\");\n            }\n\n            if (stateData.TryGetValue(\"WorkerId\", out var workerId))\n            {\n                builder.Append(\"<dt>Worker:</dt>\");\n                builder.Append($\"<dd>{helper.HtmlEncode(workerId.Substring(0, 8))}</dd>\");\n            }\n            else if (stateData.TryGetValue(\"WorkerNumber\", out var workerNumber))\n            {\n                builder.Append(\"<dt>Worker:</dt>\");\n                builder.Append($\"<dd>#{helper.HtmlEncode(workerNumber)}</dd>\");\n            }\n\n            builder.Append(\"</dl>\");\n\n            return new NonEscapedString(builder.ToString());\n        }\n\n        private static NonEscapedString EnqueuedRenderer(HtmlHelper helper, IDictionary<string, string> stateData)\n        {\n            if (!EnqueuedState.DefaultQueue.Equals(stateData[\"Queue\"], StringComparison.OrdinalIgnoreCase))\n            {\n                return new NonEscapedString(\n                    $\"<dl class=\\\"dl-horizontal\\\"><dt>Queue:</dt><dd>{helper.QueueLabel(stateData[\"Queue\"])}</dd></dl>\");\n            }\n\n            return null;\n        }\n\n        private static NonEscapedString ScheduledRenderer(HtmlHelper helper, IDictionary<string, string> stateData)\n        {\n            var enqueueAt = JobHelper.DeserializeDateTime(stateData[\"EnqueueAt\"]);\n            stateData.TryGetValue(\"Queue\", out var queue);\n\n            var sb = new StringBuilder();\n            sb.Append(\"<dl class=\\\"dl-horizontal\\\">\");\n            sb.Append($\"<dt>Enqueue at:</dt><dd data-moment=\\\"{helper.HtmlEncode(JobHelper.ToTimestamp(enqueueAt).ToString(CultureInfo.InvariantCulture))}\\\">{helper.HtmlEncode(enqueueAt.ToString(CultureInfo.CurrentCulture))}</dd>\");\n\n            if (!String.IsNullOrWhiteSpace(queue))\n            {\n                sb.Append($\"<dt>Queue:</dt><dd>{helper.QueueLabel(queue)}</dd>\");\n            }\n\n            sb.Append(\"</dl>\");\n\n            return new NonEscapedString(sb.ToString());\n        }\n\n        private static NonEscapedString AwaitingRenderer(HtmlHelper helper, IDictionary<string, string> stateData)\n        {\n            var builder = new StringBuilder();\n\n            builder.Append(\"<dl class=\\\"dl-horizontal\\\">\");\n\n            if (stateData.TryGetValue(\"ParentId\", out var parentId))\n            {\n                builder.Append($\"<dt>Parent</dt><dd>{helper.JobIdLink(parentId)}</dd>\");\n            }\n\n            if (stateData.TryGetValue(\"NextState\", out var nextStateString))\n            {\n                var nextState = SerializationHelper.Deserialize<IState>(nextStateString, SerializationOption.TypedInternal);\n\n                builder.Append($\"<dt>Next State</dt><dd>{helper.StateLabel(nextState?.Name ?? \"(no state)\")}</dd>\");\n            }\n\n            if (stateData.TryGetValue(\"Options\", out var optionsDescription))\n            {\n                if (Enum.TryParse(optionsDescription, out JobContinuationOptions options))\n                {\n                    optionsDescription = options.ToString(\"G\");\n                }\n\n                builder.Append($\"<dt>Options</dt><dd><code>{helper.HtmlEncode(optionsDescription)}</code></dd>\");\n            }\n\n            builder.Append(\"</dl>\");\n\n            return new NonEscapedString(builder.ToString());\n        }\n        \n        private static NonEscapedString DeletedRenderer(HtmlHelper html, IDictionary<string, string> stateData)\n        {\n            if (stateData.TryGetValue(\"Exception\", out var exception))\n            {\n                var exceptionInfo = SerializationHelper.Deserialize<ExceptionInfo>(exception);\n                if (exceptionInfo != null)\n                {\n                    var commaIndex = exceptionInfo.Type.IndexOf(\",\", StringComparison.OrdinalIgnoreCase);\n                    var typeName = commaIndex > 0 \n                        ? exceptionInfo.Type.Substring(0, commaIndex)\n                        : exceptionInfo.Type;\n\n                    return new NonEscapedString(\n                        $\"<h4 class=\\\"exception-type\\\">{html.HtmlEncode(typeName)}</h4><p class=\\\"text-muted\\\">{html.HtmlEncode(exceptionInfo.Message)}</p>\");\n                }\n            }\n\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/JobMethodCallRenderer.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Net;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing System.Threading;\nusing Hangfire.Common;\nusing Hangfire.Dashboard.Resources;\nusing Hangfire.Processing;\n\nnamespace Hangfire.Dashboard\n{\n    internal static class JobMethodCallRenderer\n    {\n        // Should not be converted to \"readonly\" to support https://github.com/HangfireIO/Hangfire/issues/1295\n        internal static int MaxArgumentToRenderSize = 4096;\n\n        public static NonEscapedString Render(Job job)\n        {\n            if (job == null) { return new NonEscapedString($\"<em>{Encode(Strings.Common_CannotFindTargetMethod)}</em>\"); }\n\n            var builder = new StringBuilder();\n\n            builder.Append(WrapKeyword(\"using\"));\n            builder.Append(' ');\n            builder.Append(Encode(job.Type.Namespace));\n            builder.Append(';');\n            builder.AppendLine();\n            builder.AppendLine();\n\n            string serviceName = null;\n\n            if (!job.Method.IsStatic)\n            {\n                serviceName = GetNameWithoutGenericArity(job.Type);\n\n                if (job.Type.GetTypeInfo().IsInterface && serviceName[0] == 'I' && Char.IsUpper(serviceName[1]))\n                {\n                    serviceName = serviceName.Substring(1);\n                }\n\n                serviceName = Char.ToLower(serviceName[0]\n#if !NETSTANDARD1_3\n                    , CultureInfo.InvariantCulture\n#endif\n                    ) + serviceName.Substring(1);\n\n                builder.Append(WrapKeyword(\"var\"));\n                builder.Append(\n                    $\" {Encode(serviceName)} = Activate&lt;{WrapType(Encode(job.Type.ToGenericTypeString()))}&gt;();\");\n\n                builder.AppendLine();\n            }\n\n            if (job.Method.GetCustomAttribute<AsyncStateMachineAttribute>() != null ||\n                job.Method.ReturnType.IsTaskLike(out _))\n            {\n                builder.Append($\"{WrapKeyword(\"await\")} \");\n            }\n\n            builder.Append(!job.Method.IsStatic ? Encode(serviceName) : WrapType(Encode(job.Type.ToGenericTypeString())));\n\n            builder.Append('.');\n            builder.Append(Encode(job.Method.Name));\n\n            if (job.Method.IsGenericMethod)\n            {\n                var genericArgumentTypes = job.Method.GetGenericArguments()\n                    .Select(static x => WrapType(x.Name))\n                    .ToArray();\n\n                builder.Append($\"&lt;{String.Join(\", \", genericArgumentTypes)}&gt;\");\n            }\n\n            builder.Append('(');\n\n            var parameters = job.Method.GetParameters();\n            var renderedArguments = new List<string>(parameters.Length);\n            var renderedArgumentsTotalLength = 0;\n\n            const int splitStringMinLength = 100;\n\n            for (var i = 0; i < parameters.Length; i++)\n            {\n                var parameter = parameters[i];\n#pragma warning disable 618\n                var arguments = job.Arguments;\n#pragma warning restore 618\n\n                if (i < arguments.Length)\n                {\n                    var argument = arguments[i];\n\n                    if (argument != null && argument.Length > MaxArgumentToRenderSize)\n                    {\n                        renderedArguments.Add(Encode(\"<VALUE IS TOO BIG>\"));\n                        continue;\n                    }\n\n                    string renderedArgument;\n\n                    var enumerableArgument = GetIEnumerableGenericArgument(parameter.ParameterType);\n\n                    object argumentValue;\n                    bool isJson = true;\n\n                    try\n                    {\n                        argumentValue = SerializationHelper.Deserialize(argument, parameter.ParameterType, SerializationOption.User);\n                    }\n                    catch (Exception ex) when (ex.IsCatchableExceptionType())\n                    {\n                        // If argument value is not encoded as JSON (an old\n                        // way using TypeConverter), we should display it as is.\n                        argumentValue = argument;\n                        isJson = false;\n                    }\n\n                    if (enumerableArgument == null || argumentValue == null)\n                    {\n                        var argumentRenderer = ArgumentRenderer.GetRenderer(parameter.ParameterType);\n                        renderedArgument = argumentRenderer.Render(isJson, argumentValue?.ToString(), argument);\n                    }\n                    else\n                    {\n                        var renderedItems = new List<string>();\n\n                        // ReSharper disable once LoopCanBeConvertedToQuery\n                        foreach (var item in argumentValue as IEnumerable)\n                        {\n                            var argumentRenderer = ArgumentRenderer.GetRenderer(enumerableArgument);\n                            renderedItems.Add(argumentRenderer.Render(isJson, item?.ToString(),\n                                SerializationHelper.Serialize(item, SerializationOption.User)));\n                        }\n\n                        // ReSharper disable once UseStringInterpolation\n                        renderedArgument = String.Format(\n                            CultureInfo.CurrentCulture,\n                            \"{0}{1} {{ {2} }}\",\n                            WrapKeyword(\"new\"),\n                            parameter.ParameterType.IsArray ? \" []\" : \"\",\n                            String.Join(\", \", renderedItems));\n                    }\n\n                    renderedArguments.Add(renderedArgument);\n                    renderedArgumentsTotalLength += renderedArgument.Length;\n                }\n                else\n                {\n                    renderedArguments.Add(Encode(\"<NO VALUE>\"));\n                }\n            }\n\n            for (int i = 0; i < renderedArguments.Count; i++)\n            {\n                // TODO: be aware of out of range\n                var parameter = parameters[i];\n                var tooltipPosition = \"top\";\n\n                var renderedArgument = renderedArguments[i];\n                if (renderedArgumentsTotalLength > splitStringMinLength)\n                {\n                    builder.AppendLine();\n                    builder.Append(\"    \");\n\n                    tooltipPosition = \"left\";\n                }\n                else if (i > 0)\n                {\n                    builder.Append(' ');\n                }\n\n                builder.Append($\"<span title=\\\"{parameter.Name}\\\" data-placement=\\\"{tooltipPosition}\\\">\");\n                builder.Append(renderedArgument);\n                builder.Append(\"</span>\");\n\n                if (i < renderedArguments.Count - 1)\n                {\n                    builder.Append(',');\n                }\n            }\n\n            builder.Append(\");\");\n\n            return new NonEscapedString(builder.ToString());\n        }\n\n        private static string WrapIdentifier(string value)\n        {\n            return value;\n        }\n\n        private static string WrapKeyword(string value)\n        {\n            return Span(\"keyword\", value);\n        }\n\n        private static string WrapType(string value)\n        {\n            return Span(\"type\", value);\n        }\n\n        private static string WrapString(string value)\n        {\n            return Span(\"string\", value);\n        }\n\n        private static string Span(string @class, string value)\n        {\n            return $\"<span class=\\\"{@class}\\\">{value}</span>\";\n        }\n\n        private static string Encode(string value)\n        {\n            return WebUtility.HtmlEncode(value);\n        }\n\n        private static Type GetIEnumerableGenericArgument(Type type)\n        {\n            if (type == typeof(string)) return null;\n\n            return type.GetTypeInfo().ImplementedInterfaces\n                .Where(static x => x.GetTypeInfo().IsGenericType\n                            && x.GetTypeInfo().GetGenericTypeDefinition() == typeof(IEnumerable<>))\n                .Select(static x => x.GetTypeInfo().GetAllGenericArguments()[0])\n                .FirstOrDefault();\n        }\n\n        public static string GetNameWithoutGenericArity(Type t)\n        {\n            string name = t.Name;\n            int index = name.IndexOf('`');\n            return index == -1 ? name : name.Substring(0, index);\n        }\n\n        private sealed class ArgumentRenderer\n        {\n            private string _enclosingString;\n            private Type _deserializationType;\n            private Func<string, string> _valueRenderer;\n\n            private ArgumentRenderer()\n            {\n                _enclosingString = \"\\\"\";\n                _valueRenderer = static value => value == null ? WrapKeyword(\"null\") : WrapString(value);\n            }\n\n            public string Render(bool isJson, string deserializedValue, string rawValue)\n            {\n                var builder = new StringBuilder();\n\n                if (rawValue == null)\n                {\n                    return WrapKeyword(\"null\");\n                }\n\n                if (_deserializationType != null)\n                {\n                    builder.Append(WrapIdentifier(\n                        isJson ? \"FromJson\" : \"Deserialize\"));\n\n                    builder.Append(\"&lt;\")\n                        .Append(WrapType(Encode(_deserializationType.ToGenericTypeString())))\n                        .Append(WrapIdentifier(\"&gt;\"))\n                        .Append('(');\n\n                    builder.Append(WrapString(Encode(\"\\\"\" + rawValue.Replace(\"\\\"\", \"\\\\\\\"\") + \"\\\"\")));\n                }\n                else\n                {\n                    if (deserializedValue != null)\n                    {\n                        builder.Append(_enclosingString);\n                    }\n\n                    builder.Append(_valueRenderer(Encode(deserializedValue)));\n\n                    if (deserializedValue != null)\n                    {\n                        builder.Append(_enclosingString);\n                    }\n                }\n\n                if (_deserializationType != null)\n                {\n                    builder.Append(')');\n                }\n\n                return builder.ToString();\n            }\n\n            public static ArgumentRenderer GetRenderer(Type type)\n            {\n                if (type.GetTypeInfo().IsEnum)\n                {\n                    return new ArgumentRenderer\n                    {\n                        _enclosingString = String.Empty,\n                        _valueRenderer = value => $\"{WrapType(type.Name)}.{value}\"\n                    };\n                }\n\n                if (IsNumericType(type))\n                {\n                    return new ArgumentRenderer\n                    {\n                        _enclosingString = String.Empty,\n                        _valueRenderer = WrapIdentifier\n                    };\n                }\n\n                if (type == typeof(bool))\n                {\n                    return new ArgumentRenderer\n                    {\n                        _valueRenderer = static value => WrapKeyword(value.ToLowerInvariant()),\n                        _enclosingString = String.Empty,\n                    };\n                }\n\n                if (type == typeof(char))\n                {\n                    return new ArgumentRenderer\n                    {\n                        _enclosingString = \"'\",\n                    };\n                }\n\n                if (type == typeof(string) || type == typeof(object))\n                {\n                    return new ArgumentRenderer\n                    {\n                        _enclosingString = \"\\\"\"\n                    };\n                }\n\n                if (type == typeof(TimeSpan) || type == typeof(DateTime))\n                {\n                    return new ArgumentRenderer\n                    {\n                        _enclosingString = String.Empty,\n                        _valueRenderer = value => $\"{WrapType(type.Name)}.Parse({WrapString($\"\\\"{value}\\\"\")})\"\n                    };\n                }\n\n                if (type == typeof(CancellationToken))\n                {\n                    return new ArgumentRenderer\n                    {\n                        _enclosingString = String.Empty,\n                        _valueRenderer = static _ => $\"{WrapType(nameof(CancellationToken))}.None\"\n                    };\n                }\n\n                return new ArgumentRenderer\n                {\n                    _deserializationType = type,\n                };\n            }\n\n            private static bool IsNumericType(Type type)\n            {\n                if (type == null) return false;\n\n                switch (type.GetTypeCode())\n                {\n                    case TypeCode.Byte:\n                    case TypeCode.Decimal:\n                    case TypeCode.Double:\n                    case TypeCode.Int16:\n                    case TypeCode.Int32:\n                    case TypeCode.Int64:\n                    case TypeCode.SByte:\n                    case TypeCode.Single:\n                    case TypeCode.UInt16:\n                    case TypeCode.UInt32:\n                    case TypeCode.UInt64:\n                        return true;\n\n                    case TypeCode.Object:\n                        if (IsNullableType(type))\n                        {\n                            return IsNumericType(Nullable.GetUnderlyingType(type));\n                        }\n                        return false;\n                }\n                return false;\n            }\n\n            private static bool IsNullableType(Type type)\n            {\n                return type.GetTypeInfo().IsGenericType && type.GetTypeInfo().GetGenericTypeDefinition() == typeof(Nullable<>);\n            }\n        }\n    }\n\n    internal enum TypeCode\n    {\n        Empty = 0,          // Null reference\n        Object = 1,         // Instance that isn't a value\n        DBNull = 2,         // Database null value\n        Boolean = 3,        // Boolean\n        Char = 4,           // Unicode character\n        SByte = 5,          // Signed 8-bit integer\n        Byte = 6,           // Unsigned 8-bit integer\n        Int16 = 7,          // Signed 16-bit integer\n        UInt16 = 8,         // Unsigned 16-bit integer\n        Int32 = 9,          // Signed 32-bit integer\n        UInt32 = 10,        // Unsigned 32-bit integer\n        Int64 = 11,         // Signed 64-bit integer\n        UInt64 = 12,        // Unsigned 64-bit integer\n        Single = 13,        // IEEE 32-bit float\n        Double = 14,        // IEEE 64-bit double\n        Decimal = 15,       // Decimal\n        DateTime = 16,      // DateTime\n        String = 18,        // Unicode character string\n    }\n    \n    internal static class TypeExtensionMethods\n    {\n        public static TypeCode GetTypeCode(this Type type)\n        {\n            if (type == null)\n            {\n                return TypeCode.Empty;\n            }\n            else if (type == typeof(Boolean))\n            {\n                return TypeCode.Boolean;\n            }\n            else if (type == typeof(Char))\n            {\n                return TypeCode.Char;\n            }\n            else if (type == typeof(SByte))\n            {\n                return TypeCode.SByte;\n            }\n            else if (type == typeof(Byte))\n            {\n                return TypeCode.Byte;\n            }\n            else if (type == typeof(Int16))\n            {\n                return TypeCode.Int16;\n            }\n            else if (type == typeof(UInt16))\n            {\n                return TypeCode.UInt16;\n            }\n            else if (type == typeof(Int32))\n            {\n                return TypeCode.Int32;\n            }\n            else if (type == typeof(UInt32))\n            {\n                return TypeCode.UInt32;\n            }\n            else if (type == typeof(Int64))\n            {\n                return TypeCode.Int64;\n            }\n            else if (type == typeof(UInt64))\n            {\n                return TypeCode.UInt64;\n            }\n            else if (type == typeof(Single))\n            {\n                return TypeCode.Single;\n            }\n            else if (type == typeof(Double))\n            {\n                return TypeCode.Double;\n            }\n            else if (type == typeof(Decimal))\n            {\n                return TypeCode.Decimal;\n            }\n            else if (type == typeof(DateTime))\n            {\n                return TypeCode.DateTime;\n            }\n            else if (type == typeof(String))\n            {\n                return TypeCode.String;\n            }\n            else\n            {\n                return TypeCode.Object;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/JobsSidebarMenu.cs",
    "content": "// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Dashboard.Resources;\n\nnamespace Hangfire.Dashboard\n{\n    public static class JobsSidebarMenu\n    {\n        public static readonly List<Func<RazorPage, MenuItem>> Items\n            = new List<Func<RazorPage, MenuItem>>();\n\n        static JobsSidebarMenu()\n        {\n            Items.Add(static page => new MenuItem(Strings.JobsSidebarMenu_Enqueued, page.Url.LinkToQueues())\n            {\n                Active = page.RequestPath.StartsWith(\"/jobs/enqueued\", StringComparison.OrdinalIgnoreCase),\n                Metric = DashboardMetrics.EnqueuedAndQueueCount\n            });\n\n            Items.Add(static page => new MenuItem(Strings.JobsSidebarMenu_Scheduled, page.Url.To(\"/jobs/scheduled\"))\n            {\n                Active = page.RequestPath.StartsWith(\"/jobs/scheduled\", StringComparison.OrdinalIgnoreCase),\n                Metric = DashboardMetrics.ScheduledCount\n            });\n\n            Items.Add(static page => new MenuItem(Strings.JobsSidebarMenu_Processing, page.Url.To(\"/jobs/processing\"))\n            {\n                Active = page.RequestPath.StartsWith(\"/jobs/processing\", StringComparison.OrdinalIgnoreCase),\n                Metric = DashboardMetrics.ProcessingCount\n            });\n\n            Items.Add(static page => new MenuItem(Strings.JobsSidebarMenu_Succeeded, page.Url.To(\"/jobs/succeeded\"))\n            {\n                Active = page.RequestPath.StartsWith(\"/jobs/succeeded\", StringComparison.OrdinalIgnoreCase),\n                Metric = DashboardMetrics.SucceededCount\n            });\n\n            Items.Add(static page => new MenuItem(Strings.JobsSidebarMenu_Failed, page.Url.To(\"/jobs/failed\"))\n            {\n                Active = page.RequestPath.StartsWith(\"/jobs/failed\", StringComparison.OrdinalIgnoreCase),\n                Metric = DashboardMetrics.FailedCount\n            });\n\n            Items.Add(static page => new MenuItem(Strings.JobsSidebarMenu_Deleted, page.Url.To(\"/jobs/deleted\"))\n            {\n                Active = page.RequestPath.StartsWith(\"/jobs/deleted\", StringComparison.OrdinalIgnoreCase),\n                Metric = DashboardMetrics.DeletedCount\n            });\n\n            Items.Add(static page => new MenuItem(Strings.JobsSidebarMenu_Awaiting, page.Url.To(\"/jobs/awaiting\"))\n            {\n                Active = page.RequestPath.StartsWith(\"/jobs/awaiting\", StringComparison.OrdinalIgnoreCase),\n                Metric = DashboardMetrics.AwaitingCount\n            });\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/JsonStats.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Converters;\nusing Newtonsoft.Json.Serialization;\n\nnamespace Hangfire.Dashboard\n{\n    internal sealed class JsonStats : IDashboardDispatcher\n    {\n        public async Task Dispatch(DashboardContext context)\n        {\n            var requestedMetrics = await context.Request.GetFormValuesAsync(\"metrics[]\").ConfigureAwait(false);\n            var page = new StubPage();\n            page.Assign(context);\n\n            var metrics = DashboardMetrics.GetMetrics().Where(x => requestedMetrics.Contains(x.Name));\n            var result = new Dictionary<string, Metric>();\n\n            foreach (var metric in metrics)\n            {\n                var value = metric.Func(page);\n                result.Add(metric.Name, value);\n            }\n\n            var settings = new JsonSerializerSettings\n            {\n                ContractResolver = new CamelCasePropertyNamesContractResolver(),\n                Converters = new JsonConverter[]{ new StringEnumConverter { CamelCaseText = true } }\n            };\n            var serialized = JsonConvert.SerializeObject(result, settings);\n\n            context.Response.ContentType = \"application/json\";\n            await context.Response.WriteAsync(serialized).ConfigureAwait(false);\n        }\n\n        private sealed class StubPage : RazorPage\n        {\n            public override void Execute()\n            {\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Net;\n\nnamespace Hangfire.Dashboard\n{\n    public class LocalRequestsOnlyAuthorizationFilter : IDashboardAuthorizationFilter\n#if FEATURE_OWIN\n#pragma warning disable 618\n        , IAuthorizationFilter\n#pragma warning restore 618\n#endif\n    {\n        public bool Authorize(DashboardContext context)\n        {\n            // if unknown, assume not local\n            if (String.IsNullOrEmpty(context.Request.RemoteIpAddress))\n                return false;\n\n            // check if localhost\n            if (context.Request.RemoteIpAddress == \"127.0.0.1\" || context.Request.RemoteIpAddress == \"::1\")\n                return true;\n\n            // compare with local address\n            if (context.Request.RemoteIpAddress == context.Request.LocalIpAddress)\n                return true;\n\n            // Handle addresses such as ::ffff:127.0.0.1 (IP v4 mapped to IP v6)\n            return IPAddress.TryParse(context.Request.RemoteIpAddress, out IPAddress address) && IPAddress.IsLoopback(address);\n        }\n\n#if FEATURE_OWIN\n        public bool Authorize(IDictionary<string, object> owinEnvironment)\n        {\n            var context = new Microsoft.Owin.OwinContext(owinEnvironment);\n\n            // if unknown, assume not local\n            if (String.IsNullOrEmpty(context.Request.RemoteIpAddress))\n                return false;\n\n            // check if localhost\n            if (context.Request.RemoteIpAddress == \"127.0.0.1\" || context.Request.RemoteIpAddress == \"::1\")\n                return true;\n\n            // compare with local address\n            if (context.Request.RemoteIpAddress == context.Request.LocalIpAddress)\n                return true;\n\n            // Handle addresses such as ::ffff:127.0.0.1 (IP v4 mapped to IP v6)\n            return IPAddress.TryParse(context.Request.RemoteIpAddress, out IPAddress address) && IPAddress.IsLoopback(address);\n        }\n#endif\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/MenuItem.cs",
    "content": "// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Hangfire.Dashboard\n{\n    public class MenuItem\n    {\n        public MenuItem(string text, string url)\n        {\n            Text = text;\n            Url = url;\n        }\n\n        public string Text { get; }\n        public string Url { get; }\n\n        public bool Active { get; set; }\n        public DashboardMetric Metric { get; set; }\n        public DashboardMetric[] Metrics { get; set; }\n\n        public IEnumerable<DashboardMetric> GetAllMetrics()\n        {\n            var metrics = new List<DashboardMetric> { Metric };\n            \n            if (Metrics != null)\n            {\n                metrics.AddRange(Metrics);\n            }\n\n            return metrics.Where(static x => x != null).ToList();\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Metric.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Globalization;\n\nnamespace Hangfire.Dashboard\n{\n    public class Metric\n    {\n        public Metric(string value)\n        {\n            Value = value;\n        }\n\n        public Metric(long value)\n        {\n            Value = value.ToString(\"N0\", CultureInfo.CurrentCulture);\n            IntValue = value;\n        }\n\n        public string Value { get; }\n        public long IntValue { get; set; }\n        public MetricStyle Style { get; set; }\n        public bool Highlighted { get; set; }\n        public string Title { get; set; }\n    }\n\n    public enum MetricStyle\n    {\n        Default,\n        Info,\n        Success,\n        Warning,\n        Danger,\n    }\n\n    internal static class MetricStyleExtensions\n    {\n        public static string ToClassName(this MetricStyle style)\n        {\n            switch (style)\n            {\n                case MetricStyle.Default: return \"metric-default\";\n                case MetricStyle.Info:    return \"metric-info\";\n                case MetricStyle.Success: return \"metric-success\";\n                case MetricStyle.Warning: return \"metric-warning\";\n                case MetricStyle.Danger:  return \"metric-danger\";\n                default:                  return \"metric-null\";\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/NavigationMenu.cs",
    "content": "// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Dashboard.Resources;\n\nnamespace Hangfire.Dashboard\n{\n    public static class NavigationMenu\n    {\n        public static readonly List<Func<RazorPage, MenuItem>> Items = new List<Func<RazorPage, MenuItem>>();\n\n        static NavigationMenu()\n        {\n            Items.Add(static page => new MenuItem(Strings.NavigationMenu_Jobs, page.Url.LinkToQueues())\n            {\n                Active = page.RequestPath.StartsWith(\"/jobs\", StringComparison.OrdinalIgnoreCase),\n                Metrics = new []\n                {\n                    DashboardMetrics.EnqueuedCountOrNull,\n                    DashboardMetrics.FailedCountOrNull\n                }\n            });\n\n            Items.Add(static page => new MenuItem(Strings.NavigationMenu_Retries, page.Url.To(\"/retries\"))\n            {\n                Active = page.RequestPath.StartsWith(\"/retries\", StringComparison.OrdinalIgnoreCase),\n                Metric = DashboardMetrics.RetriesCount\n            });\n\n            Items.Add(static page => new MenuItem(Strings.NavigationMenu_RecurringJobs, page.Url.To(\"/recurring\"))\n            {\n                Active = page.RequestPath.StartsWith(\"/recurring\", StringComparison.OrdinalIgnoreCase),\n                Metric = DashboardMetrics.RecurringJobCount\n            });\n\n            Items.Add(static page => new MenuItem(Strings.NavigationMenu_Servers, page.Url.To(\"/servers\"))\n            {\n                Active = page.RequestPath.Equals(\"/servers\", StringComparison.OrdinalIgnoreCase),\n                Metric = DashboardMetrics.ServerCount\n            });\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/NonEscapedString.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Dashboard\n{\n    public class NonEscapedString\n    {\n        private readonly string _value;\n\n        public NonEscapedString(string value)\n        {\n            _value = value;\n        }\n\n        public override string ToString()\n        {\n            return _value;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Owin/IOwinDashboardAntiforgery.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2018 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\n\nnamespace Hangfire.Dashboard.Owin\n{\n    public interface IOwinDashboardAntiforgery\n    {\n        string HeaderName { get; }\n        string GetToken(IDictionary<string, object> environment);\n        bool ValidateRequest(IDictionary<string, object> environment);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Owin/MiddlewareExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Net;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Dashboard.Owin;\nusing Microsoft.Owin;\n\nnamespace Hangfire.Dashboard\n{\n    using MidFunc = Func<\n        Func<IDictionary<string, object>, Task>,\n        Func<IDictionary<string, object>, Task>\n        >;\n    using BuildFunc = Action<\n        Func<\n            IDictionary<string, object>,\n            Func<\n                Func<IDictionary<string, object>, Task>,\n                Func<IDictionary<string, object>, Task>\n        >>>;\n\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public static class MiddlewareExtensions\n    {\n        public static BuildFunc UseHangfireDashboard(\n            [NotNull] this BuildFunc builder,\n            [NotNull] DashboardOptions options, \n            [NotNull] JobStorage storage, \n            [NotNull] RouteCollection routes,\n            [CanBeNull] IOwinDashboardAntiforgery antiforgery)\n        {\n            if (builder == null) throw new ArgumentNullException(nameof(builder));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (routes == null) throw new ArgumentNullException(nameof(routes));\n\n            builder(_ => UseHangfireDashboard(options, storage, routes, antiforgery));\n\n            return builder;\n        }\n\n        public static MidFunc UseHangfireDashboard(\n            [NotNull] DashboardOptions options, \n            [NotNull] JobStorage storage, \n            [NotNull] RouteCollection routes,\n            [CanBeNull] IOwinDashboardAntiforgery antiforgery)\n        {\n            if (options == null) throw new ArgumentNullException(nameof(options));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (routes == null) throw new ArgumentNullException(nameof(routes));\n\n            return\n                next =>\n                async env =>\n                {\n                    var owinContext = new OwinContext(env);\n                    var context = new OwinDashboardContext(storage, options, env);\n\n                    if (!options.IgnoreAntiforgeryToken && antiforgery != null)\n                    {\n                        context.AntiforgeryHeader = antiforgery.HeaderName;\n                        context.AntiforgeryToken = antiforgery.GetToken(env);\n                    }\n\n#pragma warning disable 618\n                    if (options.AuthorizationFilters != null)\n                    {\n                        if (options.AuthorizationFilters.Any(filter => !filter.Authorize(owinContext.Environment)))\n#pragma warning restore 618\n                        {\n                            owinContext.Response.StatusCode = GetUnauthorizedStatusCode(owinContext);\n                            return;\n                        }\n                    }\n                    else\n                    {\n                        // ReSharper disable once LoopCanBeConvertedToQuery\n                        foreach (var filter in options.Authorization)\n                        {\n                            if (!filter.Authorize(context))\n                            {\n                                owinContext.Response.StatusCode = GetUnauthorizedStatusCode(owinContext);\n                                return;\n                            }\n                        }\n\n                        foreach (var filter in options.AsyncAuthorization)\n                        {\n                            if (!await filter.AuthorizeAsync(context))\n                            {\n                                owinContext.Response.StatusCode = GetUnauthorizedStatusCode(owinContext);\n                                return;\n                            }\n                        }\n                    }\n\n                    if (!options.IgnoreAntiforgeryToken && antiforgery != null && !antiforgery.ValidateRequest(env))\n                    {\n                        owinContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;\n                        return;\n                    }\n\n                    var findResult = routes.FindDispatcher(owinContext.Request.Path.Value);\n\n                    if (findResult == null)\n                    {\n                        await next(env);\n                        return;\n                    }\n\n                    context.UriMatch = findResult.Item2;\n\n                    await findResult.Item1.Dispatch(context);\n                };\n        }\n\n        private static int GetUnauthorizedStatusCode(IOwinContext owinContext)\n        {\n            return owinContext.Authentication?.User?.Identity?.IsAuthenticated == true\n                ? (int)HttpStatusCode.Forbidden\n                : (int)HttpStatusCode.Unauthorized;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Owin/OwinDashboardContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard\n{\n    public sealed class OwinDashboardContext : DashboardContext\n    {\n        public OwinDashboardContext(\n            [NotNull] JobStorage storage,\n            [NotNull] DashboardOptions options,\n            [NotNull] IDictionary<string, object> environment) \n            : base(storage, options)\n        {\n            if (environment == null) throw new ArgumentNullException(nameof(environment));\n\n            Environment = environment;\n            Request = new OwinDashboardRequest(environment);\n            Response = new OwinDashboardResponse(environment);\n        }\n\n        public IDictionary<string, object> Environment { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Owin/OwinDashboardContextExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard\n{\n    public static class OwinDashboardContextExtensions\n    {\n        public static IDictionary<string, object> GetOwinEnvironment([NotNull] this DashboardContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            var owinContext = context as OwinDashboardContext;\n            if (owinContext == null)\n            {\n                throw new ArgumentException($\"Context argument should be of type `{nameof(OwinDashboardContext)}`!\", nameof(context));\n            }\n\n            return owinContext.Environment;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Owin/OwinDashboardRequest.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Microsoft.Owin;\n\nnamespace Hangfire.Dashboard\n{\n    internal sealed class OwinDashboardRequest : DashboardRequest\n    {\n        private const string FormCollectionKey = \"Microsoft.Owin.Form#collection\";\n        private readonly IOwinContext _context;\n\n        public OwinDashboardRequest([NotNull] IDictionary<string, object> environment)\n        {\n            if (environment == null) throw new ArgumentNullException(nameof(environment));\n            _context = new OwinContext(environment);\n        }\n\n        public override string Method => _context.Request.Method;\n        public override string Path => _context.Request.Path.Value;\n        public override string PathBase => _context.Request.PathBase.Value;\n        public override string LocalIpAddress => _context.Request.LocalIpAddress;\n        public override string RemoteIpAddress => _context.Request.RemoteIpAddress;\n\n        public override string GetQuery(string key) => _context.Request.Query[key];\n\n        public override async Task<IList<string>> GetFormValuesAsync(string key)\n        {\n            IList<string> values;\n\n            if(_context.Environment.TryGetValue(FormCollectionKey, out var formCollection))\n            {\n                if (formCollection is IFormCollection)\n                {\n                    var form = (IFormCollection)_context.Request.Environment[FormCollectionKey];\n                    values = form.GetValues(key);\n                }\n                else\n                {\n                    dynamic form = _context.Request.Environment[FormCollectionKey];\n                    values = form.GetValues(key);\n                }\n            }\n            else\n            {\n                var form = await _context.Request.ReadFormAsync().ConfigureAwait(false);\n                values = form.GetValues(key);\n            }\n            \n            return values ?? new List<string>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Owin/OwinDashboardResponse.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Microsoft.Owin;\n\nnamespace Hangfire.Dashboard\n{\n    internal sealed class OwinDashboardResponse : DashboardResponse\n    {\n        private readonly IOwinContext _context;\n\n        public OwinDashboardResponse([NotNull] IDictionary<string, object> environment)\n        {\n            if (environment == null) throw new ArgumentNullException(nameof(environment));\n            _context = new OwinContext(environment);\n        }\n\n        public override string ContentType\n        {\n            get { return _context.Response.ContentType; }\n            set { _context.Response.ContentType = value; }\n        }\n\n        public override int StatusCode\n        {\n            get { return _context.Response.StatusCode; }\n            set { _context.Response.StatusCode = value; }\n        }\n\n        public override Stream Body => _context.Response.Body;\n\n        public override void SetExpire(DateTimeOffset? value)\n        {\n            _context.Response.Expires = value;\n        }\n\n        public override Task WriteAsync(string text)\n        {\n            return _context.Response.WriteAsync(text);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pager.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Hangfire.Dashboard\n{\n    public class Pager\n    {\n        private const int PageItemsCount = 7;\n        private const int DefaultRecordsPerPage = 10;\n\n        private int _startPageIndex = 1;\n        private int _endPageIndex = 1;\n\n        public Pager(int from, int perPage, long total)\n            : this(from, perPage, DefaultRecordsPerPage, total)\n        {\n        }\n\n        public Pager(int from, int perPage, int defaultPerPage, long total)\n        {\n            FromRecord = from >= 0 ? from : 0;\n            RecordsPerPage = perPage > 0 ? perPage : defaultPerPage;\n            TotalRecordCount = total;\n            CurrentPage = FromRecord / RecordsPerPage + 1;\n            TotalPageCount = (int)Math.Ceiling((double)TotalRecordCount / RecordsPerPage);\n\n            PagerItems = GenerateItems();\n        }\n\n        public string BasePageUrl { get; set; }\n\n        public int FromRecord { get; }\n        public int RecordsPerPage { get; }\n        public int CurrentPage { get; }\n\n        public int TotalPageCount { get; }\n        public long TotalRecordCount { get; }\n\n        internal ICollection<Item> PagerItems { get; }\n\n        public virtual string PageUrl(int page)\n        {\n            if (page < 1 || page > TotalPageCount) return \"#\";\n\n            return BasePageUrl + \"?from=\" + (page - 1) * RecordsPerPage + \"&count=\" + RecordsPerPage;\n        }\n\n        public string RecordsPerPageUrl(int perPage)\n        {\n            if (perPage <= 0) return \"#\";\n            return BasePageUrl + \"?from=0&count=\" + perPage;\n        }\n\n        private ICollection<Item> GenerateItems()\n        {\n            // start page index\n            _startPageIndex = CurrentPage - PageItemsCount / 2;\n            if (_startPageIndex + PageItemsCount > TotalPageCount)\n                _startPageIndex = TotalPageCount + 1 - PageItemsCount;\n            if (_startPageIndex < 1)\n                _startPageIndex = 1;\n\n            // end page index\n            _endPageIndex = _startPageIndex + PageItemsCount - 1;\n            if (_endPageIndex > TotalPageCount)\n                _endPageIndex = TotalPageCount;\n\n            var pagerItems = new List<Item>();\n            if (TotalPageCount == 0) return pagerItems;\n\n            AddPrevious(pagerItems);\n\n            // first page\n            if (_startPageIndex > 1) \n                pagerItems.Add(new Item(1, false, ItemType.Page));\n\n            // more page before numeric page buttons\n            AddMoreBefore(pagerItems);\n\n            // numeric page\n            AddPageNumbers(pagerItems);\n\n            // more page after numeric page buttons\n            AddMoreAfter(pagerItems);\n\n            // last page\n            if (_endPageIndex < TotalPageCount)\n                pagerItems.Add(new Item(TotalPageCount, false, ItemType.Page));\n\n            // Next page\n            AddNext(pagerItems);\n\n            return pagerItems;\n        }\n\n        private void AddPrevious(ICollection<Item> results)\n        {\n            var item = new Item(CurrentPage - 1, CurrentPage == 1, ItemType.PrevPage);\n            results.Add(item);\n        }\n\n        private void AddMoreBefore(ICollection<Item> results)\n        {\n            if (_startPageIndex > 2)\n            {\n                var index = _startPageIndex - 1;\n                var item = new Item(index, false, ItemType.MorePage);\n                results.Add(item);\n            }\n        }\n\n        private void AddMoreAfter(ICollection<Item> results)\n        {\n            if (_endPageIndex < TotalPageCount - 1)\n            {\n                var index = _startPageIndex + PageItemsCount;\n                if (index > TotalPageCount) { index = TotalPageCount; }\n                var item = new Item(index, false, ItemType.MorePage);\n                results.Add(item);\n            }\n        }\n\n        private void AddPageNumbers(ICollection<Item> results)\n        {\n            for (var pageIndex = _startPageIndex; pageIndex <= _endPageIndex; pageIndex++)\n            {\n                var item = new Item(pageIndex, false, ItemType.Page);\n                results.Add(item);\n            }\n        }\n\n        private void AddNext(ICollection<Item> results)\n        {\n            var item = new Item(CurrentPage + 1, CurrentPage >= TotalPageCount, ItemType.NextPage);\n            results.Add(item);\n        }\n\n        internal sealed class Item\n        {\n            public Item(int pageIndex, bool disabled, ItemType type)\n            {\n                PageIndex = pageIndex;\n                Disabled = disabled;\n                Type = type;\n            }\n\n            public int PageIndex { get; }\n            public bool Disabled { get; }\n            public ItemType Type { get; }\n        }\n\n        internal enum ItemType\n        {\n            Page,\n            PrevPage,\n            NextPage,\n            MorePage\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/AwaitingJobsPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: true *@\n@using System\n@using System.Collections.Generic\n@using Hangfire\n@using Hangfire.Common;\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@using Hangfire.States\n@using Hangfire.Storage\n@using Hangfire.Storage.Monitoring;\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.AwaitingJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    List<string> jobIds = null;\n    Pager pager = null;\n    JobList <AwaitingJobDto> jobs = null;\n    int jobCount = 0;\n\n    if (Storage.HasFeature(JobStorageFeatures.Monitoring.AwaitingJobs))\n    {\n        var monitor = Storage.GetMonitoringApi() as JobStorageMonitor;\n        if (monitor == null) \n        {\n            throw new NotSupportedException(\"MonitoringApi should inherit the `JobStorageMonitor` class\");\n        }\n\n        pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.AwaitingCount());\n        jobs = monitor.AwaitingJobs(pager.FromRecord, pager.RecordsPerPage);\n        jobCount = jobs.Count;\n    }\n    else\n    {\n        using (var connection = Storage.GetReadOnlyConnection())\n        {\n            var storageConnection = connection as JobStorageConnection;\n\n            if (storageConnection != null)\n            {\n                pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, storageConnection.GetSetCount(\"awaiting\"));\n                jobIds = storageConnection.GetRangeFromSet(\"awaiting\", pager.FromRecord, pager.FromRecord + pager.RecordsPerPage - 1);\n                jobCount = jobIds.Count;\n            }\n        }\n    }\n}\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        @Html.JobsSidebar()\n    </div>\n    <div class=\"col-md-9\">\n        <h1 id=\"page-title\" class=\"page-header\">@Strings.AwaitingJobsPage_Title</h1>\n\n        @if (jobIds == null && jobs == null)\n        {\n            <div class=\"alert alert-warning\">\n                <h4>@Strings.AwaitingJobsPage_ContinuationsWarning_Title</h4>\n                <p>@Strings.AwaitingJobsPage_ContinuationsWarning_Text</p>\n            </div>\n        }\n        else if (jobCount > 0)\n        {\n            <div class=\"js-jobs-list\">\n                <div class=\"btn-toolbar btn-toolbar-top\">\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-primary\"\n                                data-url=\"@Url.To(\"/jobs/awaiting/enqueue\")\"\n                                data-loading-text=\"@Strings.Common_Enqueueing\">\n                            <span class=\"glyphicon glyphicon-repeat\"></span>\n                            @Strings.Common_EnqueueButton_Text\n                        </button>\n                    }\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-default\"\n                                data-url=\"@Url.To(\"/jobs/awaiting/delete\")\"\n                                data-loading-text=\"@Strings.Common_Deleting\"\n                                data-confirm=\"@Strings.Common_DeleteConfirm\">\n                            <span class=\"glyphicon glyphicon-remove\"></span>\n                            @Strings.Common_DeleteSelected\n                        </button>\n                    }\n                    @Html.PerPageSelector(pager)\n                </div>\n\n                <div class=\"table-responsive\">\n                    <table class=\"table table-hover\" aria-describedby=\"page-title\">\n                        <thead>\n                            <tr>\n                                @if (!IsReadOnly)\n                                {\n                                    <th class=\"min-width\">\n                                        <input type=\"checkbox\" class=\"js-jobs-list-select-all\"/>\n                                    </th>\n                                }\n                                <th class=\"min-width\">@Strings.Common_Id</th>\n                                <th>@Strings.Common_Job</th>\n                                <th class=\"min-width\">@Strings.AwaitingJobsPage_Table_Options</th>\n                                <th class=\"min-width\">@Strings.AwaitingJobsPage_Table_Parent</th>\n                                <th class=\"align-right\">@Strings.AwaitingJobsPage_Table_Since</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            @for (var i = 0; i < jobCount; i++)\n                            {\n                                var jobId = jobIds != null ? jobIds[i] : jobs[i].Key;\n\n                                Job job = null;\n                                bool inAwaitingState = true;\n                                IDictionary<string, string> stateData = null;\n                                string parentStateName = null;\n                                DateTime? awaitingSince = null;\n\n                                if (jobs != null)\n                                {\n                                    if (jobs[i].Value != null)\n                                    {\n                                        job = jobs[i].Value.Job;\n                                        inAwaitingState = jobs[i].Value.InAwaitingState;\n                                        stateData = jobs[i].Value.StateData;\n                                        parentStateName = jobs[i].Value.ParentStateName;\n                                        awaitingSince = jobs[i].Value.AwaitingAt;\n                                    }\n                                }\n                                else\n                                {\n                                    using (var connection = Storage.GetReadOnlyConnection())\n                                    {\n                                        var jobData = connection.GetJobData(jobId);\n                                        var state = connection.GetStateData(jobId);\n                                        inAwaitingState = AwaitingState.StateName.Equals(state?.Name);\n\n                                        if (state != null && inAwaitingState)\n                                        {\n                                            var parentState = connection.GetStateData(state.Data[\"ParentId\"]);\n                                            parentStateName = parentState.Name;\n                                        }\n\n                                        job = jobData.Job;\n                                        stateData = state?.Data;\n                                        awaitingSince = jobData.CreatedAt;\n                                    }\n                                }\n\n                                <tr class=\"js-jobs-list-row @(job == null || !inAwaitingState ? \"obsolete-data\" : null) @(job != null && inAwaitingState ? \"hover\" : null)\">\n                                    @if (!IsReadOnly)\n                                    {\n                                        <td>\n                                            @if (job != null && inAwaitingState)\n                                            {\n                                                <input type=\"checkbox\" class=\"js-jobs-list-checkbox\" name=\"jobs[]\" value=\"@jobId\" />\n                                            }\n                                        </td>\n                                    }\n                                    <td class=\"min-width\">\n                                        @Html.JobIdLink(jobId)\n                                        @if (job != null && !inAwaitingState)\n                                        {\n                                            <span title=\"@Strings.Common_JobStateChanged_Text\" class=\"glyphicon glyphicon-question-sign\"></span>\n                                        }\n                                    </td>\n                                    @if (job == null)\n                                    {\n                                        <td colspan=\"4\"><em>@Strings.Common_JobExpired</em></td>\n                                    }\n                                    else\n                                    {\n                                        <td class=\"word-break\">\n                                            @Html.JobNameLink(jobId, job)\n                                        </td>\n                                        <td class=\"min-width\">\n                                            @if (stateData != null && stateData.ContainsKey(\"Options\") && !String.IsNullOrWhiteSpace(stateData[\"Options\"]))\n                                            {\n                                                var optionsDescription = stateData[\"Options\"];\n                                                if (Enum.TryParse(optionsDescription, out JobContinuationOptions options))\n                                                {\n                                                    optionsDescription = options.ToString(\"G\");\n                                                }\n                                                <code>@optionsDescription</code>\n                                            }\n                                            else\n                                            {\n                                                <em>@Strings.Common_NotAvailable</em>\n                                            }\n                                        </td>\n                                        <td class=\"min-width\">\n                                            @if (parentStateName != null)\n                                            {\n                                                <a href=\"@Url.JobDetails(stateData[\"ParentId\"])\" class=\"text-decoration-none\">\n                                                    @Html.StateLabel(parentStateName, parentStateName, hover: true)\n                                                </a>\n                                            }\n                                            else\n                                            {\n                                                <em>@Strings.Common_NotAvailable</em>\n                                            }\n                                        </td>\n                                        <td class=\"min-width align-right\">\n                                            @if (awaitingSince.HasValue)\n                                            {\n                                                @Html.RelativeTime(awaitingSince.Value)\n                                            }\n                                            else\n                                            {\n                                                <em>@Strings.Common_NotAvailable</em>\n                                            }\n                                        </td>\n                                    }\n                                </tr>\n                            }\n                        </tbody>\n                    </table>\n                </div>\n                @Html.Paginator(pager)\n            </div>\n        }\n        else\n        {\n            <div class=\"alert alert-info\">\n                @Strings.AwaitingJobsPage_NoJobs\n            </div>\n        }\n    </div>\n</div>\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/AwaitingJobsPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n    using System;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n    using System.Collections.Generic;\n    \n    #line default\n    #line hidden\n    using System.Linq;\n    using System.Text;\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n    using Hangfire.Common;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 7 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 8 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    #line 9 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n    using Hangfire.States;\n    \n    #line default\n    #line hidden\n    \n    #line 10 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n    using Hangfire.Storage;\n    \n    #line default\n    #line hidden\n    \n    #line 11 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n    using Hangfire.Storage.Monitoring;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class AwaitingJobsPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n            \n            #line 13 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n  \n    Layout = new LayoutPage(Strings.AwaitingJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    List<string> jobIds = null;\n    Pager pager = null;\n    JobList <AwaitingJobDto> jobs = null;\n    int jobCount = 0;\n\n    if (Storage.HasFeature(JobStorageFeatures.Monitoring.AwaitingJobs))\n    {\n        var monitor = Storage.GetMonitoringApi() as JobStorageMonitor;\n        if (monitor == null) \n        {\n            throw new NotSupportedException(\"MonitoringApi should inherit the `JobStorageMonitor` class\");\n        }\n\n        pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.AwaitingCount());\n        jobs = monitor.AwaitingJobs(pager.FromRecord, pager.RecordsPerPage);\n        jobCount = jobs.Count;\n    }\n    else\n    {\n        using (var connection = Storage.GetReadOnlyConnection())\n        {\n            var storageConnection = connection as JobStorageConnection;\n\n            if (storageConnection != null)\n            {\n                pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, storageConnection.GetSetCount(\"awaiting\"));\n                jobIds = storageConnection.GetRangeFromSet(\"awaiting\", pager.FromRecord, pager.FromRecord + pager.RecordsPerPage - 1);\n                jobCount = jobIds.Count;\n            }\n        }\n    }\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-3\\\">\\r\\n        \");\n\n\n            \n            #line 56 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n   Write(Html.JobsSidebar());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"col-md-9\\\">\\r\\n        <h1 id=\\\"page-title\\\" class=\\\"page\" +\n\"-header\\\">\");\n\n\n            \n            #line 59 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                           Write(Strings.AwaitingJobsPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 61 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n         if (jobIds == null && jobs == null)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-warning\\\">\\r\\n                <h4>\");\n\n\n            \n            #line 64 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n               Write(Strings.AwaitingJobsPage_ContinuationsWarning_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h4>\\r\\n                <p>\");\n\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n              Write(Strings.AwaitingJobsPage_ContinuationsWarning_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</p>\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 67 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n        }\n        else if (jobCount > 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"js-jobs-list\\\">\\r\\n                <div class=\\\"btn-toolbar b\" +\n\"tn-toolbar-top\\\">\\r\\n\");\n\n\n            \n            #line 72 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-primar\" +\n\"y\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 75 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                     Write(Url.To(\"/jobs/awaiting/enqueue\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 76 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                              Write(Strings.Common_Enqueueing);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                            <span class=\\\"glyphicon glyphicon-repeat\\\"></span>\\r\" +\n\"\\n                            \");\n\n\n            \n            #line 78 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                       Write(Strings.Common_EnqueueButton_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 80 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 81 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-defaul\" +\n\"t\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 84 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                     Write(Url.To(\"/jobs/awaiting/delete\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 85 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                              Write(Strings.Common_Deleting);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-confirm=\\\"\");\n\n\n            \n            #line 86 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                         Write(Strings.Common_DeleteConfirm);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                            <span class=\\\"glyphicon glyphicon-remove\\\"></span>\\r\" +\n\"\\n                            \");\n\n\n            \n            #line 88 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                       Write(Strings.Common_DeleteSelected);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 90 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    \");\n\n\n            \n            #line 91 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n               Write(Html.PerPageSelector(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n\\r\\n                <div class=\\\"table-responsive\\\">\\r\\n     \" +\n\"               <table class=\\\"table table-hover\\\" aria-describedby=\\\"page-title\\\">\\r\\n\" +\n\"                        <thead>\\r\\n                            <tr>\\r\\n\");\n\n\n            \n            #line 98 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                 if (!IsReadOnly)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-select-all\\\"/>\\r\\n   \" +\n\"                                 </th>\\r\\n\");\n\n\n            \n            #line 103 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 104 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                 Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th>\");\n\n\n            \n            #line 105 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                               Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 106 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                 Write(Strings.AwaitingJobsPage_Table_Options);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 107 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                 Write(Strings.AwaitingJobsPage_Table_Parent);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 108 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                   Write(Strings.AwaitingJobsPage_Table_Since);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            </tr>\\r\\n                        </thead>\\r\\n     \" +\n\"                   <tbody>\\r\\n\");\n\n\n            \n            #line 112 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                             for (var i = 0; i < jobCount; i++)\n                            {\n                                var jobId = jobIds != null ? jobIds[i] : jobs[i].Key;\n\n                                Job job = null;\n                                bool inAwaitingState = true;\n                                IDictionary<string, string> stateData = null;\n                                string parentStateName = null;\n                                DateTime? awaitingSince = null;\n\n                                if (jobs != null)\n                                {\n                                    if (jobs[i].Value != null)\n                                    {\n                                        job = jobs[i].Value.Job;\n                                        inAwaitingState = jobs[i].Value.InAwaitingState;\n                                        stateData = jobs[i].Value.StateData;\n                                        parentStateName = jobs[i].Value.ParentStateName;\n                                        awaitingSince = jobs[i].Value.AwaitingAt;\n                                    }\n                                }\n                                else\n                                {\n                                    using (var connection = Storage.GetReadOnlyConnection())\n                                    {\n                                        var jobData = connection.GetJobData(jobId);\n                                        var state = connection.GetStateData(jobId);\n                                        inAwaitingState = AwaitingState.StateName.Equals(state?.Name);\n\n                                        if (state != null && inAwaitingState)\n                                        {\n                                            var parentState = connection.GetStateData(state.Data[\"ParentId\"]);\n                                            parentStateName = parentState.Name;\n                                        }\n\n                                        job = jobData.Job;\n                                        stateData = state?.Data;\n                                        awaitingSince = jobData.CreatedAt;\n                                    }\n                                }\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <tr class=\\\"js-jobs-list-row \");\n\n\n            \n            #line 153 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                        Write(job == null || !inAwaitingState ? \"obsolete-data\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 153 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                                                                                    Write(job != null && inAwaitingState ? \"hover\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 154 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                     if (!IsReadOnly)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td>\\r\\n\");\n\n\n            \n            #line 157 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                             if (job != null && inAwaitingState)\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <input type=\\\"checkbox\\\" class=\\\"js-\" +\n\"jobs-list-checkbox\\\" name=\\\"jobs[]\\\" value=\\\"\");\n\n\n            \n            #line 159 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                                                                                     Write(jobId);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" />\\r\\n\");\n\n\n            \n            #line 160 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n            \n            #line 162 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   \");\n\n\n            \n            #line 164 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                   Write(Html.JobIdLink(jobId));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 165 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                         if (job != null && !inAwaitingState)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <span title=\\\"\");\n\n\n            \n            #line 167 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                    Write(Strings.Common_JobStateChanged_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"glyphicon glyphicon-question-sign\\\"></span>\\r\\n\");\n\n\n            \n            #line 168 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\");\n\n\n            \n            #line 170 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                     if (job == null)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td colspan=\\\"4\\\"><em>\");\n\n\n            \n            #line 172 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                       Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 173 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                    }\n                                    else\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td class=\\\"word-break\\\">\\r\\n                \" +\n\"                            \");\n\n\n            \n            #line 177 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                       Write(Html.JobNameLink(jobId, job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                        </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                        <td class=\\\"min-width\\\">\\r\\n\");\n\n\n            \n            #line 180 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                             if (stateData != null && stateData.ContainsKey(\"Options\") && !String.IsNullOrWhiteSpace(stateData[\"Options\"]))\n                                            {\n                                                var optionsDescription = stateData[\"Options\"];\n                                                if (Enum.TryParse(optionsDescription, out JobContinuationOptions options))\n                                                {\n                                                    optionsDescription = options.ToString(\"G\");\n                                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <code>\");\n\n\n            \n            #line 187 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                 Write(optionsDescription);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</code>\\r\\n\");\n\n\n            \n            #line 188 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                            }\n                                            else\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <em>\");\n\n\n            \n            #line 191 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                               Write(Strings.Common_NotAvailable);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n\");\n\n\n            \n            #line 192 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                        <td class=\\\"min-width\\\">\\r\\n\");\n\n\n            \n            #line 195 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                             if (parentStateName != null)\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <a href=\\\"\");\n\n\n            \n            #line 197 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                    Write(Url.JobDetails(stateData[\"ParentId\"]));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"text-decoration-none\\\">\\r\\n                                                \" +\n\"    \");\n\n\n            \n            #line 198 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                               Write(Html.StateLabel(parentStateName, parentStateName, hover: true));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                                </a>\\r\\n\");\n\n\n            \n            #line 200 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                            }\n                                            else\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <em>\");\n\n\n            \n            #line 203 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                               Write(Strings.Common_NotAvailable);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n\");\n\n\n            \n            #line 204 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                        <td class=\\\"min-width align-right\\\">\\r\\n\");\n\n\n            \n            #line 207 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                             if (awaitingSince.HasValue)\n                                            {\n                                                \n            \n            #line default\n            #line hidden\n            \n            #line 209 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                           Write(Html.RelativeTime(awaitingSince.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 209 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                                                                       \n                                            }\n                                            else\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <em>\");\n\n\n            \n            #line 213 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                               Write(Strings.Common_NotAvailable);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n\");\n\n\n            \n            #line 214 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n            \n            #line 216 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </tr>\\r\\n\");\n\n\n            \n            #line 218 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        </tbody>\\r\\n                    </table>\\r\\n                <\" +\n\"/div>\\r\\n                \");\n\n\n            \n            #line 222 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n           Write(Html.Paginator(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 224 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n        }\n        else\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-info\\\">\\r\\n                \");\n\n\n            \n            #line 228 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n           Write(Strings.AwaitingJobsPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 230 \"..\\..\\Dashboard\\Pages\\AwaitingJobsPage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\\r\\n\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/DeletedJobsPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using Hangfire\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.DeletedJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.DeletedListCount());\n    var jobs = monitor.DeletedJobs(pager.FromRecord, pager.RecordsPerPage);\n}\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        @Html.JobsSidebar()\n    </div>\n    <div class=\"col-md-9\">\n        <h1 id=\"page-title\" class=\"page-header\">@Strings.DeletedJobsPage_Title</h1>\n\n        @if (pager.TotalPageCount == 0)\n        {\n            <div class=\"alert alert-info\">\n                @Strings.DeletedJobsPage_NoJobs\n            </div>\n        }\n        else\n        {\n            <div class=\"js-jobs-list\">\n                <div class=\"btn-toolbar btn-toolbar-top\">\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-primary\"\n                                data-url=\"@Url.To(\"/jobs/deleted/requeue\")\"\n                                data-loading-text=\"@Strings.Common_Enqueueing\"\n                                disabled=\"disabled\">\n                            <span class=\"glyphicon glyphicon-repeat\"></span>\n                            @Strings.Common_RequeueJobs\n                        </button>\n                    }\n                    @Html.PerPageSelector(pager)\n                </div>\n                <div class=\"table-responsive\">\n                    <table class=\"table\" aria-describedby=\"page-title\">\n                        <thead>\n                            <tr>\n                                @if (!IsReadOnly)\n                                {\n                                    <th class=\"min-width\">\n                                        <input type=\"checkbox\" class=\"js-jobs-list-select-all\"/>\n                                    </th>\n                                }\n                                <th class=\"min-width\">@Strings.Common_Id</th>\n                                <th>@Strings.Common_Job</th>\n                                @if (jobs.Any(x => x.Value?.StateData != null))\n                                {\n                                    <th class=\"min-width\">@Strings.DeletedJobsPage_Table_Exception</th>\n                                }\n                                <th class=\"align-right\">@Strings.DeletedJobsPage_Table_Deleted</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            @foreach (var job in jobs)\n                            {\n                                <tr class=\"js-jobs-list-row @(job.Value == null || !job.Value.InDeletedState ? \"obsolete-data\" : null) @(job.Value != null && job.Value.InDeletedState && job.Value != null ? \"hover\" : null)\">\n                                    @if (!IsReadOnly)\n                                    {\n                                        <td>\n                                            @if (job.Value != null && job.Value.InDeletedState)\n                                            {\n                                                <input type=\"checkbox\" class=\"js-jobs-list-checkbox\" name=\"jobs[]\" value=\"@job.Key\"/>\n                                            }\n                                        </td>\n                                    }\n                                    <td class=\"min-width\">\n                                        @Html.JobIdLink(job.Key)\n                                        @if (job.Value != null && !job.Value.InDeletedState)\n                                        {\n                                            <span title=\"@Strings.Common_JobStateChanged_Text\" class=\"glyphicon glyphicon-question-sign\"></span>\n                                        }\n                                    </td>\n\n                                    @if (job.Value == null)\n                                    {\n                                        if (jobs.Any(x => x.Value?.StateData != null))\n                                        {\n                                            <td colspan=\"3\"><em>@Strings.Common_JobExpired</em></td>\n                                        }\n                                        else\n                                        {\n                                            <td colspan=\"2\"><em>@Strings.Common_JobExpired</em></td>\n                                        }\n                                    }\n                                    else\n                                    {\n                                        <td class=\"word-break\">\n                                            @Html.JobNameLink(job.Key, job.Value.Job)\n                                        </td>\n                                        if (job.Value.StateData != null)\n                                        {\n                                            ExceptionInfo exception = null;\n                                            string typeName = null;\n                                            if (job.Value.StateData.TryGetValue(\"Exception\", out var serializedException) &&\n                                                !String.IsNullOrWhiteSpace(serializedException))\n                                            {\n                                                exception = Common.SerializationHelper.Deserialize<ExceptionInfo>(serializedException, Common.SerializationOption.Internal);\n                                                var commaIndex = exception.Type.IndexOf(\",\", StringComparison.OrdinalIgnoreCase);\n                                                typeName = commaIndex > 0 \n                                                    ? exception.Type.Substring(0, commaIndex)\n                                                    : exception.Type;\n                                            }\n                                            <td class=min-width>\n                                                @if (!String.IsNullOrEmpty(typeName))\n                                                {\n                                                    <code title=\"@exception.Message\">@typeName</code>\n                                                }\n                                            </td>\n                                        }\n                                        <td class=\"min-width align-right\">\n                                            @if (job.Value.DeletedAt.HasValue)\n                                            {\n                                                @Html.RelativeTime(job.Value.DeletedAt.Value)\n                                            }\n                                        </td>\n                                    }\n                                </tr>\n                            }\n                        </tbody>\n                    </table>\n                </div>\n\n                @Html.Paginator(pager)\n            </div>\n        }\n    </div>\n</div>\n\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/DeletedJobsPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class DeletedJobsPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n            \n            #line 7 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n  \n    Layout = new LayoutPage(Strings.DeletedJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.DeletedListCount());\n    var jobs = monitor.DeletedJobs(pager.FromRecord, pager.RecordsPerPage);\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-3\\\">\\r\\n        \");\n\n\n            \n            #line 22 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n   Write(Html.JobsSidebar());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"col-md-9\\\">\\r\\n        <h1 id=\\\"page-title\\\" class=\\\"page\" +\n\"-header\\\">\");\n\n\n            \n            #line 25 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                           Write(Strings.DeletedJobsPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 27 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n         if (pager.TotalPageCount == 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-info\\\">\\r\\n                \");\n\n\n            \n            #line 30 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n           Write(Strings.DeletedJobsPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 32 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n        }\n        else\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"js-jobs-list\\\">\\r\\n                <div class=\\\"btn-toolbar b\" +\n\"tn-toolbar-top\\\">\\r\\n\");\n\n\n            \n            #line 37 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-primar\" +\n\"y\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 40 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                     Write(Url.To(\"/jobs/deleted/requeue\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 41 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                              Write(Strings.Common_Enqueueing);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                disabled=\\\"disabled\\\">\\r\\n                        \" +\n\"    <span class=\\\"glyphicon glyphicon-repeat\\\"></span>\\r\\n                          \" +\n\"  \");\n\n\n            \n            #line 44 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                       Write(Strings.Common_RequeueJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 46 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    \");\n\n\n            \n            #line 47 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n               Write(Html.PerPageSelector(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n                <div class=\\\"table-responsive\\\">\\r\\n       \" +\n\"             <table class=\\\"table\\\" aria-describedby=\\\"page-title\\\">\\r\\n              \" +\n\"          <thead>\\r\\n                            <tr>\\r\\n\");\n\n\n            \n            #line 53 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                 if (!IsReadOnly)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-select-all\\\"/>\\r\\n   \" +\n\"                                 </th>\\r\\n\");\n\n\n            \n            #line 58 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 59 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                 Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th>\");\n\n\n            \n            #line 60 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                               Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n\");\n\n\n            \n            #line 61 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                 if (jobs.Any(x => x.Value?.StateData != null))\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 63 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                     Write(Strings.DeletedJobsPage_Table_Exception);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n\");\n\n\n            \n            #line 64 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                   Write(Strings.DeletedJobsPage_Table_Deleted);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            </tr>\\r\\n                        </thead>\\r\\n     \" +\n\"                   <tbody>\\r\\n\");\n\n\n            \n            #line 69 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                             foreach (var job in jobs)\n                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <tr class=\\\"js-jobs-list-row \");\n\n\n            \n            #line 71 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                        Write(job.Value == null || !job.Value.InDeletedState ? \"obsolete-data\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 71 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                                                                                                   Write(job.Value != null && job.Value.InDeletedState && job.Value != null ? \"hover\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 72 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                     if (!IsReadOnly)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td>\\r\\n\");\n\n\n            \n            #line 75 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                             if (job.Value != null && job.Value.InDeletedState)\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <input type=\\\"checkbox\\\" class=\\\"js-\" +\n\"jobs-list-checkbox\\\" name=\\\"jobs[]\\\" value=\\\"\");\n\n\n            \n            #line 77 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                                                                                     Write(job.Key);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"/>\\r\\n\");\n\n\n            \n            #line 78 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n            \n            #line 80 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   \");\n\n\n            \n            #line 82 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                   Write(Html.JobIdLink(job.Key));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 83 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                         if (job.Value != null && !job.Value.InDeletedState)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <span title=\\\"\");\n\n\n            \n            #line 85 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                    Write(Strings.Common_JobStateChanged_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"glyphicon glyphicon-question-sign\\\"></span>\\r\\n\");\n\n\n            \n            #line 86 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\\r\\n\");\n\n\n            \n            #line 89 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                     if (job.Value == null)\n                                    {\n                                        if (jobs.Any(x => x.Value?.StateData != null))\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <td colspan=\\\"3\\\"><em>\");\n\n\n            \n            #line 93 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                           Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 94 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                        }\n                                        else\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <td colspan=\\\"2\\\"><em>\");\n\n\n            \n            #line 97 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                           Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 98 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                        }\n                                    }\n                                    else\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td class=\\\"word-break\\\">\\r\\n                \" +\n\"                            \");\n\n\n            \n            #line 103 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                       Write(Html.JobNameLink(job.Key, job.Value.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                        </td>\\r\\n\");\n\n\n            \n            #line 105 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                        if (job.Value.StateData != null)\n                                        {\n                                            ExceptionInfo exception = null;\n                                            string typeName = null;\n                                            if (job.Value.StateData.TryGetValue(\"Exception\", out var serializedException) &&\n                                                !String.IsNullOrWhiteSpace(serializedException))\n                                            {\n                                                exception = Common.SerializationHelper.Deserialize<ExceptionInfo>(serializedException, Common.SerializationOption.Internal);\n                                                var commaIndex = exception.Type.IndexOf(\",\", StringComparison.OrdinalIgnoreCase);\n                                                typeName = commaIndex > 0 \n                                                    ? exception.Type.Substring(0, commaIndex)\n                                                    : exception.Type;\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <td class=min-width>\\r\\n\");\n\n\n            \n            #line 119 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                 if (!String.IsNullOrEmpty(typeName))\n                                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                    <code title=\\\"\");\n\n\n            \n            #line 121 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                            Write(exception.Message);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\");\n\n\n            \n            #line 121 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                                                Write(typeName);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</code>\\r\\n\");\n\n\n            \n            #line 122 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            </td>\\r\\n\");\n\n\n            \n            #line 124 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td class=\\\"min-width align-right\\\">\\r\\n\");\n\n\n            \n            #line 126 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                             if (job.Value.DeletedAt.HasValue)\n                                            {\n                                                \n            \n            #line default\n            #line hidden\n            \n            #line 128 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                           Write(Html.RelativeTime(job.Value.DeletedAt.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 128 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                                                                             \n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n            \n            #line 131 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </tr>\\r\\n\");\n\n\n            \n            #line 133 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        </tbody>\\r\\n                    </table>\\r\\n                <\" +\n\"/div>\\r\\n\\r\\n                \");\n\n\n            \n            #line 138 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n           Write(Html.Paginator(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 140 \"..\\..\\Dashboard\\Pages\\DeletedJobsPage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\\r\\n\\r\\n\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/EnqueuedJobsPage.cs",
    "content": "﻿namespace Hangfire.Dashboard.Pages\n{\n    partial class EnqueuedJobsPage\n    {\n        public EnqueuedJobsPage(string queue)\n        {\n            Queue = queue;\n        }\n\n        public string Queue { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/EnqueuedJobsPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using System.Collections\n@using System.Collections.Generic\n@using Hangfire\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Queue);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.EnqueuedCount(Queue));\n    var enqueuedJobs = monitor.EnqueuedJobs(Queue, pager.FromRecord, pager.RecordsPerPage);\n}\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        @Html.JobsSidebar()\n    </div>\n    <div class=\"col-md-9\">\n        @Html.Breadcrumbs(Queue, new Dictionary<string, string>\n        {\n            { \"Queues\", Url.LinkToQueues() }\n        })\n\n        <h1 id=\"page-title\" class=\"page-header\">@Queue <small>@Strings.EnqueuedJobsPage_Title</small></h1>\n\n        @if (pager.TotalPageCount == 0)\n        {\n            <div class=\"alert alert-info\">\n                @Strings.EnqueuedJobsPage_NoJobs\n            </div>\n        }\n        else\n        {\n            <div class=\"js-jobs-list\">\n                <div class=\"btn-toolbar btn-toolbar-top\">\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-default\"\n                                data-url=\"@Url.To(\"/jobs/enqueued/delete\")\"\n                                data-loading-text=\"@Strings.Common_Deleting\"\n                                data-confirm=\"@Strings.Common_DeleteConfirm\"\n                                disabled=\"disabled\">\n                            <span class=\"glyphicon glyphicon-remove\"></span>\n                            @Strings.Common_DeleteSelected\n                        </button>\n                    }\n                    @Html.PerPageSelector(pager)\n                </div>\n\n                <div class=\"table-responsive\">\n                    <table class=\"table\" aria-describedby=\"page-title\">\n                        <thead>\n                        <tr>\n                            @if (!IsReadOnly)\n                            {\n                                <th class=\"min-width\">\n                                    <input type=\"checkbox\" class=\"js-jobs-list-select-all\"/>\n                                </th>\n                            }\n                            <th class=\"min-width\">@Strings.Common_Id</th>\n                            <th class=\"min-width\">@Strings.Common_State</th>\n                            <th>@Strings.Common_Job</th>\n                            <th class=\"align-right\">@Strings.Common_Enqueued</th>\n                        </tr>\n                        </thead>\n                        <tbody>\n                        @foreach (var job in enqueuedJobs)\n                        {\n                            <tr class=\"js-jobs-list-row hover @(job.Value == null || !job.Value.InEnqueuedState ? \"obsolete-data\" : null)\">\n                                @if (!IsReadOnly)\n                                {\n                                    <td>\n                                        @if (job.Value != null && job.Value.InEnqueuedState)\n                                        {\n                                            <input type=\"checkbox\" class=\"js-jobs-list-checkbox\" name=\"jobs[]\" value=\"@job.Key\"/>\n                                        }\n                                    </td>\n                                }\n                                <td class=\"min-width\">\n                                    @Html.JobIdLink(job.Key)\n                                    @if (job.Value != null && !job.Value.InEnqueuedState)\n                                    {\n                                        <span title=\"@Strings.Common_JobStateChanged_Text\" class=\"glyphicon glyphicon-question-sign\"></span>\n                                    }\n                                </td>\n                                @if (job.Value == null)\n                                {\n                                    <td colspan=\"3\"><em>@Strings.Common_JobExpired</em></td>\n                                }\n                                else\n                                {\n                                    <td class=\"min-width\">\n                                        @Html.StateLabel(job.Value.State)\n                                    </td>\n                                    <td class=\"word-break\">\n                                        @Html.JobNameLink(job.Key, job.Value.Job)\n                                    </td>\n                                    <td class=\"align-right\">\n                                        @if (job.Value.EnqueuedAt.HasValue)\n                                        {\n                                            @Html.RelativeTime(job.Value.EnqueuedAt.Value)\n                                        }\n                                        else\n                                        {\n                                            <em>@Strings.Common_NotAvailable</em>\n                                        }\n                                    </td>\n                                }\n                            </tr>\n                        }\n                        </tbody>\n                    </table>\n                </div>\n\n                @Html.Paginator(pager)\n            </div>\n        }\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/EnqueuedJobsPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n    using System.Collections;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n    using System.Collections.Generic;\n    \n    #line default\n    #line hidden\n    using System.Linq;\n    using System.Text;\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 7 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class EnqueuedJobsPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n  \n    Layout = new LayoutPage(Queue);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.EnqueuedCount(Queue));\n    var enqueuedJobs = monitor.EnqueuedJobs(Queue, pager.FromRecord, pager.RecordsPerPage);\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-3\\\">\\r\\n        \");\n\n\n            \n            #line 24 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n   Write(Html.JobsSidebar());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"col-md-9\\\">\\r\\n        \");\n\n\n            \n            #line 27 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n   Write(Html.Breadcrumbs(Queue, new Dictionary<string, string>\n        {\n            { \"Queues\", Url.LinkToQueues() }\n        }));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\\r\\n        <h1 id=\\\"page-title\\\" class=\\\"page-header\\\">\");\n\n\n            \n            #line 32 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                           Write(Queue);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" <small>\");\n\n\n            \n            #line 32 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                                         Write(Strings.EnqueuedJobsPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</small></h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 34 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n         if (pager.TotalPageCount == 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-info\\\">\\r\\n                \");\n\n\n            \n            #line 37 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n           Write(Strings.EnqueuedJobsPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 39 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n        }\n        else\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"js-jobs-list\\\">\\r\\n                <div class=\\\"btn-toolbar b\" +\n\"tn-toolbar-top\\\">\\r\\n\");\n\n\n            \n            #line 44 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-defaul\" +\n\"t\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 47 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                     Write(Url.To(\"/jobs/enqueued/delete\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 48 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                              Write(Strings.Common_Deleting);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-confirm=\\\"\");\n\n\n            \n            #line 49 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                         Write(Strings.Common_DeleteConfirm);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                disabled=\\\"disabled\\\">\\r\\n                        \" +\n\"    <span class=\\\"glyphicon glyphicon-remove\\\"></span>\\r\\n                          \" +\n\"  \");\n\n\n            \n            #line 52 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                       Write(Strings.Common_DeleteSelected);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 54 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    \");\n\n\n            \n            #line 55 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n               Write(Html.PerPageSelector(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n\\r\\n                <div class=\\\"table-responsive\\\">\\r\\n     \" +\n\"               <table class=\\\"table\\\" aria-describedby=\\\"page-title\\\">\\r\\n            \" +\n\"            <thead>\\r\\n                        <tr>\\r\\n\");\n\n\n            \n            #line 62 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                             if (!IsReadOnly)\n                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th class=\\\"min-width\\\">\\r\\n                         \" +\n\"           <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-select-all\\\"/>\\r\\n           \" +\n\"                     </th>\\r\\n\");\n\n\n            \n            #line 67 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 68 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                             Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 69 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                             Write(Strings.Common_State);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th>\");\n\n\n            \n            #line 70 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                           Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 71 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                               Write(Strings.Common_Enqueued);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                        </tr>\\r\\n                        </thead>\\r\\n         \" +\n\"               <tbody>\\r\\n\");\n\n\n            \n            #line 75 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                         foreach (var job in enqueuedJobs)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <tr class=\\\"js-jobs-list-row hover \");\n\n\n            \n            #line 77 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                                          Write(job.Value == null || !job.Value.InEnqueuedState ? \"obsolete-data\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 78 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                 if (!IsReadOnly)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td>\\r\\n\");\n\n\n            \n            #line 81 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                         if (job.Value != null && job.Value.InEnqueuedState)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <input type=\\\"checkbox\\\" class=\\\"js-jobs\" +\n\"-list-checkbox\\\" name=\\\"jobs[]\\\" value=\\\"\");\n\n\n            \n            #line 83 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                                                                                                 Write(job.Key);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"/>\\r\\n\");\n\n\n            \n            #line 84 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\");\n\n\n            \n            #line 86 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <td class=\\\"min-width\\\">\\r\\n                         \" +\n\"           \");\n\n\n            \n            #line 88 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                               Write(Html.JobIdLink(job.Key));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 89 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                     if (job.Value != null && !job.Value.InEnqueuedState)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <span title=\\\"\");\n\n\n            \n            #line 91 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                                Write(Strings.Common_JobStateChanged_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"glyphicon glyphicon-question-sign\\\"></span>\\r\\n\");\n\n\n            \n            #line 92 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </td>\\r\\n\");\n\n\n            \n            #line 94 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                 if (job.Value == null)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td colspan=\\\"3\\\"><em>\");\n\n\n            \n            #line 96 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                                   Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 97 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                }\n                                else\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   \");\n\n\n            \n            #line 101 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                   Write(Html.StateLabel(job.Value.State));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                    </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                    <td class=\\\"word-break\\\">\\r\\n                    \" +\n\"                    \");\n\n\n            \n            #line 104 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                   Write(Html.JobNameLink(job.Key, job.Value.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                    </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                    <td class=\\\"align-right\\\">\\r\\n\");\n\n\n            \n            #line 107 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                         if (job.Value.EnqueuedAt.HasValue)\n                                        {\n                                            \n            \n            #line default\n            #line hidden\n            \n            #line 109 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                       Write(Html.RelativeTime(job.Value.EnqueuedAt.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 109 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                                                                          \n                                        }\n                                        else\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <em>\");\n\n\n            \n            #line 113 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                           Write(Strings.Common_NotAvailable);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n\");\n\n\n            \n            #line 114 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\");\n\n\n            \n            #line 116 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            </tr>\\r\\n\");\n\n\n            \n            #line 118 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        </tbody>\\r\\n                    </table>\\r\\n                <\" +\n\"/div>\\r\\n\\r\\n                \");\n\n\n            \n            #line 123 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n           Write(Html.Paginator(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 125 \"..\\..\\Dashboard\\Pages\\EnqueuedJobsPage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/FailedJobsPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using System\n@using Hangfire\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.FailedJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.FailedCount());\n    var failedJobs = monitor.FailedJobs(pager.FromRecord, pager.RecordsPerPage);\n}\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        @Html.JobsSidebar()\n    </div>\n    <div class=\"col-md-9\">\n        <h1 id=\"page-title\" class=\"page-header\">@Strings.FailedJobsPage_Title</h1>\n\n        @if (pager.TotalPageCount == 0)\n        {\n            <div class=\"alert alert-success\">\n               @Strings.FailedJobsPage_NoJobs\n            </div>\n        }\n        else\n        {\n            <div class=\"alert alert-warning\">\n                @Html.Raw(Strings.FailedJobsPage_FailedJobsNotExpire_Warning_Html)\n            </div>\n            \n            <div class=\"js-jobs-list\">\n                <div class=\"btn-toolbar btn-toolbar-top\">\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-primary\"\n                                data-url=\"@Url.To(\"/jobs/failed/requeue\")\"\n                                data-loading-text=\"@Strings.Common_Enqueueing\"\n                                disabled=\"disabled\">\n                            <span class=\"glyphicon glyphicon-repeat\"></span>\n                            @Strings.Common_RequeueJobs\n                        </button>\n                    }\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-default\"\n                                data-url=\"@Url.To(\"/jobs/failed/delete\")\"\n                                data-loading-text=\"@Strings.Common_Deleting\"\n                                data-confirm=\"@Strings.Common_DeleteConfirm\">\n                            <span class=\"glyphicon glyphicon-remove\"></span>\n                            @Strings.Common_DeleteSelected\n                        </button>\n                    }\n                    @Html.PerPageSelector(pager)\n                </div>\n\n                <div class=\"table-responsive\">\n                    <table class=\"table\" aria-describedby=\"page-title\">\n                        <thead>\n                            <tr>\n                                @if (!IsReadOnly)\n                                {\n                                    <th class=\"min-width\">\n                                        <input type=\"checkbox\" class=\"js-jobs-list-select-all\"/>\n                                    </th>\n                                }\n                                <th class=\"min-width\">@Strings.Common_Id</th>\n                                <th>@Strings.Common_Job</th>\n                                <th class=\"align-right\">@Strings.FailedJobsPage_Table_Failed</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            @{ var index = 0; }\n                            @foreach (var job in failedJobs)\n                            {\n                                <tr class=\"js-jobs-list-row @(job.Value == null || !job.Value.InFailedState ? \"obsolete-data\" : null) @(job.Value != null && job.Value.InFailedState ? \"hover\" : null)\">\n                                    @if (!IsReadOnly)\n                                    {\n                                        <td rowspan=\"@(job.Value != null && job.Value.InFailedState ? \"2\" : \"1\")\">\n                                            @if (job.Value != null && job.Value.InFailedState)\n                                            {\n                                                <input type=\"checkbox\" class=\"js-jobs-list-checkbox\" name=\"jobs[]\" value=\"@job.Key\" />\n                                            }\n                                        </td>\n                                    }\n                                    <td class=\"min-width\" rowspan=\"@(job.Value != null && job.Value.InFailedState ? \"2\" : \"1\")\">\n                                        @Html.JobIdLink(job.Key)\n                                        @if (job.Value != null && !job.Value.InFailedState)\n                                        {\n                                            <span title=\"@Strings.Common_JobStateChanged_Text\" class=\"glyphicon glyphicon-question-sign\"></span>\n                                        }\n                                    </td>\n                                    @if (job.Value == null)\n                                    {\n                                        <td colspan=\"2\">\n                                            <em>@Strings.Common_JobExpired</em>\n                                        </td>\n                                    }\n                                    else\n                                    {\n                                        <td>\n                                            <div class=\"word-break\">\n                                                @Html.JobNameLink(job.Key, job.Value.Job)\n                                            </div>\n                                            @if (!String.IsNullOrEmpty(job.Value.ExceptionMessage) || !String.IsNullOrEmpty(job.Value.ExceptionDetails))\n                                            {\n                                                <div class=\"text-muted\">\n                                                    @job.Value.Reason <a class=\"expander\" href=\"#\">@(index == 0 ? Strings.Common_LessDetails : Strings.Common_MoreDetails)</a>\n                                                </div>\n                                            }\n                                        </td>\n                                        <td class=\"align-right\">\n                                            @if (job.Value.FailedAt.HasValue)\n                                            {\n                                                @Html.RelativeTime(job.Value.FailedAt.Value)\n                                            }\n                                        </td>\n                                    }\n                                </tr>\n                                if (job.Value != null && job.Value.InFailedState)\n                                {\n                                    <tr>\n                                        <td colspan=\"2\" class=\"failed-job-details\">\n                                            @{\n                                                var displayCss = index++ == 0 ? \"display-block\" : null;\n                                                var serverId = job.Value.StateData != null && job.Value.StateData.ContainsKey(\"ServerId\") ? $\" ({Html.ServerId(job.Value.StateData[\"ServerId\"])})\" : null;\n                                            }\n                                            <div class=\"expandable @displayCss\">\n                                                <h4>@job.Value.ExceptionType@Html.Raw(serverId)</h4>\n                                                <p class=\"text-muted\">\n                                                    @job.Value.ExceptionMessage\n                                                </p>\n\n                                                @if (!String.IsNullOrEmpty(job.Value.ExceptionDetails))\n                                                {\n                                                    <pre class=\"stack-trace\"><code>@Html.StackTrace(job.Value.ExceptionDetails)</code></pre>\n                                                }\n                                            </div>\n                                        </td>\n                                    </tr>\n                                }\n                            }\n                        </tbody>\n                    </table>\n                </div>\n\n                @Html.Paginator(pager)\n            </div>\n        }\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/FailedJobsPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n    using System;\n    \n    #line default\n    #line hidden\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class FailedJobsPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n            \n            #line 8 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n  \n    Layout = new LayoutPage(Strings.FailedJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.FailedCount());\n    var failedJobs = monitor.FailedJobs(pager.FromRecord, pager.RecordsPerPage);\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-3\\\">\\r\\n        \");\n\n\n            \n            #line 23 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n   Write(Html.JobsSidebar());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"col-md-9\\\">\\r\\n        <h1 id=\\\"page-title\\\" class=\\\"page\" +\n\"-header\\\">\");\n\n\n            \n            #line 26 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                           Write(Strings.FailedJobsPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 28 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n         if (pager.TotalPageCount == 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-success\\\">\\r\\n               \");\n\n\n            \n            #line 31 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n          Write(Strings.FailedJobsPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 33 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n        }\n        else\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-warning\\\">\\r\\n                \");\n\n\n            \n            #line 37 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n           Write(Html.Raw(Strings.FailedJobsPage_FailedJobsNotExpire_Warning_Html));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 39 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n            \n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"js-jobs-list\\\">\\r\\n                <div class=\\\"btn-toolbar b\" +\n\"tn-toolbar-top\\\">\\r\\n\");\n\n\n            \n            #line 42 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-primar\" +\n\"y\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 45 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                     Write(Url.To(\"/jobs/failed/requeue\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 46 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                              Write(Strings.Common_Enqueueing);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                disabled=\\\"disabled\\\">\\r\\n                        \" +\n\"    <span class=\\\"glyphicon glyphicon-repeat\\\"></span>\\r\\n                          \" +\n\"  \");\n\n\n            \n            #line 49 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                       Write(Strings.Common_RequeueJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 51 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 52 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-defaul\" +\n\"t\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 55 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                     Write(Url.To(\"/jobs/failed/delete\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 56 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                              Write(Strings.Common_Deleting);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-confirm=\\\"\");\n\n\n            \n            #line 57 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                         Write(Strings.Common_DeleteConfirm);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                            <span class=\\\"glyphicon glyphicon-remove\\\"></span>\\r\" +\n\"\\n                            \");\n\n\n            \n            #line 59 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                       Write(Strings.Common_DeleteSelected);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 61 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    \");\n\n\n            \n            #line 62 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n               Write(Html.PerPageSelector(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n\\r\\n                <div class=\\\"table-responsive\\\">\\r\\n     \" +\n\"               <table class=\\\"table\\\" aria-describedby=\\\"page-title\\\">\\r\\n            \" +\n\"            <thead>\\r\\n                            <tr>\\r\\n\");\n\n\n            \n            #line 69 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                 if (!IsReadOnly)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-select-all\\\"/>\\r\\n   \" +\n\"                                 </th>\\r\\n\");\n\n\n            \n            #line 74 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 75 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                 Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th>\");\n\n\n            \n            #line 76 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                               Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 77 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                   Write(Strings.FailedJobsPage_Table_Failed);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            </tr>\\r\\n                        </thead>\\r\\n     \" +\n\"                   <tbody>\\r\\n\");\n\n\n            \n            #line 81 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                               var index = 0; \n\n            \n            #line default\n            #line hidden\n\n            \n            #line 82 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                             foreach (var job in failedJobs)\n                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <tr class=\\\"js-jobs-list-row \");\n\n\n            \n            #line 84 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                        Write(job.Value == null || !job.Value.InFailedState ? \"obsolete-data\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 84 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                                                                                                  Write(job.Value != null && job.Value.InFailedState ? \"hover\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 85 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                     if (!IsReadOnly)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td rowspan=\\\"\");\n\n\n            \n            #line 87 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                 Write(job.Value != null && job.Value.InFailedState ? \"2\" : \"1\");\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 88 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                             if (job.Value != null && job.Value.InFailedState)\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <input type=\\\"checkbox\\\" class=\\\"js-\" +\n\"jobs-list-checkbox\\\" name=\\\"jobs[]\\\" value=\\\"\");\n\n\n            \n            #line 90 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                                                                                     Write(job.Key);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" />\\r\\n\");\n\n\n            \n            #line 91 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n            \n            #line 93 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td class=\\\"min-width\\\" rowspan=\\\"\");\n\n\n            \n            #line 94 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                               Write(job.Value != null && job.Value.InFailedState ? \"2\" : \"1\");\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                        \");\n\n\n            \n            #line 95 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                   Write(Html.JobIdLink(job.Key));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 96 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                         if (job.Value != null && !job.Value.InFailedState)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <span title=\\\"\");\n\n\n            \n            #line 98 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                    Write(Strings.Common_JobStateChanged_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"glyphicon glyphicon-question-sign\\\"></span>\\r\\n\");\n\n\n            \n            #line 99 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\");\n\n\n            \n            #line 101 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                     if (job.Value == null)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td colspan=\\\"2\\\">\\r\\n                       \" +\n\"                     <em>\");\n\n\n            \n            #line 104 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                           Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n                                        </td>\\r\\n\");\n\n\n            \n            #line 106 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                    }\n                                    else\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td>\\r\\n                                   \" +\n\"         <div class=\\\"word-break\\\">\\r\\n                                             \" +\n\"   \");\n\n\n            \n            #line 111 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                           Write(Html.JobNameLink(job.Key, job.Value.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                            </div>\\r\\n\");\n\n\n            \n            #line 113 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                             if (!String.IsNullOrEmpty(job.Value.ExceptionMessage) || !String.IsNullOrEmpty(job.Value.ExceptionDetails))\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <div class=\\\"text-muted\\\">\\r\\n       \" +\n\"                                             \");\n\n\n            \n            #line 116 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                               Write(job.Value.Reason);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" <a class=\\\"expander\\\" href=\\\"#\\\">\");\n\n\n            \n            #line 116 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                                                               Write(index == 0 ? Strings.Common_LessDetails : Strings.Common_MoreDetails);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</a>\\r\\n                                                </div>\\r\\n\");\n\n\n            \n            #line 118 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                        <td class=\\\"align-right\\\">\\r\\n\");\n\n\n            \n            #line 121 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                             if (job.Value.FailedAt.HasValue)\n                                            {\n                                                \n            \n            #line default\n            #line hidden\n            \n            #line 123 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                           Write(Html.RelativeTime(job.Value.FailedAt.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 123 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                                                            \n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n            \n            #line 126 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </tr>\\r\\n\");\n\n\n            \n            #line 128 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                if (job.Value != null && job.Value.InFailedState)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <tr>\\r\\n                                       \" +\n\" <td colspan=\\\"2\\\" class=\\\"failed-job-details\\\">\\r\\n\");\n\n\n            \n            #line 132 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                              \n                                                var displayCss = index++ == 0 ? \"display-block\" : null;\n                                                var serverId = job.Value.StateData != null && job.Value.StateData.ContainsKey(\"ServerId\") ? $\" ({Html.ServerId(job.Value.StateData[\"ServerId\"])})\" : null;\n                                            \n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <div class=\\\"expandable \");\n\n\n            \n            #line 136 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                              Write(displayCss);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                                <h4>\");\n\n\n            \n            #line 137 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                               Write(job.Value.ExceptionType);\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 137 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                                       Write(Html.Raw(serverId));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h4>\\r\\n                                                <p class=\\\"text-muted\\\">\\r\\n  \" +\n\"                                                  \");\n\n\n            \n            #line 139 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                               Write(job.Value.ExceptionMessage);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                                </p>\\r\\n\\r\\n\");\n\n\n            \n            #line 142 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                 if (!String.IsNullOrEmpty(job.Value.ExceptionDetails))\n                                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                    <pre class=\\\"stack-trace\\\"><cod\" +\n\"e>\");\n\n\n            \n            #line 144 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                                              Write(Html.StackTrace(job.Value.ExceptionDetails));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</code></pre>\\r\\n\");\n\n\n            \n            #line 145 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            </div>\\r\\n                             \" +\n\"           </td>\\r\\n                                    </tr>\\r\\n\");\n\n\n            \n            #line 149 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n                                }\n                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        </tbody>\\r\\n                    </table>\\r\\n                <\" +\n\"/div>\\r\\n\\r\\n                \");\n\n\n            \n            #line 155 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n           Write(Html.Paginator(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 157 \"..\\..\\Dashboard\\Pages\\FailedJobsPage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/FetchedJobsPage.cs",
    "content": "﻿namespace Hangfire.Dashboard.Pages\n{\n    partial class FetchedJobsPage\n    {\n        public FetchedJobsPage(string queue)\n        {\n            Queue = queue;\n        }\n\n        public string Queue { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/FetchedJobsPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using System.Collections\n@using System.Collections.Generic\n@using Hangfire\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Queue);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.FetchedCount(Queue));\n    var fetchedJobs = monitor.FetchedJobs(Queue, pager.FromRecord, pager.RecordsPerPage);\n}\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        @Html.JobsSidebar()\n    </div>\n    <div class=\"col-md-9\">\n        @Html.Breadcrumbs(Strings.FetchedJobsPage_Title, new Dictionary<string, string>\n        {\n            { \"Queues\", Url.LinkToQueues() },\n            { Queue, Url.Queue(Queue) }\n        })\n\n        <h1 id=\"page-title\" class=\"page-header\">\n            @Queue <small>@Strings.FetchedJobsPage_Title</small>\n        </h1>\n\n        @if (pager.TotalPageCount == 0)\n        {\n        <div class=\"alert alert-info\">\n            @Strings.FetchedJobsPage_NoJobs\n        </div>\n        }\n        else\n        {\n        <div class=\"js-jobs-list\">\n            <div class=\"btn-toolbar btn-toolbar-top\">\n                @if (!IsReadOnly)\n                {\n                    <button class=\"js-jobs-list-command btn btn-sm btn-primary\"\n                            data-url=\"@Url.To(\"/jobs/enqueued/requeue\")\"\n                            data-loading-text=\"@Strings.Common_Enqueueing\"\n                            disabled=\"disabled\">\n                        <span class=\"glyphicon glyphicon-repeat\"></span>\n                        @Strings.Common_RequeueJobs\n                    </button>\n                }\n                @if (!IsReadOnly)\n                {\n                    <button class=\"js-jobs-list-command btn btn-sm btn-default\"\n                            data-url=\"@Url.To(\"/jobs/enqueued/delete\")\"\n                            data-loading-text=\"@Strings.Common_Deleting\"\n                            data-confirm=\"@Strings.Common_DeleteConfirm\"\n                            disabled=\"disabled\">\n                        <span class=\"glyphicon glyphicon-remove\"></span>\n                        @Strings.Common_DeleteSelected\n                    </button>\n                }\n                @Html.PerPageSelector(pager)\n            </div>\n\n            <div class=\"table-responsive\">\n                <table class=\"table\" aria-describedby=\"page-title\">\n                    <thead>\n                        <tr>\n                            @if (!IsReadOnly)\n                            {\n                                <th class=\"min-width\">\n                                    <input type=\"checkbox\" class=\"js-jobs-list-select-all\"/>\n                                </th>\n                            }\n                            <th class=\"min-width\">@Strings.Common_Id</th>\n                            <th class=\"min-width\">@Strings.Common_State</th>\n                            <th>@Strings.Common_Job</th>\n                            <th class=\"align-right\">@Strings.Common_Fetched</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        @foreach (var job in fetchedJobs)\n                        {\n                            <tr class=\"js-jobs-list-row hover @(job.Value == null ? \"obsolete-data\" : null)\">\n                                @if (!IsReadOnly)\n                                {\n                                    <td>\n                                        @if (job.Value != null)\n                                        {\n                                            <input type=\"checkbox\" class=\"js-jobs-list-checkbox\" name=\"jobs[]\" value=\"@job.Key\"/>\n                                        }\n                                    </td>\n                                }\n                                <td class=\"min-width\">\n                                    @Html.JobIdLink(job.Key)\n                                </td>\n                                @if (job.Value == null)\n                                {\n                                    <td colspan=\"3\"><em>@Strings.Common_JobExpired</em></td>\n                                }\n                                else\n                                {\n                                    <td class=\"min-width\">\n                                        @Html.StateLabel(job.Value.State)\n                                    </td>\n                                    <td class=\"word-break\">\n                                        @Html.JobNameLink(job.Key, job.Value.Job)\n                                    </td>\n                                    <td class=\"align-right\">\n                                        @if (job.Value.FetchedAt.HasValue)\n                                        {\n                                            @Html.RelativeTime(job.Value.FetchedAt.Value)\n                                        }\n                                    </td>\n                                }\n                            </tr>\n                        }\n                    </tbody>\n                </table>\n            </div>\n\n            @Html.Paginator(pager)\n        </div>\n        }\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/FetchedJobsPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n    using System.Collections;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n    using System.Collections.Generic;\n    \n    #line default\n    #line hidden\n    using System.Linq;\n    using System.Text;\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 7 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class FetchedJobsPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n  \n    Layout = new LayoutPage(Queue);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.FetchedCount(Queue));\n    var fetchedJobs = monitor.FetchedJobs(Queue, pager.FromRecord, pager.RecordsPerPage);\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-3\\\">\\r\\n        \");\n\n\n            \n            #line 24 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n   Write(Html.JobsSidebar());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"col-md-9\\\">\\r\\n        \");\n\n\n            \n            #line 27 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n   Write(Html.Breadcrumbs(Strings.FetchedJobsPage_Title, new Dictionary<string, string>\n        {\n            { \"Queues\", Url.LinkToQueues() },\n            { Queue, Url.Queue(Queue) }\n        }));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\\r\\n        <h1 id=\\\"page-title\\\" class=\\\"page-header\\\">\\r\\n            \");\n\n\n            \n            #line 34 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n       Write(Queue);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" <small>\");\n\n\n            \n            #line 34 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                     Write(Strings.FetchedJobsPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</small>\\r\\n        </h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 37 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n         if (pager.TotalPageCount == 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"        <div class=\\\"alert alert-info\\\">\\r\\n            \");\n\n\n            \n            #line 40 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n       Write(Strings.FetchedJobsPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n        </div>\\r\\n\");\n\n\n            \n            #line 42 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n        }\n        else\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"        <div class=\\\"js-jobs-list\\\">\\r\\n            <div class=\\\"btn-toolbar btn-toolb\" +\n\"ar-top\\\">\\r\\n\");\n\n\n            \n            #line 47 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                 if (!IsReadOnly)\n                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    <button class=\\\"js-jobs-list-command btn btn-sm btn-primary\\\"\\r\\n\" +\n\"                            data-url=\\\"\");\n\n\n            \n            #line 50 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                 Write(Url.To(\"/jobs/enqueued/requeue\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                            data-loading-text=\\\"\");\n\n\n            \n            #line 51 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                          Write(Strings.Common_Enqueueing);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                            disabled=\\\"disabled\\\">\\r\\n                        <spa\" +\n\"n class=\\\"glyphicon glyphicon-repeat\\\"></span>\\r\\n                        \");\n\n\n            \n            #line 54 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                   Write(Strings.Common_RequeueJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                    </button>\\r\\n\");\n\n\n            \n            #line 56 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 57 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                 if (!IsReadOnly)\n                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    <button class=\\\"js-jobs-list-command btn btn-sm btn-default\\\"\\r\\n\" +\n\"                            data-url=\\\"\");\n\n\n            \n            #line 60 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                 Write(Url.To(\"/jobs/enqueued/delete\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                            data-loading-text=\\\"\");\n\n\n            \n            #line 61 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                          Write(Strings.Common_Deleting);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                            data-confirm=\\\"\");\n\n\n            \n            #line 62 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                     Write(Strings.Common_DeleteConfirm);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                            disabled=\\\"disabled\\\">\\r\\n                        <spa\" +\n\"n class=\\\"glyphicon glyphicon-remove\\\"></span>\\r\\n                        \");\n\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                   Write(Strings.Common_DeleteSelected);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                    </button>\\r\\n\");\n\n\n            \n            #line 67 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                \");\n\n\n            \n            #line 68 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n           Write(Html.PerPageSelector(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\\r\\n            <div class=\\\"table-responsive\\\">\\r\\n             \" +\n\"   <table class=\\\"table\\\" aria-describedby=\\\"page-title\\\">\\r\\n                    <the\" +\n\"ad>\\r\\n                        <tr>\\r\\n\");\n\n\n            \n            #line 75 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                             if (!IsReadOnly)\n                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th class=\\\"min-width\\\">\\r\\n                         \" +\n\"           <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-select-all\\\"/>\\r\\n           \" +\n\"                     </th>\\r\\n\");\n\n\n            \n            #line 80 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 81 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                             Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 82 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                             Write(Strings.Common_State);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th>\");\n\n\n            \n            #line 83 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                           Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 84 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                               Write(Strings.Common_Fetched);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                        </tr>\\r\\n                    </thead>\\r\\n             \" +\n\"       <tbody>\\r\\n\");\n\n\n            \n            #line 88 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                         foreach (var job in fetchedJobs)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <tr class=\\\"js-jobs-list-row hover \");\n\n\n            \n            #line 90 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                                          Write(job.Value == null ? \"obsolete-data\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 91 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                 if (!IsReadOnly)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td>\\r\\n\");\n\n\n            \n            #line 94 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                         if (job.Value != null)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <input type=\\\"checkbox\\\" class=\\\"js-jobs\" +\n\"-list-checkbox\\\" name=\\\"jobs[]\\\" value=\\\"\");\n\n\n            \n            #line 96 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                                                                                                 Write(job.Key);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"/>\\r\\n\");\n\n\n            \n            #line 97 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\");\n\n\n            \n            #line 99 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <td class=\\\"min-width\\\">\\r\\n                         \" +\n\"           \");\n\n\n            \n            #line 101 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                               Write(Html.JobIdLink(job.Key));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                </td>\\r\\n\");\n\n\n            \n            #line 103 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                 if (job.Value == null)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td colspan=\\\"3\\\"><em>\");\n\n\n            \n            #line 105 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                                   Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 106 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                }\n                                else\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   \");\n\n\n            \n            #line 110 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                   Write(Html.StateLabel(job.Value.State));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                    </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                    <td class=\\\"word-break\\\">\\r\\n                    \" +\n\"                    \");\n\n\n            \n            #line 113 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                   Write(Html.JobNameLink(job.Key, job.Value.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                    </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                    <td class=\\\"align-right\\\">\\r\\n\");\n\n\n            \n            #line 116 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                         if (job.Value.FetchedAt.HasValue)\n                                        {\n                                            \n            \n            #line default\n            #line hidden\n            \n            #line 118 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                       Write(Html.RelativeTime(job.Value.FetchedAt.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 118 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                                                                         \n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\");\n\n\n            \n            #line 121 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            </tr>\\r\\n\");\n\n\n            \n            #line 123 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    </tbody>\\r\\n                </table>\\r\\n            </div>\\r\\n\\r\\n   \" +\n\"         \");\n\n\n            \n            #line 128 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n       Write(Html.Paginator(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n        </div>\\r\\n\");\n\n\n            \n            #line 130 \"..\\..\\Dashboard\\Pages\\FetchedJobsPage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/HomePage.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Hangfire.Dashboard.Pages\n{\n    partial class HomePage\n    {\n        public static readonly List<DashboardMetric> Metrics = new List<DashboardMetric>(); \n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/HomePage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using System\n@using System.Collections.Generic\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@using Hangfire.Storage\n@using Newtonsoft.Json\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.HomePage_Title);\n    IDictionary<DateTime, long> succeeded = null;\n    IDictionary<DateTime, long> failed = null;\n    IDictionary<DateTime, long> deleted = null;\n\n    var period = Query(\"period\") ?? \"day\";\n\n    var monitor = Storage.GetMonitoringApi();\n    if (\"week\".Equals(period, StringComparison.OrdinalIgnoreCase))\n    {\n        succeeded = monitor.SucceededByDatesCount();\n        failed = monitor.FailedByDatesCount();\n        if (Storage.HasFeature(JobStorageFeatures.Monitoring.DeletedStateGraphs) &&\n            monitor is JobStorageMonitor jobStorageMonitor)\n        {\n            deleted = jobStorageMonitor.DeletedByDatesCount();\n        }\n    }\n    else if (\"day\".Equals(period, StringComparison.OrdinalIgnoreCase))\n    {\n        succeeded = monitor.HourlySucceededJobs();\n        failed = monitor.HourlyFailedJobs();\n        if (Storage.HasFeature(JobStorageFeatures.Monitoring.DeletedStateGraphs) &&\n            monitor is JobStorageMonitor jobStorageMonitor)\n        {\n            deleted = jobStorageMonitor.HourlyDeletedJobs();\n        }\n    }\n}\n\n<div class=\"row\">\n    <div class=\"col-md-12\">\n        <h1 class=\"page-header\">@Strings.HomePage_Title</h1>\n        @if (Metrics.Count > 0)\n        {\n            <div class=\"row\">\n                @foreach (var metric in Metrics)\n                {\n                    <div class=\"col-md-2\">\n                        @Html.BlockMetric(metric)\n                    </div>\n                }\n            </div>\n        }\n        <h3>@Strings.HomePage_RealtimeGraph</h3>\n        <canvas width=\"1140\" height=\"210\" id=\"realtimeGraph\" data-succeeded=\"@Statistics.Succeeded\" data-failed=\"@Statistics.Failed\" data-deleted=\"@Statistics.Deleted\"\n             data-succeeded-string=\"@Strings.HomePage_GraphHover_Succeeded\"\n             data-failed-string=\"@Strings.HomePage_GraphHover_Failed\"\n             data-deleted-string=\"@Strings.JobsSidebarMenu_Deleted\"></canvas>\n        <div class=\"display-none\">\n            <span data-metric=\"succeeded:count\"></span>\n            <span data-metric=\"failed:count\"></span>\n            <span data-metric=\"deleted:count\"></span>\n        </div>\n\n        <h3>\n            <div class=\"btn-group pull-right margin-top-2p\">\n                <a href=\"?period=day\" class=\"btn btn-sm btn-default @(\"day\".Equals(period, StringComparison.OrdinalIgnoreCase) ? \"active\" : null)\">@Strings.Common_PeriodDay</a>\n                <a href=\"?period=week\" class=\"btn btn-sm btn-default @(\"week\".Equals(period, StringComparison.OrdinalIgnoreCase) ? \"active\" : null)\">@Strings.Common_PeriodWeek</a>\n            </div>\n            @Strings.HomePage_HistoryGraph\n        </h3>\n\n        @if (succeeded != null && failed != null)\n        {\n            <canvas width=\"1140\" height=\"210\" id=\"historyGraph\"\n                 data-succeeded=\"@JsonConvert.SerializeObject(succeeded)\"\n                 data-failed=\"@JsonConvert.SerializeObject(failed)\"\n                 data-deleted=\"@(deleted != null ? JsonConvert.SerializeObject(deleted) : String.Empty)\"\n                 data-succeeded-string=\"@Strings.HomePage_GraphHover_Succeeded\"\n                 data-failed-string=\"@Strings.HomePage_GraphHover_Failed\"\n                 data-deleted-string=\"@Strings.JobsSidebarMenu_Deleted\"\n                 data-period=\"@period\">\n            </canvas>\n        }\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/HomePage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n    using System;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n    using System.Collections.Generic;\n    \n    #line default\n    #line hidden\n    using System.Linq;\n    using System.Text;\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    #line 7 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n    using Hangfire.Storage;\n    \n    #line default\n    #line hidden\n    \n    #line 8 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n    using Newtonsoft.Json;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class HomePage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n\n\n            \n            #line 10 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n  \n    Layout = new LayoutPage(Strings.HomePage_Title);\n    IDictionary<DateTime, long> succeeded = null;\n    IDictionary<DateTime, long> failed = null;\n    IDictionary<DateTime, long> deleted = null;\n\n    var period = Query(\"period\") ?? \"day\";\n\n    var monitor = Storage.GetMonitoringApi();\n    if (\"week\".Equals(period, StringComparison.OrdinalIgnoreCase))\n    {\n        succeeded = monitor.SucceededByDatesCount();\n        failed = monitor.FailedByDatesCount();\n        if (Storage.HasFeature(JobStorageFeatures.Monitoring.DeletedStateGraphs) &&\n            monitor is JobStorageMonitor jobStorageMonitor)\n        {\n            deleted = jobStorageMonitor.DeletedByDatesCount();\n        }\n    }\n    else if (\"day\".Equals(period, StringComparison.OrdinalIgnoreCase))\n    {\n        succeeded = monitor.HourlySucceededJobs();\n        failed = monitor.HourlyFailedJobs();\n        if (Storage.HasFeature(JobStorageFeatures.Monitoring.DeletedStateGraphs) &&\n            monitor is JobStorageMonitor jobStorageMonitor)\n        {\n            deleted = jobStorageMonitor.HourlyDeletedJobs();\n        }\n    }\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-12\\\">\\r\\n        <h1 class=\\\"page-header\\\"\" +\n\">\");\n\n\n            \n            #line 43 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                           Write(Strings.HomePage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\");\n\n\n            \n            #line 44 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n         if (Metrics.Count > 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"row\\\">\\r\\n\");\n\n\n            \n            #line 47 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                 foreach (var metric in Metrics)\n                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    <div class=\\\"col-md-2\\\">\\r\\n                        \");\n\n\n            \n            #line 50 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                   Write(Html.BlockMetric(metric));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                    </div>\\r\\n\");\n\n\n            \n            #line 52 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            </div>\\r\\n\");\n\n\n            \n            #line 54 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"        <h3>\");\n\n\n            \n            #line 55 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n       Write(Strings.HomePage_RealtimeGraph);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h3>\\r\\n        <canvas width=\\\"1140\\\" height=\\\"210\\\" id=\\\"realtimeGraph\\\" data-succeede\" +\n\"d=\\\"\");\n\n\n            \n            #line 56 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                                                                        Write(Statistics.Succeeded);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" data-failed=\\\"\");\n\n\n            \n            #line 56 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                                                                                                            Write(Statistics.Failed);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" data-deleted=\\\"\");\n\n\n            \n            #line 56 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                                                                                                                                              Write(Statistics.Deleted);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n             data-succeeded-string=\\\"\");\n\n\n            \n            #line 57 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                               Write(Strings.HomePage_GraphHover_Succeeded);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n             data-failed-string=\\\"\");\n\n\n            \n            #line 58 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                            Write(Strings.HomePage_GraphHover_Failed);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n             data-deleted-string=\\\"\");\n\n\n            \n            #line 59 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                             Write(Strings.JobsSidebarMenu_Deleted);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(@\"\"\"></canvas>\n        <div class=\"\"display-none\"\">\n            <span data-metric=\"\"succeeded:count\"\"></span>\n            <span data-metric=\"\"failed:count\"\"></span>\n            <span data-metric=\"\"deleted:count\"\"></span>\n        </div>\n\n        <h3>\n            <div class=\"\"btn-group pull-right margin-top-2p\"\">\n                <a href=\"\"?period=day\"\" class=\"\"btn btn-sm btn-default \");\n\n\n            \n            #line 68 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                                                                Write(\"day\".Equals(period, StringComparison.OrdinalIgnoreCase) ? \"active\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\");\n\n\n            \n            #line 68 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                                                                                                                                              Write(Strings.Common_PeriodDay);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</a>\\r\\n                <a href=\\\"?period=week\\\" class=\\\"btn btn-sm btn-default \");\n\n\n            \n            #line 69 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                                                                 Write(\"week\".Equals(period, StringComparison.OrdinalIgnoreCase) ? \"active\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\");\n\n\n            \n            #line 69 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                                                                                                                                                Write(Strings.Common_PeriodWeek);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</a>\\r\\n            </div>\\r\\n            \");\n\n\n            \n            #line 71 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n       Write(Strings.HomePage_HistoryGraph);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n        </h3>\\r\\n\\r\\n\");\n\n\n            \n            #line 74 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n         if (succeeded != null && failed != null)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <canvas width=\\\"1140\\\" height=\\\"210\\\" id=\\\"historyGraph\\\"\\r\\n                \" +\n\" data-succeeded=\\\"\");\n\n\n            \n            #line 77 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                            Write(JsonConvert.SerializeObject(succeeded));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                 data-failed=\\\"\");\n\n\n            \n            #line 78 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                         Write(JsonConvert.SerializeObject(failed));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                 data-deleted=\\\"\");\n\n\n            \n            #line 79 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                           Write(deleted != null ? JsonConvert.SerializeObject(deleted) : String.Empty);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                 data-succeeded-string=\\\"\");\n\n\n            \n            #line 80 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                                   Write(Strings.HomePage_GraphHover_Succeeded);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                 data-failed-string=\\\"\");\n\n\n            \n            #line 81 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                                Write(Strings.HomePage_GraphHover_Failed);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                 data-deleted-string=\\\"\");\n\n\n            \n            #line 82 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                                 Write(Strings.JobsSidebarMenu_Deleted);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                 data-period=\\\"\");\n\n\n            \n            #line 83 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n                         Write(period);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n            </canvas>\\r\\n\");\n\n\n            \n            #line 85 \"..\\..\\Dashboard\\Pages\\HomePage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/JobDetailsPage.cs",
    "content": "﻿using Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard.Pages\n{\n    partial class JobDetailsPage\n    {\n        public JobDetailsPage(string jobId)\n        {\n            JobId = jobId;\n        }\n\n        public string JobId { get; }\n\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/JobDetailsPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using System\n@using System.Collections.Generic\n@using System.Linq\n@using Hangfire\n@using Hangfire.Common\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@using Hangfire.States\n@using Hangfire.Storage\n@using Hangfire.Storage.Monitoring\n@inherits RazorPage\n@{\n    var monitor = Storage.GetMonitoringApi();\n    var job = monitor.JobDetails(JobId);\n    var dto = job != null ? new JobDetailsRendererDto(this, JobId, job) : null;\n    \n    string title = null;\n\n    if (job != null)\n    {\n        title = job.Job != null ? Html.JobName(job.Job) : null;\n\n        var historyList = new List<StateHistoryDto>(job.History);\n        historyList.Add(new StateHistoryDto { StateName = \"Created\", CreatedAt = job.CreatedAt ?? default(DateTime) });\n\n        job.History = historyList;\n    }\n\n    title = title ?? Strings.Common_Job;\n    Layout = new LayoutPage(title);\n}\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        @Html.JobsSidebar()\n    </div>\n    <div class=\"col-md-9\">\n        <h1 class=\"page-header\">@title</h1>\n\n        @if (job == null)\n        {\n            <div class=\"alert alert-warning\">\n                @String.Format(Strings.JobDetailsPage_JobExpired, JobId)\n            </div>\n        }\n        else\n        {\n            var currentState = job.History[0];\n            if (currentState.StateName == ProcessingState.StateName)\n            {\n                var server = monitor.Servers().FirstOrDefault(x => x.Name == currentState.Data[\"ServerId\"]);\n                if (server == null)\n                {\n                    <div class=\"alert alert-danger\">\n                        @Html.Raw(String.Format(Strings.JobDetailsPage_JobAbortedNotActive_Warning_Html, currentState.Data[\"ServerId\"], Url.To(\"/servers\")))\n                    </div>\n                }\n                else if (server.Heartbeat.HasValue && server.Heartbeat < (StorageUtcNow ?? ApplicationUtcNow).Add(DashboardOptions.ServerPossiblyAbortedThreshold.Negate()))\n                {\n                    <div class=\"alert alert-warning\">\n                        @Html.Raw(String.Format(Strings.JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html, server.Name))\n                    </div>\n                }\n            }\n\n            if (job.ExpireAt.HasValue)\n            {\n                <div class=\"alert alert-info\">\n                    @Html.Raw(String.Format(Strings.JobDetailsPage_JobFinished_Warning_Html, JobHelper.ToTimestamp(job.ExpireAt.Value), job.ExpireAt))\n                </div>\n            }\n\n            if (job.Job != null)\n            {\n                <div class=\"job-snippet\">\n                    <div class=\"job-snippet-code\">\n                        <pre><code><span class=\"comment\">// @Strings.JobDetailsPage_JobId: @Html.JobId(JobId.ToString(), false)</span>\n@if (job.Job.Queue != null)\n{\n<span class=\"comment\">// @Strings.QueuesPage_Table_Queue: @job.Job.Queue</span>\n}\n@JobMethodCallRenderer.Render(job.Job)\n</code></pre>\n                    </div>\n                </div>\n            }\n            else\n            {\n                var dbgParameters = job.Properties.Where(x => x.Key.StartsWith(\"DBG_\")).ToArray();\n\n                <div class=\"alert alert-warning\">\n                    <h4 id=\"job-details-missing-method\">@Strings.Common_CannotFindTargetMethod</h4>\n                    <table class=\"table table-condensed job-snippet-properties margin-bottom-0\" aria-describedby=\"job-details-missing-method\">\n                        <tr>\n                            <td>@Strings.JobDetailsPage_JobId</td>\n                            <td><pre><code>@Html.JobId(JobId, false)</code></pre></td>\n                        </tr>\n                        @foreach (var parameter in dbgParameters)\n                        {\n                            <tr>\n                                <td class=\"width-15\">@parameter.Key.Substring(4)</td>\n                                <td><pre><code>@parameter.Value</code></pre></td>\n                            </tr>\n                        }\n                    </table>\n                </div>\n            }\n\n            var displayParameters = job.Properties.Where(x => !x.Key.StartsWith(\"DBG_\") && x.Key != \"Continuations\").ToArray();\n\n            if (displayParameters.Length > 0)\n            {\n                <h3 id=\"job-details-parameters\">@Strings.JobDetailsPage_Parameters</h3>\n                <table class=\"table table-condensed job-snippet-properties\" aria-describedby=\"job-details-parameters\">\n                    @foreach (var parameter in displayParameters)\n                    {\n                        <tr>\n                            <td class=\"width-20 word-break\">@parameter.Key</td>\n                            <td><pre><code>@parameter.Value</code></pre></td>\n                        </tr>\n                    }\n                </table>\n            }\n\n            if (job.Properties.TryGetValue(\"Continuations\", out var serializedContinuations))\n            {\n                var continuations = ContinuationsSupportAttribute.DeserializeContinuations(serializedContinuations);\n\n                if (continuations.Count > 0)\n                {\n                    <h3 id=\"job-details-continuations\">@Strings.Common_Continuations</h3>\n                    <div class=\"table-responsive\">\n                        <table class=\"table\" aria-describedby=\"job-details-continuations\">\n                            <thead>\n                            <tr>\n                                <th class=\"min-width\">@Strings.Common_Id</th>\n                                <th class=\"min-width\">@Strings.Common_Condition</th>\n                                <th class=\"min-width\">@Strings.Common_State</th>\n                                <th>@Strings.Common_Job</th>\n                                <th class=\"align-right\">@Strings.Common_Created</th>\n                            </tr>\n                            </thead>\n                            <tbody>\n                            @foreach (var continuation in continuations)\n                            {\n                                JobData jobData;\n\n                                using (var connection = Storage.GetReadOnlyConnection())\n                                {\n                                    jobData = connection.GetJobData(continuation.JobId);\n                                }\n\n                                <tr>\n                                    @if (jobData == null)\n                                    {\n                                        <td colspan=\"5\"><em>@String.Format(Strings.JobDetailsPage_JobExpired, continuation.JobId)</em></td>\n                                    }\n                                    else\n                                    {\n                                        <td class=\"min-width\">@Html.JobIdLink(continuation.JobId)</td>\n                                        <td class=\"min-width\"><code>@continuation.Options.ToString(\"G\")</code></td>\n                                        <td class=\"min-width\">@Html.StateLabel(jobData.State)</td>\n                                        <td class=\"word-break\">@Html.JobNameLink(continuation.JobId, jobData.Job)</td>\n                                        <td class=\"align-right\">@Html.RelativeTime(jobData.CreatedAt)</td>\n                                    }\n                                </tr>\n                            }\n                            </tbody>\n                        </table>\n                    </div>\n                }\n            }\n\n            if (dto != null)\n            {\n                foreach (var renderer in JobDetailsRenderer.GetRenderers())\n                {\n                    try\n                    {\n                        @renderer.Item2(dto)\n                    }\n                    catch (Exception ex)\n                    {\n                        <h4 class=\"exception-type\">@ex.GetType().Name</h4>\n                        <p class=\"text-muted\">@ex.Message</p>\n                        <pre class=\"stack-trace\">@Html.StackTrace(ex.StackTrace)</pre>\n                    }\n                }\n            }\n\n            <h3>\n                @if (job.History.Count > 1)\n                {\n                    <span class=\"job-snippet-buttons pull-right\">\n                        @if (!IsReadOnly)\n                        {\n                            <button class=\"btn btn-sm btn-default\"\n                                    data-ajax=\"@Url.To(\"/jobs/actions/requeue/\" + JobId)\"\n                                    data-loading-text=\"@Strings.Common_Enqueueing\">\n                                @Strings.JobDetailsPage_Requeue\n                            </button>\n                        }\n                        @if (!IsReadOnly)\n                        {\n                            <button class=\"btn btn-sm btn-death\"\n                                    data-ajax=\"@Url.To(\"/jobs/actions/delete/\" + JobId)\"\n                                    data-loading-text=\"@Strings.Common_Deleting\"\n                                    data-confirm=\"@Strings.JobDetailsPage_DeleteConfirm\">\n                                @Strings.Common_Delete\n                            </button>\n                        }\n                    </span>\n                }\n\n                @Strings.JobDetailsPage_State\n            </h3>\n\n            var index = 0;\n\n            foreach (var entry in job.History)\n            {\n                var accentColor = JobHistoryRenderer.GetForegroundStateColor(entry.StateName);\n                var backgroundColor = JobHistoryRenderer.GetBackgroundStateColor(entry.StateName);\n                var cardCss = index == 0 ? JobHistoryRenderer.GetStateCssSuffix(entry.StateName) : null;\n                var cardStyle = index == 0 && cardCss == null ? $\"border-color: {accentColor}\" : null;\n                var cardTitleStyle = index == 0 && cardCss == null ? $\"color: {accentColor}\" : null;\n                var cardBackgroundStyle = index == 0 && cardCss == null ? $\"background-color: {backgroundColor}\" : null;\n\n                <div class=\"state-card @(cardCss != null ? \"state-card-state-\" + cardCss : null)\" style=\"@cardStyle\">\n                    <h4 class=\"state-card-title\" style=\"@cardTitleStyle\">\n                        <small class=\"pull-right text-muted\">\n                            @if (index == job.History.Count - 1)\n                            {\n                                @Html.RelativeTime(entry.CreatedAt)\n                            }\n                            else\n                            {\n                                var duration = Html.ToHumanDuration(entry.CreatedAt - job.History[index + 1].CreatedAt);\n\n                                if (index == 0)\n                                {\n                                    @: @Html.RelativeTime(entry.CreatedAt) (@duration)\n                                }\n                                else\n                                {\n                                    @: @Html.MomentTitle(entry.CreatedAt, duration)\n                                }\n                            }\n                        </small>\n\n                        @entry.StateName\n                    </h4>\n\n                    @if (!String.IsNullOrWhiteSpace(entry.Reason))\n                    {\n                        <p class=\"state-card-text text-muted\">@entry.Reason</p>\n                    }\n\n                    @{\n                        var rendered = Html.RenderHistory(entry.StateName, entry.Data);\n                    }\n\n                    @if (rendered != null)\n                    {\n                        <div class=\"state-card-body\" style=\"@cardBackgroundStyle\">\n                            @rendered\n                        </div>\n                    }\n                </div>\n\n                index++;\n            }\n        }\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/JobDetailsPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using System;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using System.Collections.Generic;\n    \n    #line default\n    #line hidden\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using System.Linq;\n    \n    #line default\n    #line hidden\n    using System.Text;\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using Hangfire.Common;\n    \n    #line default\n    #line hidden\n    \n    #line 7 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 8 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 9 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    #line 10 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using Hangfire.States;\n    \n    #line default\n    #line hidden\n    \n    #line 11 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using Hangfire.Storage;\n    \n    #line default\n    #line hidden\n    \n    #line 12 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n    using Hangfire.Storage.Monitoring;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class JobDetailsPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n            \n            #line 14 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n  \n    var monitor = Storage.GetMonitoringApi();\n    var job = monitor.JobDetails(JobId);\n    var dto = job != null ? new JobDetailsRendererDto(this, JobId, job) : null;\n    \n    string title = null;\n\n    if (job != null)\n    {\n        title = job.Job != null ? Html.JobName(job.Job) : null;\n\n        var historyList = new List<StateHistoryDto>(job.History);\n        historyList.Add(new StateHistoryDto { StateName = \"Created\", CreatedAt = job.CreatedAt ?? default(DateTime) });\n\n        job.History = historyList;\n    }\n\n    title = title ?? Strings.Common_Job;\n    Layout = new LayoutPage(title);\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-3\\\">\\r\\n        \");\n\n\n            \n            #line 37 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n   Write(Html.JobsSidebar());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"col-md-9\\\">\\r\\n        <h1 class=\\\"page-header\\\">\");\n\n\n            \n            #line 40 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                           Write(title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 42 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n         if (job == null)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-warning\\\">\\r\\n                \");\n\n\n            \n            #line 45 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n           Write(String.Format(Strings.JobDetailsPage_JobExpired, JobId));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 47 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n        }\n        else\n        {\n            var currentState = job.History[0];\n            if (currentState.StateName == ProcessingState.StateName)\n            {\n                var server = monitor.Servers().FirstOrDefault(x => x.Name == currentState.Data[\"ServerId\"]);\n                if (server == null)\n                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    <div class=\\\"alert alert-danger\\\">\\r\\n                        \");\n\n\n            \n            #line 57 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                   Write(Html.Raw(String.Format(Strings.JobDetailsPage_JobAbortedNotActive_Warning_Html, currentState.Data[\"ServerId\"], Url.To(\"/servers\"))));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                    </div>\\r\\n\");\n\n\n            \n            #line 59 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                }\n                else if (server.Heartbeat.HasValue && server.Heartbeat < (StorageUtcNow ?? ApplicationUtcNow).Add(DashboardOptions.ServerPossiblyAbortedThreshold.Negate()))\n                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    <div class=\\\"alert alert-warning\\\">\\r\\n                        \");\n\n\n            \n            #line 63 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                   Write(Html.Raw(String.Format(Strings.JobDetailsPage_JobAbortedWithHeartbeat_Warning_Html, server.Name)));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                    </div>\\r\\n\");\n\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                }\n            }\n\n            if (job.ExpireAt.HasValue)\n            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                <div class=\\\"alert alert-info\\\">\\r\\n                    \");\n\n\n            \n            #line 71 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n               Write(Html.Raw(String.Format(Strings.JobDetailsPage_JobFinished_Warning_Html, JobHelper.ToTimestamp(job.ExpireAt.Value), job.ExpireAt)));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n\");\n\n\n            \n            #line 73 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n            }\n\n            if (job.Job != null)\n            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                <div class=\\\"job-snippet\\\">\\r\\n                    <div class=\\\"job-sn\" +\n\"ippet-code\\\">\\r\\n                        <pre><code><span class=\\\"comment\\\">// \");\n\n\n            \n            #line 79 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                       Write(Strings.JobDetailsPage_JobId);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\": \");\n\n\n            \n            #line 79 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                                                      Write(Html.JobId(JobId.ToString(), false));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</span>\\r\\n\");\n\n\n            \n            #line 80 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n if (job.Job.Queue != null)\n{\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"<span class=\\\"comment\\\">// \");\n\n\n            \n            #line 82 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                    Write(Strings.QueuesPage_Table_Queue);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\": \");\n\n\n            \n            #line 82 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                     Write(job.Job.Queue);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</span>\\r\\n\");\n\n\n            \n            #line 83 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n}\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 84 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\nWrite(JobMethodCallRenderer.Render(job.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n</code></pre>\\r\\n                    </div>\\r\\n                </div>\\r\\n\");\n\n\n            \n            #line 88 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n            }\n            else\n            {\n                var dbgParameters = job.Properties.Where(x => x.Key.StartsWith(\"DBG_\")).ToArray();\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                <div class=\\\"alert alert-warning\\\">\\r\\n                    <h4 id=\\\"jo\" +\n\"b-details-missing-method\\\">\");\n\n\n            \n            #line 94 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                   Write(Strings.Common_CannotFindTargetMethod);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h4>\\r\\n                    <table class=\\\"table table-condensed job-snippet-proper\" +\n\"ties margin-bottom-0\\\" aria-describedby=\\\"job-details-missing-method\\\">\\r\\n          \" +\n\"              <tr>\\r\\n                            <td>\");\n\n\n            \n            #line 97 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                           Write(Strings.JobDetailsPage_JobId);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n                            <td><pre><code>\");\n\n\n            \n            #line 98 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                      Write(Html.JobId(JobId, false));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</code></pre></td>\\r\\n                        </tr>\\r\\n\");\n\n\n            \n            #line 100 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                         foreach (var parameter in dbgParameters)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <tr>\\r\\n                                <td class=\\\"widt\" +\n\"h-15\\\">\");\n\n\n            \n            #line 103 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                Write(parameter.Key.Substring(4));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n                                <td><pre><code>\");\n\n\n            \n            #line 104 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                          Write(parameter.Value);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</code></pre></td>\\r\\n                            </tr>\\r\\n\");\n\n\n            \n            #line 106 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    </table>\\r\\n                </div>\\r\\n\");\n\n\n            \n            #line 109 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n            }\n\n            var displayParameters = job.Properties.Where(x => !x.Key.StartsWith(\"DBG_\") && x.Key != \"Continuations\").ToArray();\n\n            if (displayParameters.Length > 0)\n            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                <h3 id=\\\"job-details-parameters\\\">\");\n\n\n            \n            #line 115 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                           Write(Strings.JobDetailsPage_Parameters);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h3>\\r\\n\");\n\n\n\nWriteLiteral(\"                <table class=\\\"table table-condensed job-snippet-properties\\\" aria-\" +\n\"describedby=\\\"job-details-parameters\\\">\\r\\n\");\n\n\n            \n            #line 117 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                     foreach (var parameter in displayParameters)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <tr>\\r\\n                            <td class=\\\"width-20 wor\" +\n\"d-break\\\">\");\n\n\n            \n            #line 120 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                       Write(parameter.Key);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n                            <td><pre><code>\");\n\n\n            \n            #line 121 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                      Write(parameter.Value);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</code></pre></td>\\r\\n                        </tr>\\r\\n\");\n\n\n            \n            #line 123 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                </table>\\r\\n\");\n\n\n            \n            #line 125 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n            }\n\n            if (job.Properties.TryGetValue(\"Continuations\", out var serializedContinuations))\n            {\n                var continuations = ContinuationsSupportAttribute.DeserializeContinuations(serializedContinuations);\n\n                if (continuations.Count > 0)\n                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    <h3 id=\\\"job-details-continuations\\\">\");\n\n\n            \n            #line 133 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                  Write(Strings.Common_Continuations);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h3>\\r\\n\");\n\n\n\nWriteLiteral(@\"                    <div class=\"\"table-responsive\"\">\n                        <table class=\"\"table\"\" aria-describedby=\"\"job-details-continuations\"\">\n                            <thead>\n                            <tr>\n                                <th class=\"\"min-width\"\">\");\n\n\n            \n            #line 138 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                 Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 139 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                 Write(Strings.Common_Condition);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 140 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                 Write(Strings.Common_State);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th>\");\n\n\n            \n            #line 141 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                               Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 142 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                   Write(Strings.Common_Created);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            </tr>\\r\\n                            </thead>\\r\\n \" +\n\"                           <tbody>\\r\\n\");\n\n\n            \n            #line 146 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                             foreach (var continuation in continuations)\n                            {\n                                JobData jobData;\n\n                                using (var connection = Storage.GetReadOnlyConnection())\n                                {\n                                    jobData = connection.GetJobData(continuation.JobId);\n                                }\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <tr>\\r\\n\");\n\n\n            \n            #line 156 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                     if (jobData == null)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td colspan=\\\"5\\\"><em>\");\n\n\n            \n            #line 158 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                       Write(String.Format(Strings.JobDetailsPage_JobExpired, continuation.JobId));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 159 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                    }\n                                    else\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td class=\\\"min-width\\\">\");\n\n\n            \n            #line 162 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                         Write(Html.JobIdLink(continuation.JobId));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                        <td class=\\\"min-width\\\"><code>\");\n\n\n            \n            #line 163 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                               Write(continuation.Options.ToString(\"G\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</code></td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                        <td class=\\\"min-width\\\">\");\n\n\n            \n            #line 164 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                         Write(Html.StateLabel(jobData.State));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                        <td class=\\\"word-break\\\">\");\n\n\n            \n            #line 165 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                          Write(Html.JobNameLink(continuation.JobId, jobData.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                        <td class=\\\"align-right\\\">\");\n\n\n            \n            #line 166 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                           Write(Html.RelativeTime(jobData.CreatedAt));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n\");\n\n\n            \n            #line 167 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </tr>\\r\\n\");\n\n\n            \n            #line 169 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            </tbody>\\r\\n                        </table>\\r\\n         \" +\n\"           </div>\\r\\n\");\n\n\n            \n            #line 173 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                }\n            }\n\n            if (dto != null)\n            {\n                foreach (var renderer in JobDetailsRenderer.GetRenderers())\n                {\n                    try\n                    {\n                        \n            \n            #line default\n            #line hidden\n            \n            #line 182 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                   Write(renderer.Item2(dto));\n\n            \n            #line default\n            #line hidden\n            \n            #line 182 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                            \n                    }\n                    catch (Exception ex)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <h4 class=\\\"exception-type\\\">\");\n\n\n            \n            #line 186 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                              Write(ex.GetType().Name);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h4>\\r\\n\");\n\n\n\nWriteLiteral(\"                        <p class=\\\"text-muted\\\">\");\n\n\n            \n            #line 187 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                         Write(ex.Message);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</p>\\r\\n\");\n\n\n\nWriteLiteral(\"                        <pre class=\\\"stack-trace\\\">\");\n\n\n            \n            #line 188 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                            Write(Html.StackTrace(ex.StackTrace));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</pre>\\r\\n\");\n\n\n            \n            #line 189 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                    }\n                }\n            }\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <h3>\\r\\n\");\n\n\n            \n            #line 194 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                 if (job.History.Count > 1)\n                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    <span class=\\\"job-snippet-buttons pull-right\\\">\\r\\n\");\n\n\n            \n            #line 197 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                         if (!IsReadOnly)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <button class=\\\"btn btn-sm btn-default\\\"\\r\\n             \" +\n\"                       data-ajax=\\\"\");\n\n\n            \n            #line 200 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                          Write(Url.To(\"/jobs/actions/requeue/\" + JobId));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                    data-loading-text=\\\"\");\n\n\n            \n            #line 201 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                  Write(Strings.Common_Enqueueing);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                \");\n\n\n            \n            #line 202 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                           Write(Strings.JobDetailsPage_Requeue);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                            </button>\\r\\n\");\n\n\n            \n            #line 204 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 205 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                         if (!IsReadOnly)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <button class=\\\"btn btn-sm btn-death\\\"\\r\\n               \" +\n\"                     data-ajax=\\\"\");\n\n\n            \n            #line 208 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                          Write(Url.To(\"/jobs/actions/delete/\" + JobId));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                    data-loading-text=\\\"\");\n\n\n            \n            #line 209 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                  Write(Strings.Common_Deleting);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                    data-confirm=\\\"\");\n\n\n            \n            #line 210 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                             Write(Strings.JobDetailsPage_DeleteConfirm);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                \");\n\n\n            \n            #line 211 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                           Write(Strings.Common_Delete);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                            </button>\\r\\n\");\n\n\n            \n            #line 213 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    </span>\\r\\n\");\n\n\n            \n            #line 215 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                \");\n\n\n            \n            #line 217 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n           Write(Strings.JobDetailsPage_State);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </h3>\\r\\n\");\n\n\n            \n            #line 219 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n\n            var index = 0;\n\n            foreach (var entry in job.History)\n            {\n                var accentColor = JobHistoryRenderer.GetForegroundStateColor(entry.StateName);\n                var backgroundColor = JobHistoryRenderer.GetBackgroundStateColor(entry.StateName);\n                var cardCss = index == 0 ? JobHistoryRenderer.GetStateCssSuffix(entry.StateName) : null;\n                var cardStyle = index == 0 && cardCss == null ? $\"border-color: {accentColor}\" : null;\n                var cardTitleStyle = index == 0 && cardCss == null ? $\"color: {accentColor}\" : null;\n                var cardBackgroundStyle = index == 0 && cardCss == null ? $\"background-color: {backgroundColor}\" : null;\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                <div class=\\\"state-card \");\n\n\n            \n            #line 231 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                   Write(cardCss != null ? \"state-card-state-\" + cardCss : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" style=\\\"\");\n\n\n            \n            #line 231 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                                                                    Write(cardStyle);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                    <h4 class=\\\"state-card-title\\\" style=\\\"\");\n\n\n            \n            #line 232 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                   Write(cardTitleStyle);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                        <small class=\\\"pull-right text-muted\\\">\\r\\n\");\n\n\n            \n            #line 234 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                             if (index == job.History.Count - 1)\n                            {\n                                \n            \n            #line default\n            #line hidden\n            \n            #line 236 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                           Write(Html.RelativeTime(entry.CreatedAt));\n\n            \n            #line default\n            #line hidden\n            \n            #line 236 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                                   \n                            }\n                            else\n                            {\n                                var duration = Html.ToHumanDuration(entry.CreatedAt - job.History[index + 1].CreatedAt);\n\n                                if (index == 0)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    \");\n\nWriteLiteral(\" \");\n\n\n            \n            #line 244 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                  Write(Html.RelativeTime(entry.CreatedAt));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" (\");\n\n\n            \n            #line 244 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                                       Write(duration);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\")\\r\\n\");\n\n\n            \n            #line 245 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                }\n                                else\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    \");\n\nWriteLiteral(\" \");\n\n\n            \n            #line 248 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                  Write(Html.MomentTitle(entry.CreatedAt, duration));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 249 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                }\n                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        </small>\\r\\n\\r\\n                        \");\n\n\n            \n            #line 253 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                   Write(entry.StateName);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                    </h4>\\r\\n\\r\\n\");\n\n\n            \n            #line 256 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                     if (!String.IsNullOrWhiteSpace(entry.Reason))\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <p class=\\\"state-card-text text-muted\\\">\");\n\n\n            \n            #line 258 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                         Write(entry.Reason);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</p>\\r\\n\");\n\n\n            \n            #line 259 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 261 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                      \n                        var rendered = Html.RenderHistory(entry.StateName, entry.Data);\n                    \n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 265 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                     if (rendered != null)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <div class=\\\"state-card-body\\\" style=\\\"\");\n\n\n            \n            #line 267 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                                                       Write(cardBackgroundStyle);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                            \");\n\n\n            \n            #line 268 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                       Write(rendered);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </div>\\r\\n\");\n\n\n            \n            #line 270 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                </div>\\r\\n\");\n\n\n            \n            #line 272 \"..\\..\\Dashboard\\Pages\\JobDetailsPage.cshtml\"\n\n                index++;\n            }\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/LayoutPage.cs",
    "content": "﻿namespace Hangfire.Dashboard.Pages\n{\n    partial class LayoutPage\n    {\n        public LayoutPage(string title)\n        {\n            Title = title;\n        }\n\n        public string Title { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/LayoutPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Public GeneratePrettyNames: True *@\n@using System\n@using System.Globalization\n@using System.Reflection\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n<!DOCTYPE html>\n<html lang=\"@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName\">\n<head>\n    <title>@Title – @(DashboardOptions.DashboardTitle.Contains(\"<\") ? \"Hangfire Dashboard\" : DashboardOptions.DashboardTitle)</title>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <meta name=\"robots\" content=\"none\">\n    @if (!DashboardOptions.IgnoreAntiforgeryToken)\n    {\n        if (!String.IsNullOrWhiteSpace(Context.AntiforgeryHeader))\n        {\n            @: <meta name=\"csrf-header\" content=\"@Context.AntiforgeryHeader\">\n        }\n        if (!String.IsNullOrWhiteSpace(Context.AntiforgeryToken))\n        {\n            @: <meta name=\"csrf-token\" content=\"@Context.AntiforgeryToken\">\n        }\n    }\n    @{ \n        var version = GetType().GetTypeInfo().Assembly.GetName().Version;\n        var storageVersion = Storage.GetType().GetTypeInfo().Assembly.GetName().Version;\n    }\n\n    @if(!string.IsNullOrWhiteSpace(DashboardOptions.FaviconPath))\n    {\n        @: <link rel=\"shortcut icon\" href=\"@DashboardOptions.FaviconPath\" type=\"image/x-icon\">\n    }\n    else\n    {\n        @: <link rel=\"shortcut icon\" href=\"data:image/x-icon;,\" type=\"image/x-icon\">\n    }\n    \n    <link rel=\"stylesheet\" href=\"@Url.To($\"/css{version.Major}{version.Minor}{version.Build}0{Math.Abs(DashboardRoutes.StylesheetsHashCode)}\")\">\n    @if (DashboardOptions.DarkModeEnabled)\n    {\n        @: <link rel=\"stylesheet\" href=\"@Url.To($\"/css-dark{version.Major}{version.Minor}{version.Build}0{Math.Abs(DashboardRoutes.StylesheetsDarkModeHashCode)}\")\">\n    }\n</head>\n    <body>\n        <!-- Wrap all page content here -->\n        <div id=\"wrap\">\n\n            <!-- Fixed navbar -->\n            <div class=\"navbar navbar-default navbar-fixed-top\">\n                <div class=\"container\">\n                    <div class=\"navbar-header\">\n                        <button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\" data-target=\".navbar-collapse\">\n                            <span class=\"icon-bar\"></span>\n                            <span class=\"icon-bar\"></span>\n                            <span class=\"icon-bar\"></span>\n                        </button>\n                        <a class=\"navbar-brand\" href=\"@Url.Home()\">@Html.Raw(DashboardOptions.DashboardTitle)</a>\n                    </div>\n                    <div class=\"collapse navbar-collapse\">\n                        @Html.RenderPartial(new Navigation())\n                        @if(@AppPath != null) {\n                            <ul class=\"nav navbar-nav navbar-right\">\n                                <li>\n                                    <a href=\"@AppPath\">\n                                        <span class=\"glyphicon glyphicon-log-out\"></span>\n                                        <span class=\"hidden-sm\">\n                                            @Strings.LayoutPage_Back\n                                        </span>\n                                    </a>\n                                </li>\n                            </ul>\n                        }\n                    </div>\n                    <!--/.nav-collapse -->\n                </div>\n                <!-- Error alert when polling fails -->\n                @Html.RenderPartial(new ErrorAlert())\n            </div>\n\n            <!-- Begin page content -->\n            <div class=\"container js-page-container margin-bottom-20p\">\n                @RenderBody()\n            </div>\n        </div>\n\n        <div id=\"footer\">\n            <div class=\"container\">\n                <ul class=\"list-inline credit\">\n                    <li>\n                        <a href=\"https://www.hangfire.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Hangfire @($\"{version.Major}.{version.Minor}.{version.Build}\")\n                        </a>\n                    </li>\n                    @if(DashboardOptions.DisplayStorageConnectionString){\n                    <li>@Storage @($\"{storageVersion.Major}.{storageVersion.Minor}.{storageVersion.Build}\")</li>\n                    }\n                    @if (StorageUtcNow.HasValue)\n                    {\n                        <li>@Strings.LayoutPage_Footer_StorageTime @Html.LocalTime(StorageUtcNow.Value)</li>\n                    }\n                    <li>\n                        @if (TimeDifference.HasValue && Math.Abs(TimeDifference.Value.TotalSeconds) > 30)\n                        {\n                            <span class=\"text-warning\" title=\"@Strings.LayoutPage_Footer_TimeIsOutOfSync\">\n                                <span class=\"glyphicon glyphicon-warning-sign\"></span>&nbsp;@Strings.LayoutPage_Footer_Time @Html.LocalTime(ApplicationUtcNow)\n                            </span>\n                        }\n                        else\n                        {\n                            <span>@Strings.LayoutPage_Footer_Time @Html.LocalTime(ApplicationUtcNow)</span>\n                        }\n                    </li>\n                    <li>@String.Format(Strings.LayoutPage_Footer_Generatedms, GenerationTime.Elapsed.TotalMilliseconds.ToString(\"N\"))</li>\n                </ul>\n            </div>\n        </div>\n        \n        <div id=\"hangfireConfig\"\n             data-pollinterval=\"@DashboardOptions.StatsPollingInterval\"\n             data-pollurl=\"@(Url.To(\"/stats\"))\"\n             data-darkmode=\"@(DashboardOptions.DarkModeEnabled.ToString().ToLowerInvariant())\">\n        </div>\n\n        <script src=\"@Url.To($\"/js{version.Major}{version.Minor}{version.Build}0{Math.Abs(DashboardRoutes.JavaScriptsHashCode)}\")\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/LayoutPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n    using System;\n    \n    #line default\n    #line hidden\n    using System.Collections.Generic;\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n    using System.Globalization;\n    \n    #line default\n    #line hidden\n    using System.Linq;\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n    using System.Reflection;\n    \n    #line default\n    #line hidden\n    using System.Text;\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 7 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    public partial class LayoutPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\nWriteLiteral(\"<!DOCTYPE html>\\r\\n<html lang=\\\"\");\n\n\n            \n            #line 10 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n       Write(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n<head>\\r\\n    <title>\");\n\n\n            \n            #line 12 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n      Write(Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" – \");\n\n\n            \n            #line 12 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                Write(DashboardOptions.DashboardTitle.Contains(\"<\") ? \"Hangfire Dashboard\" : DashboardOptions.DashboardTitle);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</title>\\r\\n    <meta http-equiv=\\\"X-UA-Compatible\\\" content=\\\"IE=edge\\\">\\r\\n    <meta ch\" +\n\"arset=\\\"utf-8\\\">\\r\\n    <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-s\" +\n\"cale=1.0\\\">\\r\\n    <meta name=\\\"robots\\\" content=\\\"none\\\">\\r\\n\");\n\n\n            \n            #line 17 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n     if (!DashboardOptions.IgnoreAntiforgeryToken)\n    {\n        if (!String.IsNullOrWhiteSpace(Context.AntiforgeryHeader))\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            \");\n\nWriteLiteral(\" <meta name=\\\"csrf-header\\\" content=\\\"\");\n\n\n            \n            #line 21 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                            Write(Context.AntiforgeryHeader);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 22 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n        }\n        if (!String.IsNullOrWhiteSpace(Context.AntiforgeryToken))\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            \");\n\nWriteLiteral(\" <meta name=\\\"csrf-token\\\" content=\\\"\");\n\n\n            \n            #line 25 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                           Write(Context.AntiforgeryToken);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 26 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n        }\n    }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 28 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n       \n        var version = GetType().GetTypeInfo().Assembly.GetName().Version;\n        var storageVersion = Storage.GetType().GetTypeInfo().Assembly.GetName().Version;\n    \n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 33 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n     if(!string.IsNullOrWhiteSpace(DashboardOptions.FaviconPath))\n    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"        \");\n\nWriteLiteral(\" <link rel=\\\"shortcut icon\\\" href=\\\"\");\n\n\n            \n            #line 35 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                      Write(DashboardOptions.FaviconPath);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" type=\\\"image/x-icon\\\">\\r\\n\");\n\n\n            \n            #line 36 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n    }\n    else\n    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"        \");\n\nWriteLiteral(\" <link rel=\\\"shortcut icon\\\" href=\\\"data:image/x-icon;,\\\" type=\\\"image/x-icon\\\">\\r\\n\");\n\n\n            \n            #line 40 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    \\r\\n    <link rel=\\\"stylesheet\\\" href=\\\"\");\n\n\n            \n            #line 42 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                            Write(Url.To($\"/css{version.Major}{version.Minor}{version.Build}0{Math.Abs(DashboardRoutes.StylesheetsHashCode)}\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 43 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n     if (DashboardOptions.DarkModeEnabled)\n    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"        \");\n\nWriteLiteral(\" <link rel=\\\"stylesheet\\\" href=\\\"\");\n\n\n            \n            #line 45 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                   Write(Url.To($\"/css-dark{version.Major}{version.Minor}{version.Build}0{Math.Abs(DashboardRoutes.StylesheetsDarkModeHashCode)}\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 46 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(@\"</head>\n    <body>\n        <!-- Wrap all page content here -->\n        <div id=\"\"wrap\"\">\n\n            <!-- Fixed navbar -->\n            <div class=\"\"navbar navbar-default navbar-fixed-top\"\">\n                <div class=\"\"container\"\">\n                    <div class=\"\"navbar-header\"\">\n                        <button type=\"\"button\"\" class=\"\"navbar-toggle\"\" data-toggle=\"\"collapse\"\" data-target=\"\".navbar-collapse\"\">\n                            <span class=\"\"icon-bar\"\"></span>\n                            <span class=\"\"icon-bar\"\"></span>\n                            <span class=\"\"icon-bar\"\"></span>\n                        </button>\n                        <a class=\"\"navbar-brand\"\" href=\"\"\");\n\n\n            \n            #line 61 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                                 Write(Url.Home());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\");\n\n\n            \n            #line 61 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                                              Write(Html.Raw(DashboardOptions.DashboardTitle));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</a>\\r\\n                    </div>\\r\\n                    <div class=\\\"collapse navbar\" +\n\"-collapse\\\">\\r\\n                        \");\n\n\n            \n            #line 64 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                   Write(Html.RenderPartial(new Navigation()));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                         if(@AppPath != null) {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <ul class=\\\"nav navbar-nav navbar-right\\\">\\r\\n           \" +\n\"                     <li>\\r\\n                                    <a href=\\\"\");\n\n\n            \n            #line 68 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                        Write(AppPath);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                        <span class=\\\"glyphicon glyphicon-log-\" +\n\"out\\\"></span>\\r\\n                                        <span class=\\\"hidden-sm\\\">\\r\\n\" +\n\"                                            \");\n\n\n            \n            #line 71 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                       Write(Strings.LayoutPage_Back);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                        </span>\\r\\n                              \" +\n\"      </a>\\r\\n                                </li>\\r\\n                            <\" +\n\"/ul>\\r\\n\");\n\n\n            \n            #line 76 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    </div>\\r\\n                    <!--/.nav-collapse -->\\r\\n         \" +\n\"       </div>\\r\\n                <!-- Error alert when polling fails -->\\r\\n        \" +\n\"        \");\n\n\n            \n            #line 81 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n           Write(Html.RenderPartial(new ErrorAlert()));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\\r\\n            <!-- Begin page content -->\\r\\n            <div\" +\n\" class=\\\"container js-page-container margin-bottom-20p\\\">\\r\\n                \");\n\n\n            \n            #line 86 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n           Write(RenderBody());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(@\"\n            </div>\n        </div>\n\n        <div id=\"\"footer\"\">\n            <div class=\"\"container\"\">\n                <ul class=\"\"list-inline credit\"\">\n                    <li>\n                        <a href=\"\"https://www.hangfire.io/\"\" target=\"\"_blank\"\" rel=\"\"noopener noreferrer\"\">Hangfire \");\n\n\n            \n            #line 94 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                                                                                          Write($\"{version.Major}.{version.Minor}.{version.Build}\");\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </a>\\r\\n                    </li>\\r\\n\");\n\n\n            \n            #line 97 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                     if(DashboardOptions.DisplayStorageConnectionString){\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    <li>\");\n\n\n            \n            #line 98 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                   Write(Storage);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 98 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                             Write($\"{storageVersion.Major}.{storageVersion.Minor}.{storageVersion.Build}\");\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</li>\\r\\n\");\n\n\n            \n            #line 99 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 100 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                     if (StorageUtcNow.HasValue)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <li>\");\n\n\n            \n            #line 102 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                       Write(Strings.LayoutPage_Footer_StorageTime);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 102 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                                              Write(Html.LocalTime(StorageUtcNow.Value));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</li>\\r\\n\");\n\n\n            \n            #line 103 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    <li>\\r\\n\");\n\n\n            \n            #line 105 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                         if (TimeDifference.HasValue && Math.Abs(TimeDifference.Value.TotalSeconds) > 30)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <span class=\\\"text-warning\\\" title=\\\"\");\n\n\n            \n            #line 107 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                                         Write(Strings.LayoutPage_Footer_TimeIsOutOfSync);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                <span class=\\\"glyphicon glyphicon-warning-sign\" +\n\"\\\"></span>&nbsp;\");\n\n\n            \n            #line 108 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                                                                       Write(Strings.LayoutPage_Footer_Time);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 108 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                                                                                                       Write(Html.LocalTime(ApplicationUtcNow));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                            </span>\\r\\n\");\n\n\n            \n            #line 110 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                        }\n                        else\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <span>\");\n\n\n            \n            #line 113 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                             Write(Strings.LayoutPage_Footer_Time);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 113 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                                                             Write(Html.LocalTime(ApplicationUtcNow));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</span>\\r\\n\");\n\n\n            \n            #line 114 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    </li>\\r\\n                    <li>\");\n\n\n            \n            #line 116 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                   Write(String.Format(Strings.LayoutPage_Footer_Generatedms, GenerationTime.Elapsed.TotalMilliseconds.ToString(\"N\")));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</li>\\r\\n                </ul>\\r\\n            </div>\\r\\n        </div>\\r\\n        \\r\\n     \" +\n\"   <div id=\\\"hangfireConfig\\\"\\r\\n             data-pollinterval=\\\"\");\n\n\n            \n            #line 122 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                           Write(DashboardOptions.StatsPollingInterval);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n             data-pollurl=\\\"\");\n\n\n            \n            #line 123 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                       Write(Url.To(\"/stats\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n             data-darkmode=\\\"\");\n\n\n            \n            #line 124 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                        Write(DashboardOptions.DarkModeEnabled.ToString().ToLowerInvariant());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n        </div>\\r\\n\\r\\n        <script src=\\\"\");\n\n\n            \n            #line 127 \"..\\..\\Dashboard\\Pages\\LayoutPage.cshtml\"\n                Write(Url.To($\"/js{version.Major}{version.Minor}{version.Build}0{Math.Abs(DashboardRoutes.JavaScriptsHashCode)}\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"></script>\\r\\n    </body>\\r\\n</html>\\r\\n\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/ProcessingJobsPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using System\n@using System.Linq\n@using Hangfire\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.ProcessingJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.ProcessingCount());\n    var processingJobs = monitor.ProcessingJobs(pager.FromRecord, pager.RecordsPerPage);\n    var servers = monitor.Servers();\n}\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        @Html.JobsSidebar()\n    </div>\n    <div class=\"col-md-9\">\n        <h1 id=\"page-title\" class=\"page-header\">@Strings.ProcessingJobsPage_Title</h1>\n\n        @if (pager.TotalPageCount == 0)\n        {\n            <div class=\"alert alert-info\">\n                @Strings.ProcessingJobsPage_NoJobs\n            </div>\n        }\n        else\n        {\n            <div class=\"js-jobs-list\">\n                <div class=\"btn-toolbar btn-toolbar-top\">\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-primary\"\n                                data-url=\"@Url.To(\"/jobs/processing/requeue\")\"\n                                data-loading-text=\"@Strings.Common_Enqueueing\"\n                                disabled=\"disabled\">\n                            <span class=\"glyphicon glyphicon-repeat\"></span>\n                            @Strings.Common_RequeueJobs\n                        </button>\n                    }\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-default\"\n                                data-url=\"@Url.To(\"/jobs/processing/delete\")\"\n                                data-loading-text=\"@Strings.Common_Deleting\"\n                                data-confirm=\"@Strings.Common_DeleteConfirm\"\n                                disabled=\"disabled\">\n                            <span class=\"glyphicon glyphicon-remove\"></span>\n                            @Strings.Common_DeleteSelected\n                        </button>\n                    }\n                    @Html.PerPageSelector(pager)\n                </div>\n\n                <div class=\"table-responsive\">\n                    <table class=\"table\" aria-describedby=\"page-title\">\n                        <thead>\n                            <tr>\n                                @if (!IsReadOnly)\n                                {\n                                    <th class=\"min-width\">\n                                        <input type=\"checkbox\" class=\"js-jobs-list-select-all\"/>\n                                    </th>\n                                }\n                                <th class=\"min-width\">@Strings.Common_Id</th>\n                                <th class=\"min-width\">@Strings.Common_Server</th>\n                                <th>@Strings.Common_Job</th>\n                                <th class=\"align-right\">@Strings.ProcessingJobsPage_Table_Started</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            @foreach (var job in processingJobs)\n                            {\n                                <tr class=\"js-jobs-list-row @(job.Value == null || !job.Value.InProcessingState ? \"obsolete-data\" : null) @(job.Value != null && job.Value.InProcessingState ? \"hover\" : null)\">\n                                    @if (!IsReadOnly)\n                                    {\n                                        <td>\n                                            @if (job.Value != null && job.Value.InProcessingState)\n                                            {\n                                                <input type=\"checkbox\" class=\"js-jobs-list-checkbox\" name=\"jobs[]\" value=\"@job.Key\"/>\n                                            }\n                                        </td>\n                                    }\n                                    <td class=\"min-width\">\n                                        @Html.JobIdLink(job.Key)\n                                        @if (job.Value != null && !job.Value.InProcessingState)\n                                        {\n                                            <span title=\"@Strings.Common_JobStateChanged_Text\" class=\"glyphicon glyphicon-question-sign\"></span>\n                                        }\n                                    </td>\n                                    @if (job.Value == null)\n                                    {\n                                        <td colspan=\"3\"><em>@Strings.Common_JobExpired</em></td>\n                                    }\n                                    else if (!job.Value.InProcessingState)\n                                    {\n                                        <td colspan=\"3\">@Strings.Common_JobStateChanged_Text</td>\n                                    }\n                                    else\n                                    {\n                                        <td class=\"min-width\">\n                                            @Html.ServerId(job.Value.ServerId)\n                                        </td>\n                                        <td class=\"word-break\">\n                                            @if (servers.All(x => x.Name != job.Value.ServerId || x.Heartbeat < (StorageUtcNow ?? ApplicationUtcNow).Add(DashboardOptions.ServerPossiblyAbortedThreshold.Negate())))\n                                            {\n                                                <span title=\"@Strings.ProcessingJobsPage_Aborted\" class=\"glyphicon glyphicon-warning-sign glyphicon-sm text-warning\"></span>\n                                            }\n\n                                            @Html.JobNameLink(job.Key, job.Value.Job)\n                                        </td>\n                                        <td class=\"align-right\">\n                                            @if (job.Value.StartedAt.HasValue)\n                                            {\n                                                @Html.RelativeTime(job.Value.StartedAt.Value)\n                                            }\n                                        </td>\n                                    }\n                                </tr>\n                            }\n                        </tbody>\n                    </table>\n                </div>\n\n                @Html.Paginator(pager)\n            </div>\n        }\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/ProcessingJobsPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n    using System;\n    \n    #line default\n    #line hidden\n    using System.Collections.Generic;\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n    using System.Linq;\n    \n    #line default\n    #line hidden\n    using System.Text;\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 7 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class ProcessingJobsPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n  \n    Layout = new LayoutPage(Strings.ProcessingJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.ProcessingCount());\n    var processingJobs = monitor.ProcessingJobs(pager.FromRecord, pager.RecordsPerPage);\n    var servers = monitor.Servers();\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-3\\\">\\r\\n        \");\n\n\n            \n            #line 25 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n   Write(Html.JobsSidebar());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"col-md-9\\\">\\r\\n        <h1 id=\\\"page-title\\\" class=\\\"page\" +\n\"-header\\\">\");\n\n\n            \n            #line 28 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                           Write(Strings.ProcessingJobsPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 30 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n         if (pager.TotalPageCount == 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-info\\\">\\r\\n                \");\n\n\n            \n            #line 33 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n           Write(Strings.ProcessingJobsPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 35 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n        }\n        else\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"js-jobs-list\\\">\\r\\n                <div class=\\\"btn-toolbar b\" +\n\"tn-toolbar-top\\\">\\r\\n\");\n\n\n            \n            #line 40 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-primar\" +\n\"y\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 43 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                     Write(Url.To(\"/jobs/processing/requeue\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 44 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                              Write(Strings.Common_Enqueueing);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                disabled=\\\"disabled\\\">\\r\\n                        \" +\n\"    <span class=\\\"glyphicon glyphicon-repeat\\\"></span>\\r\\n                          \" +\n\"  \");\n\n\n            \n            #line 47 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                       Write(Strings.Common_RequeueJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 49 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 50 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-defaul\" +\n\"t\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 53 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                     Write(Url.To(\"/jobs/processing/delete\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 54 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                              Write(Strings.Common_Deleting);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-confirm=\\\"\");\n\n\n            \n            #line 55 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                         Write(Strings.Common_DeleteConfirm);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                disabled=\\\"disabled\\\">\\r\\n                        \" +\n\"    <span class=\\\"glyphicon glyphicon-remove\\\"></span>\\r\\n                          \" +\n\"  \");\n\n\n            \n            #line 58 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                       Write(Strings.Common_DeleteSelected);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 60 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    \");\n\n\n            \n            #line 61 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n               Write(Html.PerPageSelector(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n\\r\\n                <div class=\\\"table-responsive\\\">\\r\\n     \" +\n\"               <table class=\\\"table\\\" aria-describedby=\\\"page-title\\\">\\r\\n            \" +\n\"            <thead>\\r\\n                            <tr>\\r\\n\");\n\n\n            \n            #line 68 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                 if (!IsReadOnly)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-select-all\\\"/>\\r\\n   \" +\n\"                                 </th>\\r\\n\");\n\n\n            \n            #line 73 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 74 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                 Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 75 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                 Write(Strings.Common_Server);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th>\");\n\n\n            \n            #line 76 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                               Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 77 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                   Write(Strings.ProcessingJobsPage_Table_Started);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            </tr>\\r\\n                        </thead>\\r\\n     \" +\n\"                   <tbody>\\r\\n\");\n\n\n            \n            #line 81 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                             foreach (var job in processingJobs)\n                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <tr class=\\\"js-jobs-list-row \");\n\n\n            \n            #line 83 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                        Write(job.Value == null || !job.Value.InProcessingState ? \"obsolete-data\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 83 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                                                                                                      Write(job.Value != null && job.Value.InProcessingState ? \"hover\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 84 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                     if (!IsReadOnly)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td>\\r\\n\");\n\n\n            \n            #line 87 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                             if (job.Value != null && job.Value.InProcessingState)\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <input type=\\\"checkbox\\\" class=\\\"js-\" +\n\"jobs-list-checkbox\\\" name=\\\"jobs[]\\\" value=\\\"\");\n\n\n            \n            #line 89 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                                                                                     Write(job.Key);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"/>\\r\\n\");\n\n\n            \n            #line 90 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n            \n            #line 92 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   \");\n\n\n            \n            #line 94 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                   Write(Html.JobIdLink(job.Key));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 95 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                         if (job.Value != null && !job.Value.InProcessingState)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <span title=\\\"\");\n\n\n            \n            #line 97 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                    Write(Strings.Common_JobStateChanged_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"glyphicon glyphicon-question-sign\\\"></span>\\r\\n\");\n\n\n            \n            #line 98 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\");\n\n\n            \n            #line 100 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                     if (job.Value == null)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td colspan=\\\"3\\\"><em>\");\n\n\n            \n            #line 102 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                       Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 103 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                    }\n                                    else if (!job.Value.InProcessingState)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td colspan=\\\"3\\\">\");\n\n\n            \n            #line 106 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                   Write(Strings.Common_JobStateChanged_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n\");\n\n\n            \n            #line 107 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                    }\n                                    else\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td class=\\\"min-width\\\">\\r\\n                 \" +\n\"                           \");\n\n\n            \n            #line 111 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                       Write(Html.ServerId(job.Value.ServerId));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                        </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                        <td class=\\\"word-break\\\">\\r\\n\");\n\n\n            \n            #line 114 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                             if (servers.All(x => x.Name != job.Value.ServerId || x.Heartbeat < (StorageUtcNow ?? ApplicationUtcNow).Add(DashboardOptions.ServerPossiblyAbortedThreshold.Negate())))\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <span title=\\\"\");\n\n\n            \n            #line 116 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                        Write(Strings.ProcessingJobsPage_Aborted);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"glyphicon glyphicon-warning-sign glyphicon-sm text-warning\\\"></span>\\r\\n\");\n\n\n            \n            #line 117 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                            \");\n\n\n            \n            #line 119 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                       Write(Html.JobNameLink(job.Key, job.Value.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                        </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                        <td class=\\\"align-right\\\">\\r\\n\");\n\n\n            \n            #line 122 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                             if (job.Value.StartedAt.HasValue)\n                                            {\n                                                \n            \n            #line default\n            #line hidden\n            \n            #line 124 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                           Write(Html.RelativeTime(job.Value.StartedAt.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 124 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                                                                             \n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n            \n            #line 127 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </tr>\\r\\n\");\n\n\n            \n            #line 129 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        </tbody>\\r\\n                    </table>\\r\\n                <\" +\n\"/div>\\r\\n\\r\\n                \");\n\n\n            \n            #line 134 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n           Write(Html.Paginator(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 136 \"..\\..\\Dashboard\\Pages\\ProcessingJobsPage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/QueuesPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using System.Linq\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.QueuesPage_Title);\n\n    var monitor = Storage.GetMonitoringApi();\n    var queues = monitor.Queues();\n}\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        @Html.JobsSidebar()\n    </div>\n    <div class=\"col-md-9\">\n        <h1 id=\"page-title\" class=\"page-header\">@Strings.QueuesPage_Title</h1>\n\n        @if (queues.Count == 0)\n        {\n            <div class=\"alert alert-warning\">\n                @Strings.QueuesPage_NoQueues\n            </div>\n        }\n        else\n        {\n            <div class=\"table-responsive\">\n                <table class=\"table table-striped\" aria-describedby=\"page-title\">\n                    <thead>\n                        <tr>\n                            <th class=\"min-width-200p\">@Strings.QueuesPage_Table_Queue</th>\n                            <th>@Strings.QueuesPage_Table_Length</th>\n                            <th>@Strings.Common_Fetched</th>\n                            <th>@Strings.QueuesPage_Table_NextsJobs</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        @foreach (var queue in queues)\n                        {\n                            <tr>\n                                <td>@Html.QueueLabel(queue.Name)</td>\n                                <td>@queue.Length</td>\n                                <td>\n                                    @if (queue.Fetched.HasValue)\n                                    {\n                                        <a href=\"@Url.To(\"/jobs/enqueued/fetched/\" + queue.Name)\">\n                                            @queue.Fetched\n                                        </a>\n                                    }\n                                    else\n                                    {\n                                        <em>@Strings.Common_NotAvailable</em>\n                                    }\n                                </td>\n                                <td>\n                                    @if (queue.FirstJobs.Count == 0)\n                                    {\n                                        <em>\n                                            @Strings.QueuesPage_NoJobs\n                                        </em>\n                                    }\n                                    else\n                                    {\n                                        <table class=\"table table-condensed table-inner\">\n                                            <thead>\n                                                <tr>\n                                                    <th class=\"min-width\">@Strings.Common_Id</th>\n                                                    <th class=\"min-width\">@Strings.Common_State</th>\n                                                    <th>@Strings.Common_Job</th>\n                                                    <th class=\"align-right min-width\">@Strings.Common_Enqueued</th>\n                                                </tr>\n                                            </thead>\n                                            <tbody>\n                                                @foreach (var job in queue.FirstJobs)\n                                                {\n                                                    <tr class=\"@(job.Value == null || !job.Value.InEnqueuedState ? \"obsolete-data\" : null)\">\n                                                        <td class=\"min-width\">\n                                                            @Html.JobIdLink(job.Key)\n                                                            @if (job.Value != null && !job.Value.InEnqueuedState)\n                                                            {\n                                                                <span title=\"@Strings.Common_JobStateChanged_Text\" class=\"glyphicon glyphicon-question-sign\"></span>\n                                                            }\n                                                        </td>\n                                                        @if (job.Value == null)\n                                                        {\n                                                            <td colspan=\"3\"><em>@Strings.Common_JobExpired</em></td>\n                                                        }\n                                                        else\n                                                        {\n                                                            <td class=\"min-width\">\n                                                                @Html.StateLabel(job.Value.State)\n                                                            </td>\n                                                            <td class=\"word-break\">\n                                                                @Html.JobNameLink(job.Key, job.Value.Job)\n                                                            </td>\n                                                            <td class=\"align-right min-width\">\n                                                                @if (job.Value.EnqueuedAt.HasValue)\n                                                                {\n                                                                    @Html.RelativeTime(job.Value.EnqueuedAt.Value)\n                                                                }\n                                                                else\n                                                                {\n                                                                    <em>@Strings.Common_NotAvailable</em>\n                                                                }\n                                                            </td>\n                                                        }\n                                                    </tr>\n                                                }\n                                            </tbody>\n                                        </table>\n                                    }\n                                </td>\n                            </tr>\n                        }\n                    </tbody>\n                </table>\n            </div>\n        }\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/QueuesPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n    using System.Linq;\n    \n    #line default\n    #line hidden\n    using System.Text;\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class QueuesPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n            \n            #line 7 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n  \n    Layout = new LayoutPage(Strings.QueuesPage_Title);\n\n    var monitor = Storage.GetMonitoringApi();\n    var queues = monitor.Queues();\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-3\\\">\\r\\n        \");\n\n\n            \n            #line 16 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n   Write(Html.JobsSidebar());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"col-md-9\\\">\\r\\n        <h1 id=\\\"page-title\\\" class=\\\"page\" +\n\"-header\\\">\");\n\n\n            \n            #line 19 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                           Write(Strings.QueuesPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 21 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n         if (queues.Count == 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-warning\\\">\\r\\n                \");\n\n\n            \n            #line 24 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n           Write(Strings.QueuesPage_NoQueues);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 26 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n        }\n        else\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"table-responsive\\\">\\r\\n                <table class=\\\"table t\" +\n\"able-striped\\\" aria-describedby=\\\"page-title\\\">\\r\\n                    <thead>\\r\\n     \" +\n\"                   <tr>\\r\\n                            <th class=\\\"min-width-200p\\\">\" +\n\"\");\n\n\n            \n            #line 33 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                  Write(Strings.QueuesPage_Table_Queue);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th>\");\n\n\n            \n            #line 34 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                           Write(Strings.QueuesPage_Table_Length);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th>\");\n\n\n            \n            #line 35 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                           Write(Strings.Common_Fetched);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th>\");\n\n\n            \n            #line 36 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                           Write(Strings.QueuesPage_Table_NextsJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                        </tr>\\r\\n                    </thead>\\r\\n             \" +\n\"       <tbody>\\r\\n\");\n\n\n            \n            #line 40 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                         foreach (var queue in queues)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <tr>\\r\\n                                <td>\");\n\n\n            \n            #line 43 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                               Write(Html.QueueLabel(queue.Name));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n                                <td>\");\n\n\n            \n            #line 44 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                               Write(queue.Length);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n                                <td>\\r\\n\");\n\n\n            \n            #line 46 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                     if (queue.Fetched.HasValue)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <a href=\\\"\");\n\n\n            \n            #line 48 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                            Write(Url.To(\"/jobs/enqueued/fetched/\" + queue.Name));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                            \");\n\n\n            \n            #line 49 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                       Write(queue.Fetched);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                        </a>\\r\\n\");\n\n\n            \n            #line 51 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                    }\n                                    else\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <em>\");\n\n\n            \n            #line 54 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                       Write(Strings.Common_NotAvailable);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n\");\n\n\n            \n            #line 55 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </td>\\r\\n                                <td>\\r\\n\");\n\n\n            \n            #line 58 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                     if (queue.FirstJobs.Count == 0)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <em>\\r\\n                                   \" +\n\"         \");\n\n\n            \n            #line 61 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                       Write(Strings.QueuesPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                        </em>\\r\\n\");\n\n\n            \n            #line 63 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                    }\n                                    else\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(@\"                                        <table class=\"\"table table-condensed table-inner\"\">\n                                            <thead>\n                                                <tr>\n                                                    <th class=\"\"min-width\"\">\");\n\n\n            \n            #line 69 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                                     Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                                    <th class=\\\"min-width\\\">\" +\n\"\");\n\n\n            \n            #line 70 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                                     Write(Strings.Common_State);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                                    <th>\");\n\n\n            \n            #line 71 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                   Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                                    <th class=\\\"align-right\" +\n\" min-width\\\">\");\n\n\n            \n            #line 72 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                                                 Write(Strings.Common_Enqueued);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                                </tr>\\r\\n                   \" +\n\"                         </thead>\\r\\n                                            <\" +\n\"tbody>\\r\\n\");\n\n\n            \n            #line 76 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                 foreach (var job in queue.FirstJobs)\n                                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                    <tr class=\\\"\");\n\n\n            \n            #line 78 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                           Write(job.Value == null || !job.Value.InEnqueuedState ? \"obsolete-data\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                                        <td class=\\\"min-width\\\"\" +\n\">\\r\\n                                                            \");\n\n\n            \n            #line 80 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                       Write(Html.JobIdLink(job.Key));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 81 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                             if (job.Value != null && !job.Value.InEnqueuedState)\n                                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                                <span title=\\\"\");\n\n\n            \n            #line 83 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                                        Write(Strings.Common_JobStateChanged_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"glyphicon glyphicon-question-sign\\\"></span>\\r\\n\");\n\n\n            \n            #line 84 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                        </td>\\r\\n\");\n\n\n            \n            #line 86 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                         if (job.Value == null)\n                                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                            <td colspan=\\\"3\\\"><em>\");\n\n\n            \n            #line 88 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                                           Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 89 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                        }\n                                                        else\n                                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                            <td class=\\\"min-width\\\"\" +\n\">\\r\\n                                                                \");\n\n\n            \n            #line 93 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                           Write(Html.StateLabel(job.Value.State));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                                            </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                                            <td class=\\\"word-break\" +\n\"\\\">\\r\\n                                                                \");\n\n\n            \n            #line 96 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                           Write(Html.JobNameLink(job.Key, job.Value.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                                            </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                                            <td class=\\\"align-righ\" +\n\"t min-width\\\">\\r\\n\");\n\n\n            \n            #line 99 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                                 if (job.Value.EnqueuedAt.HasValue)\n                                                                {\n                                                                    \n            \n            #line default\n            #line hidden\n            \n            #line 101 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                               Write(Html.RelativeTime(job.Value.EnqueuedAt.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 101 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                                                                                  \n                                                                }\n                                                                else\n                                                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                                    <em>\");\n\n\n            \n            #line 105 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                                   Write(Strings.Common_NotAvailable);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n\");\n\n\n            \n            #line 106 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                            </td>\\r\\n\");\n\n\n            \n            #line 108 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                    </tr>\\r\\n\");\n\n\n            \n            #line 110 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            </tbody>\\r\\n                           \" +\n\"             </table>\\r\\n\");\n\n\n            \n            #line 113 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </td>\\r\\n                            </tr>\\r\\n\");\n\n\n            \n            #line 116 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    </tbody>\\r\\n                </table>\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 120 \"..\\..\\Dashboard\\Pages\\QueuesPage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/RecurringJobsPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: true *@\n@using System\n@using System.Collections.Generic\n@using Hangfire\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@using Hangfire.States\n@using Hangfire.Storage\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.RecurringJobsPage_Title);\n\tList<RecurringJobDto> recurringJobs;\n    \n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    Pager pager = null;\n\n\tusing (var connection = Storage.GetReadOnlyConnection())\n\t{\n\t    var storageConnection = connection as JobStorageConnection;\n\t    if (storageConnection != null)\n\t    {\n\t        pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, storageConnection.GetRecurringJobCount());\n\t        recurringJobs = storageConnection.GetRecurringJobs(pager.FromRecord, pager.FromRecord + pager.RecordsPerPage - 1);\n\t    }\n\t    else\n\t    {\n            recurringJobs = connection.GetRecurringJobs();\n\t    }\n\t}\n}\n\n<div class=\"row\">\n    <div class=\"col-md-12\">\n        <h1 id=\"page-title\" class=\"page-header\">@Strings.RecurringJobsPage_Title</h1>\n        @if (recurringJobs.Count == 0)\n        {\n            <div class=\"alert alert-info\">\n                @Strings.RecurringJobsPage_NoJobs\n            </div>\n        }\n        else\n        {\n            <div class=\"js-jobs-list\">\n                <div class=\"btn-toolbar btn-toolbar-top\">\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-primary\"\n                                data-url=\"@Url.To(\"/recurring/trigger\")\"\n                                data-loading-text=\"@Strings.RecurringJobsPage_Triggering\"\n                                disabled=\"disabled\">\n                            <span class=\"glyphicon glyphicon-play-circle\"></span>\n                            @Strings.RecurringJobsPage_TriggerNow\n                        </button>\n                    }\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-default\"\n                                data-url=\"@Url.To(\"/recurring/remove\")\"\n                                data-loading-text=\"@Strings.Common_Deleting\"\n                                data-confirm=\"@Strings.Common_DeleteConfirm\"\n                                disabled=\"disabled\">\n                            <span class=\"glyphicon glyphicon-remove\"></span>\n                            @Strings.Common_Delete\n                        </button>\n                    }\n                    @if (pager != null)\n                    {\n                        @: @Html.PerPageSelector(pager)\n                    }\n                </div>\n\n                <div class=\"table-responsive\">\n                    <table class=\"table\" aria-describedby=\"page-title\">\n                        <thead>\n                            <tr>\n                                @if (!IsReadOnly)\n                                {\n                                    <th class=\"min-width\">\n                                        <input type=\"checkbox\" class=\"js-jobs-list-select-all\"/>\n                                    </th>\n                                }\n                                <th>@Strings.Common_Id</th>\n                                <th class=\"min-width\">@Strings.RecurringJobsPage_Table_Cron</th>\n                                <th>@Strings.RecurringJobsPage_Table_TimeZone</th>\n                                <th>@Strings.Common_Job</th>\n                                <th class=\"align-right min-width\">@Strings.RecurringJobsPage_Table_NextExecution</th>\n                                <th class=\"align-right min-width\">@Strings.RecurringJobsPage_Table_LastExecution</th>\n                                <th class=\"align-right min-width\">@Strings.Common_Created</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            @foreach (var job in recurringJobs)\n                            {\n                                <tr class=\"js-jobs-list-row hover\">\n                                    @if (!IsReadOnly)\n                                    {\n                                        <td rowspan=\"@(job.Error != null ? \"2\" : \"1\")\">\n                                            <input type=\"checkbox\" class=\"js-jobs-list-checkbox\" name=\"jobs[]\" value=\"@job.Id\"/>\n                                        </td>\n                                    }\n                                    <td class=\"word-break\">@job.Id</td>\n                                    <td class=\"min-width min-width-125p \">\n                                        @* ReSharper disable once EmptyGeneralCatchClause *@\n                                        @{\n                                            string cronDescription = null;\n                                            bool cronError = false;\n\n                                            if (!String.IsNullOrEmpty(job.Cron))\n                                            {\n                                                try\n                                                {\n                                                    RecurringJobEntity.ParseCronExpression(job.Cron);\n                                                }\n                                                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                                                {\n                                                    cronDescription = ex.Message;\n                                                    cronError = true;\n                                                }\n\n                                                if (cronDescription == null)\n                                                {\n#if FEATURE_CRONDESCRIPTOR\n                                                    try\n                                                    {\n                                                        cronDescription = CronExpressionDescriptor.ExpressionDescriptor.GetDescription(job.Cron);\n                                                    }\n                                                    catch (FormatException)\n                                                    {\n                                                    }\n#endif\n                                                }\n                                            }\n                                        }\n\n                                        @if (cronDescription != null)\n                                        {\n                                        <code title=\"@cronDescription\" class=\"cron-badge\">\n                                            @if (cronError)\n                                            {\n                                                <span class=\"glyphicon glyphicon-exclamation-sign\"></span>\n                                            }\n                                            @job.Cron\n                                        </code>\n                                        }\n                                        else\n                                        {\n                                            <code class=\"cron-badge\">@job.Cron</code>\n                                        }\n                                    </td>\n                                    <td>\n                                        @if (!String.IsNullOrWhiteSpace(job.TimeZoneId))\n                                        {\n                                            string displayName;\n                                            Exception exception = null;\n\n                                            try\n                                            {\n                                                var resolver = DashboardOptions.TimeZoneResolver ?? new DefaultTimeZoneResolver();\n                                                displayName = resolver.GetTimeZoneById(job.TimeZoneId).DisplayName;\n                                            }\n                                            catch (Exception ex) when (ex.IsCatchableExceptionType())\n                                            {\n                                                displayName = null;\n                                                exception = ex;\n                                            }\n\n                                            <span title=\"@displayName\" data-container=\"body\">@job.TimeZoneId\n                                                @if (exception != null)\n                                                {\n                                                    <span class=\"glyphicon glyphicon-exclamation-sign\" title=\"@exception.Message\"></span>\n                                                }\n                                            </span>\n                                        }\n                                        else\n                                        {\n                                            @: UTC\n                                        }\n                                    </td>\n                                    <td class=\"word-break width-30\">\n                                        @if (job.Job != null)\n                                        {\n                                            @: @Html.JobName(job.Job)\n                                        }\n                                        else if (job.LoadException != null && job.LoadException.InnerException != null)\n                                        {\n                                            <em>@job.LoadException.InnerException.Message</em>\n                                        }\n                                        else if (job.LoadException != null)\n                                        {\n                                            <em>@job.LoadException.Message</em>\n                                        }\n                                        else\n                                        {\n                                            <em>@Strings.Common_NotAvailable</em>\n                                        }\n                                    </td>\n                                    <td class=\"align-right min-width\">\n                                        @if (!job.NextExecution.HasValue)\n                                        {\n                                            if (job.Error != null)\n                                            {\n                                                <span class=\"label label-danger text-uppercase\">@Strings.Common_Error</span>\n                                            }\n                                            else\n                                            {\n                                                <span class=\"label label-default text-uppercase\" title=\"@Strings.RecurringJobsPage_RecurringJobDisabled_Tooltip\">@Strings.Common_Disabled</span>\n                                            }\n\n                                        }\n                                        else if (job.RetryAttempt > 0)\n                                        {\n                                            <span class=\"label label-warning\">@Html.RelativeTime(job.NextExecution.Value)</span>\n                                        }\n                                        else\n                                        {\n                                            @Html.RelativeTime(job.NextExecution.Value)\n                                        }\n                                    </td>\n                                    <td class=\"align-right min-width\">\n                                        @if (job.LastExecution != null)\n                                        {\n                                            if (!String.IsNullOrEmpty(job.LastJobId))\n                                            {\n                                                <a href=\"@Url.JobDetails(job.LastJobId)\" class=\"text-decoration-none\">\n                                                    @{\n                                                        var cssSuffix = JobHistoryRenderer.GetStateCssSuffix(job.LastJobState ?? EnqueuedState.StateName);\n                                                    }\n                                                    @if (cssSuffix != null)\n                                                    {\n                                                        <span class=\"label label-default label-hover label-state-@cssSuffix\">\n                                                            @Html.RelativeTime(job.LastExecution.Value)\n                                                        </span>\n                                                    }\n                                                    else\n                                                    {\n                                                        <span class=\"label label-default label-hover\" style=\"@($\"background-color: {JobHistoryRenderer.GetForegroundStateColor(job.LastJobState ?? EnqueuedState.StateName)};\")\">\n                                                            @Html.RelativeTime(job.LastExecution.Value)\n                                                        </span>\n                                                    }\n                                                </a>\n                                            }\n                                            else\n                                            {\n                                                <span class=\"label label-default label-state-inactive\" title=\"@Strings.RecurringJobsPage_Canceled\">\n                                                    @Html.RelativeTime(job.LastExecution.Value)\n                                                </span>\n                                            }\n                                        }\n                                        else\n                                        {\n                                            <em>@Strings.Common_NotAvailable</em>\n                                        }\n                                    </td>\n                                    <td class=\"align-right min-width\">\n                                        @if (job.CreatedAt != null)\n                                        {\n                                            @Html.RelativeTime(job.CreatedAt.Value)\n                                        }\n                                        else\n                                        {\n                                            <em>N/A</em>\n                                        }\n                                    </td>\n                                    @if (job.Error != null)\n                                    {\n                                        <tr>\n                                            <td colspan=\"@(IsReadOnly ? \"6\" : \"7\")\" class=\"failed-job-details\">\n                                                <pre class=\"stack-trace\"><code>@Html.StackTrace(job.Error)</code></pre>\n                                            </td>\n                                        </tr>\n                                    }\n                                </tr>\n                             }\n                        </tbody>\n                    </table>\n                </div>\n\n                @if (pager != null)\n                {\n                    @: @Html.Paginator(pager)\n                }\n            </div>\n        }\n    </div>\n</div>    "
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/RecurringJobsPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n    using System;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n    using System.Collections.Generic;\n    \n    #line default\n    #line hidden\n    using System.Linq;\n    using System.Text;\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 7 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    #line 8 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n    using Hangfire.States;\n    \n    #line default\n    #line hidden\n    \n    #line 9 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n    using Hangfire.Storage;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class RecurringJobsPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n\n\n\n            \n            #line 11 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n  \n    Layout = new LayoutPage(Strings.RecurringJobsPage_Title);\n\tList<RecurringJobDto> recurringJobs;\n    \n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    Pager pager = null;\n\n\tusing (var connection = Storage.GetReadOnlyConnection())\n\t{\n\t    var storageConnection = connection as JobStorageConnection;\n\t    if (storageConnection != null)\n\t    {\n\t        pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, storageConnection.GetRecurringJobCount());\n\t        recurringJobs = storageConnection.GetRecurringJobs(pager.FromRecord, pager.FromRecord + pager.RecordsPerPage - 1);\n\t    }\n\t    else\n\t    {\n            recurringJobs = connection.GetRecurringJobs();\n\t    }\n\t}\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-12\\\">\\r\\n        <h1 id=\\\"page-title\\\" cla\" +\n\"ss=\\\"page-header\\\">\");\n\n\n            \n            #line 39 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                           Write(Strings.RecurringJobsPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\");\n\n\n            \n            #line 40 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n         if (recurringJobs.Count == 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-info\\\">\\r\\n                \");\n\n\n            \n            #line 43 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n           Write(Strings.RecurringJobsPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 45 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n        }\n        else\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"js-jobs-list\\\">\\r\\n                <div class=\\\"btn-toolbar b\" +\n\"tn-toolbar-top\\\">\\r\\n\");\n\n\n            \n            #line 50 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-primar\" +\n\"y\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 53 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                     Write(Url.To(\"/recurring/trigger\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 54 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                              Write(Strings.RecurringJobsPage_Triggering);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                disabled=\\\"disabled\\\">\\r\\n                        \" +\n\"    <span class=\\\"glyphicon glyphicon-play-circle\\\"></span>\\r\\n                     \" +\n\"       \");\n\n\n            \n            #line 57 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                       Write(Strings.RecurringJobsPage_TriggerNow);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 59 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 60 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-defaul\" +\n\"t\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 63 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                     Write(Url.To(\"/recurring/remove\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 64 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                              Write(Strings.Common_Deleting);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-confirm=\\\"\");\n\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                         Write(Strings.Common_DeleteConfirm);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                disabled=\\\"disabled\\\">\\r\\n                        \" +\n\"    <span class=\\\"glyphicon glyphicon-remove\\\"></span>\\r\\n                          \" +\n\"  \");\n\n\n            \n            #line 68 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                       Write(Strings.Common_Delete);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 70 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 71 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                     if (pager != null)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        \");\n\nWriteLiteral(\" \");\n\n\n            \n            #line 73 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                      Write(Html.PerPageSelector(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 74 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                </div>\\r\\n\\r\\n                <div class=\\\"table-responsive\\\">\\r\\n       \" +\n\"             <table class=\\\"table\\\" aria-describedby=\\\"page-title\\\">\\r\\n              \" +\n\"          <thead>\\r\\n                            <tr>\\r\\n\");\n\n\n            \n            #line 81 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                 if (!IsReadOnly)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-select-all\\\"/>\\r\\n   \" +\n\"                                 </th>\\r\\n\");\n\n\n            \n            #line 86 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th>\");\n\n\n            \n            #line 87 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                               Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 88 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                 Write(Strings.RecurringJobsPage_Table_Cron);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th>\");\n\n\n            \n            #line 89 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                               Write(Strings.RecurringJobsPage_Table_TimeZone);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th>\");\n\n\n            \n            #line 90 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                               Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"align-right min-width\\\">\");\n\n\n            \n            #line 91 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                             Write(Strings.RecurringJobsPage_Table_NextExecution);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"align-right min-width\\\">\");\n\n\n            \n            #line 92 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                             Write(Strings.RecurringJobsPage_Table_LastExecution);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"align-right min-width\\\">\");\n\n\n            \n            #line 93 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                             Write(Strings.Common_Created);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            </tr>\\r\\n                        </thead>\\r\\n     \" +\n\"                   <tbody>\\r\\n\");\n\n\n            \n            #line 97 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                             foreach (var job in recurringJobs)\n                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <tr class=\\\"js-jobs-list-row hover\\\">\\r\\n\");\n\n\n            \n            #line 100 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                     if (!IsReadOnly)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td rowspan=\\\"\");\n\n\n            \n            #line 102 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                 Write(job.Error != null ? \"2\" : \"1\");\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                            <input type=\\\"checkbox\\\" class=\\\"js-\" +\n\"jobs-list-checkbox\\\" name=\\\"jobs[]\\\" value=\\\"\");\n\n\n            \n            #line 103 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                                                 Write(job.Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"/>\\r\\n                                        </td>\\r\\n\");\n\n\n            \n            #line 105 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td class=\\\"word-break\\\">\");\n\n\n            \n            #line 106 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                      Write(job.Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n                                    <td class=\\\"min-width min-width-125p \\\">\" +\n\"\\r\\n                                        \");\n\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 109 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                          \n                                            string cronDescription = null;\n                                            bool cronError = false;\n\n                                            if (!String.IsNullOrEmpty(job.Cron))\n                                            {\n                                                try\n                                                {\n                                                    RecurringJobEntity.ParseCronExpression(job.Cron);\n                                                }\n                                                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                                                {\n                                                    cronDescription = ex.Message;\n                                                    cronError = true;\n                                                }\n\n                                                if (cronDescription == null)\n                                                {\n#if FEATURE_CRONDESCRIPTOR\n                                                    try\n                                                    {\n                                                        cronDescription = CronExpressionDescriptor.ExpressionDescriptor.GetDescription(job.Cron);\n                                                    }\n                                                    catch (FormatException)\n                                                    {\n                                                    }\n#endif\n                                                }\n                                            }\n                                        \n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 140 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                         if (cronDescription != null)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <code title=\\\"\");\n\n\n            \n            #line 142 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                Write(cronDescription);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"cron-badge\\\">\\r\\n\");\n\n\n            \n            #line 143 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                             if (cronError)\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <span class=\\\"glyphicon glyphicon-\" +\n\"exclamation-sign\\\"></span>\\r\\n\");\n\n\n            \n            #line 146 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            \");\n\n\n            \n            #line 147 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                       Write(job.Cron);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                        </code>\\r\\n\");\n\n\n            \n            #line 149 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n                                        else\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <code class=\\\"cron-badge\\\">\");\n\n\n            \n            #line 152 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                Write(job.Cron);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</code>\\r\\n\");\n\n\n            \n            #line 153 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n                                    <t\" +\n\"d>\\r\\n\");\n\n\n            \n            #line 156 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                         if (!String.IsNullOrWhiteSpace(job.TimeZoneId))\n                                        {\n                                            string displayName;\n                                            Exception exception = null;\n\n                                            try\n                                            {\n                                                var resolver = DashboardOptions.TimeZoneResolver ?? new DefaultTimeZoneResolver();\n                                                displayName = resolver.GetTimeZoneById(job.TimeZoneId).DisplayName;\n                                            }\n                                            catch (Exception ex) when (ex.IsCatchableExceptionType())\n                                            {\n                                                displayName = null;\n                                                exception = ex;\n                                            }\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <span title=\\\"\");\n\n\n            \n            #line 172 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                    Write(displayName);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" data-container=\\\"body\\\">\");\n\n\n            \n            #line 172 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                        Write(job.TimeZoneId);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 173 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                 if (exception != null)\n                                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                    <span class=\\\"glyphicon glyphi\" +\n\"con-exclamation-sign\\\" title=\\\"\");\n\n\n            \n            #line 175 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                                         Write(exception.Message);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"></span>\\r\\n\");\n\n\n            \n            #line 176 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            </span>\\r\\n\");\n\n\n            \n            #line 178 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n                                        else\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            \");\n\nWriteLiteral(\" UTC\\r\\n\");\n\n\n            \n            #line 182 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n                                    <t\" +\n\"d class=\\\"word-break width-30\\\">\\r\\n\");\n\n\n            \n            #line 185 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                         if (job.Job != null)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            \");\n\nWriteLiteral(\" \");\n\n\n            \n            #line 187 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                          Write(Html.JobName(job.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 188 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n                                        else if (job.LoadException != null && job.LoadException.InnerException != null)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <em>\");\n\n\n            \n            #line 191 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                           Write(job.LoadException.InnerException.Message);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n\");\n\n\n            \n            #line 192 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n                                        else if (job.LoadException != null)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <em>\");\n\n\n            \n            #line 195 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                           Write(job.LoadException.Message);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n\");\n\n\n            \n            #line 196 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n                                        else\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <em>\");\n\n\n            \n            #line 199 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                           Write(Strings.Common_NotAvailable);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n\");\n\n\n            \n            #line 200 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n                                    <t\" +\n\"d class=\\\"align-right min-width\\\">\\r\\n\");\n\n\n            \n            #line 203 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                         if (!job.NextExecution.HasValue)\n                                        {\n                                            if (job.Error != null)\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <span class=\\\"label label-danger t\" +\n\"ext-uppercase\\\">\");\n\n\n            \n            #line 207 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                           Write(Strings.Common_Error);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</span>\\r\\n\");\n\n\n            \n            #line 208 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                            }\n                                            else\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <span class=\\\"label label-default \" +\n\"text-uppercase\\\" title=\\\"\");\n\n\n            \n            #line 211 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                                   Write(Strings.RecurringJobsPage_RecurringJobDisabled_Tooltip);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\");\n\n\n            \n            #line 211 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                                                                                            Write(Strings.Common_Disabled);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</span>\\r\\n\");\n\n\n            \n            #line 212 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                            }\n\n                                        }\n                                        else if (job.RetryAttempt > 0)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <span class=\\\"label label-warning\\\">\");\n\n\n            \n            #line 217 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                         Write(Html.RelativeTime(job.NextExecution.Value));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</span>\\r\\n\");\n\n\n            \n            #line 218 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n                                        else\n                                        {\n                                            \n            \n            #line default\n            #line hidden\n            \n            #line 221 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                       Write(Html.RelativeTime(job.NextExecution.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 221 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                       \n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n                                    <t\" +\n\"d class=\\\"align-right min-width\\\">\\r\\n\");\n\n\n            \n            #line 225 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                         if (job.LastExecution != null)\n                                        {\n                                            if (!String.IsNullOrEmpty(job.LastJobId))\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <a href=\\\"\");\n\n\n            \n            #line 229 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                    Write(Url.JobDetails(job.LastJobId));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"text-decoration-none\\\">\\r\\n\");\n\n\n            \n            #line 230 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                      \n                                                        var cssSuffix = JobHistoryRenderer.GetStateCssSuffix(job.LastJobState ?? EnqueuedState.StateName);\n                                                    \n\n            \n            #line default\n            #line hidden\n\n            \n            #line 233 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                     if (cssSuffix != null)\n                                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                        <span class=\\\"label label-\" +\n\"default label-hover label-state-\");\n\n\n            \n            #line 235 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                                            Write(cssSuffix);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                                            \");\n\n\n            \n            #line 236 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                       Write(Html.RelativeTime(job.LastExecution.Value));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                                        </span>\\r\\n\");\n\n\n            \n            #line 238 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                    }\n                                                    else\n                                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                        <span class=\\\"label label-\" +\n\"default label-hover\\\" style=\\\"\");\n\n\n            \n            #line 241 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                                         Write($\"background-color: {JobHistoryRenderer.GetForegroundStateColor(job.LastJobState ?? EnqueuedState.StateName)};\");\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                                            \");\n\n\n            \n            #line 242 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                       Write(Html.RelativeTime(job.LastExecution.Value));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                                        </span>\\r\\n\");\n\n\n            \n            #line 244 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                </a>\\r\\n\");\n\n\n            \n            #line 246 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                            }\n                                            else\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <span class=\\\"label label-default \" +\n\"label-state-inactive\\\" title=\\\"\");\n\n\n            \n            #line 249 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                                         Write(Strings.RecurringJobsPage_Canceled);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                                                    \");\n\n\n            \n            #line 250 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                   Write(Html.RelativeTime(job.LastExecution.Value));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                                </span>\\r\\n\");\n\n\n            \n            #line 252 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                            }\n                                        }\n                                        else\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <em>\");\n\n\n            \n            #line 256 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                           Write(Strings.Common_NotAvailable);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em>\\r\\n\");\n\n\n            \n            #line 257 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n                                    <t\" +\n\"d class=\\\"align-right min-width\\\">\\r\\n\");\n\n\n            \n            #line 260 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                         if (job.CreatedAt != null)\n                                        {\n                                            \n            \n            #line default\n            #line hidden\n            \n            #line 262 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                       Write(Html.RelativeTime(job.CreatedAt.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 262 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                                   \n                                        }\n                                        else\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <em>N/A</em>\\r\\n\");\n\n\n            \n            #line 267 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\");\n\n\n            \n            #line 269 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                     if (job.Error != null)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <tr>\\r\\n                                   \" +\n\"         <td colspan=\\\"\");\n\n\n            \n            #line 272 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                     Write(IsReadOnly ? \"6\" : \"7\");\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"failed-job-details\\\">\\r\\n                                                <p\" +\n\"re class=\\\"stack-trace\\\"><code>\");\n\n\n            \n            #line 273 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                                                          Write(Html.StackTrace(job.Error));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</code></pre>\\r\\n                                            </td>\\r\\n               \" +\n\"                         </tr>\\r\\n\");\n\n\n            \n            #line 276 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </tr>\\r\\n\");\n\n\n            \n            #line 278 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                             }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        </tbody>\\r\\n                    </table>\\r\\n                <\" +\n\"/div>\\r\\n\\r\\n\");\n\n\n            \n            #line 283 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                 if (pager != null)\n                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    \");\n\nWriteLiteral(\" \");\n\n\n            \n            #line 285 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                  Write(Html.Paginator(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 286 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            </div>\\r\\n\");\n\n\n            \n            #line 288 \"..\\..\\Dashboard\\Pages\\RecurringJobsPage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>    \");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/RetriesPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using System\n@using System.Collections.Generic\n@using Hangfire\n@using Hangfire.Common\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@using Hangfire.Storage\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.RetriesPage_Title);\n    \n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    Pager pager = null;\n    List<string> jobIds = null;\n\n    using (var connection = Storage.GetReadOnlyConnection())\n    {\n        var storageConnection = connection as JobStorageConnection;\n\n        if (storageConnection != null)\n        {\n            pager = new Pager(@from, perPage, DashboardOptions.DefaultRecordsPerPage, storageConnection.GetSetCount(\"retries\"));\n            jobIds = storageConnection.GetRangeFromSet(\"retries\", pager.FromRecord, pager.FromRecord + pager.RecordsPerPage - 1);\n        }\n    }\n}\n\n@if (pager == null)\n{\n    <div class=\"alert alert-warning\">\n        @Html.Raw(String.Format(Strings.RetriesPage_Warning_Html, Url.To(\"/jobs/scheduled\")))\n    </div>\n}\nelse\n{\n    <div class=\"row\">\n        <div class=\"col-md-12\">\n            <h1 id=\"page-title\" class=\"page-header\">@Strings.RetriesPage_Title</h1>\n            @if (jobIds.Count == 0)\n            {\n                <div class=\"alert alert-success\">\n                    @Strings.RetriesPage_NoJobs\n                </div>\n            }\n            else\n            {\n                <div class=\"js-jobs-list\">\n                    <div class=\"btn-toolbar btn-toolbar-top\">\n                        @if (!IsReadOnly)\n                        {\n                            <button class=\"js-jobs-list-command btn btn-sm btn-primary\"\n                                    data-url=\"@Url.To(\"/jobs/scheduled/enqueue\")\"\n                                    data-loading-text=\"@Strings.Common_Enqueueing\"\n                                    disabled=\"disabled\">\n                                <span class=\"glyphicon glyphicon-repeat\"></span>\n                                @Strings.Common_EnqueueButton_Text\n                            </button>\n                        }\n                        @if (!IsReadOnly)\n                        {\n                            <button class=\"js-jobs-list-command btn btn-sm btn-default\"\n                                    data-url=\"@Url.To(\"/jobs/scheduled/delete\")\"\n                                    data-loading-text=\"@Strings.Common_Deleting\"\n                                    data-confirm=\"@Strings.Common_DeleteConfirm\"\n                                    disabled=\"disabled\">\n                                <span class=\"glyphicon glyphicon-remove\"></span>\n                                @Strings.Common_DeleteSelected\n                            </button>\n                        }\n                        @Html.PerPageSelector(pager)\n                    </div>\n\n                    <div class=\"table-responsive\">\n                        <table class=\"table table-hover\" aria-describedby=\"page-title\">\n                            <thead>\n                                <tr>\n                                    @if (!IsReadOnly)\n                                    {\n                                        <th class=\"min-width\">\n                                            <input type=\"checkbox\" class=\"js-jobs-list-select-all\"/>\n                                        </th>\n                                    }\n                                    <th class=\"min-width\">@Strings.Common_Id</th>\n                                    <th class=\"min-width\">@Strings.Common_State</th>\n                                    <th>@Strings.Common_Job</th>\n                                    <th>@Strings.Common_Reason</th>\n                                    <th class=\"align-right\">@Strings.Common_Retry</th>\n                                    <th class=\"align-right\">@Strings.Common_Created</th>\n                                </tr>\n                            </thead>\n                            <tbody>\n                                @foreach (var jobId in jobIds)\n                                {\n                                    JobData jobData;\n                                    StateData stateData;\n\n                                    using (var connection = Storage.GetReadOnlyConnection())\n                                    {\n                                        jobData = connection.GetJobData(jobId);\n                                        stateData = connection.GetStateData(jobId);\n                                    }\n\n                                    <tr class=\"js-jobs-list-row @(jobData != null ? \"hover\" : null)\">\n                                        @if (!IsReadOnly)\n                                        {\n                                            <td>\n                                                <input type=\"checkbox\" class=\"js-jobs-list-checkbox\" name=\"jobs[]\" value=\"@jobId\"/>\n                                            </td>\n                                        }\n                                        <td class=\"min-width\">\n                                            @Html.JobIdLink(jobId)\n                                        </td>\n                                        @if (jobData == null)\n                                        {\n                                            <td colspan=\"5\"><em>Job expired.</em></td>\n                                        }\n                                        else\n                                        {\n                                            <td class=\"min-width\">\n                                                @Html.StateLabel(jobData.State)\n                                            </td>\n                                            <td class=\"word-break\">\n                                                @Html.JobNameLink(jobId, jobData.Job)\n                                            </td>\n                                            <td>\n                                                @(stateData?.Reason)\n                                            </td>\n                                            <td class=\"align-right\">\n                                                @if (stateData != null && stateData.Data.ContainsKey(\"EnqueueAt\"))\n                                                {\n                                                    @Html.RelativeTime(JobHelper.DeserializeDateTime(stateData.Data[\"EnqueueAt\"]))\n                                                }\n                                            </td>\n                                            <td class=\"align-right\">\n                                                @Html.RelativeTime(jobData.CreatedAt)\n                                            </td>\n                                        }\n                                    </tr>\n                                }\n                            </tbody>\n                        </table>\n                    </div>\n\n                    @Html.Paginator(pager)\n                </div>\n            }\n        </div>\n    </div>\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/RetriesPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n    using System;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n    using System.Collections.Generic;\n    \n    #line default\n    #line hidden\n    using System.Linq;\n    using System.Text;\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n    using Hangfire.Common;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 7 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 8 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    #line 9 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n    using Hangfire.Storage;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class RetriesPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n\n\n\n            \n            #line 11 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n  \n    Layout = new LayoutPage(Strings.RetriesPage_Title);\n    \n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    Pager pager = null;\n    List<string> jobIds = null;\n\n    using (var connection = Storage.GetReadOnlyConnection())\n    {\n        var storageConnection = connection as JobStorageConnection;\n\n        if (storageConnection != null)\n        {\n            pager = new Pager(@from, perPage, DashboardOptions.DefaultRecordsPerPage, storageConnection.GetSetCount(\"retries\"));\n            jobIds = storageConnection.GetRangeFromSet(\"retries\", pager.FromRecord, pager.FromRecord + pager.RecordsPerPage - 1);\n        }\n    }\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 34 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n if (pager == null)\n{\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    <div class=\\\"alert alert-warning\\\">\\r\\n        \");\n\n\n            \n            #line 37 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n   Write(Html.Raw(String.Format(Strings.RetriesPage_Warning_Html, Url.To(\"/jobs/scheduled\"))));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n\");\n\n\n            \n            #line 39 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n}\nelse\n{\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    <div class=\\\"row\\\">\\r\\n        <div class=\\\"col-md-12\\\">\\r\\n            <h1 id=\\\"page-\" +\n\"title\\\" class=\\\"page-header\\\">\");\n\n\n            \n            #line 44 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                               Write(Strings.RetriesPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\");\n\n\n            \n            #line 45 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n             if (jobIds.Count == 0)\n            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                <div class=\\\"alert alert-success\\\">\\r\\n                    \");\n\n\n            \n            #line 48 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n               Write(Strings.RetriesPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n\");\n\n\n            \n            #line 50 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n            }\n            else\n            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                <div class=\\\"js-jobs-list\\\">\\r\\n                    <div class=\\\"btn-t\" +\n\"oolbar btn-toolbar-top\\\">\\r\\n\");\n\n\n            \n            #line 55 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                         if (!IsReadOnly)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <button class=\\\"js-jobs-list-command btn btn-sm btn-pr\" +\n\"imary\\\"\\r\\n                                    data-url=\\\"\");\n\n\n            \n            #line 58 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                         Write(Url.To(\"/jobs/scheduled/enqueue\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                    data-loading-text=\\\"\");\n\n\n            \n            #line 59 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                                  Write(Strings.Common_Enqueueing);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                    disabled=\\\"disabled\\\">\\r\\n                    \" +\n\"            <span class=\\\"glyphicon glyphicon-repeat\\\"></span>\\r\\n                  \" +\n\"              \");\n\n\n            \n            #line 62 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                           Write(Strings.Common_EnqueueButton_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                            </button>\\r\\n\");\n\n\n            \n            #line 64 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                         if (!IsReadOnly)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <button class=\\\"js-jobs-list-command btn btn-sm btn-de\" +\n\"fault\\\"\\r\\n                                    data-url=\\\"\");\n\n\n            \n            #line 68 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                         Write(Url.To(\"/jobs/scheduled/delete\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                    data-loading-text=\\\"\");\n\n\n            \n            #line 69 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                                  Write(Strings.Common_Deleting);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                    data-confirm=\\\"\");\n\n\n            \n            #line 70 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                             Write(Strings.Common_DeleteConfirm);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                    disabled=\\\"disabled\\\">\\r\\n                    \" +\n\"            <span class=\\\"glyphicon glyphicon-remove\\\"></span>\\r\\n                  \" +\n\"              \");\n\n\n            \n            #line 73 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                           Write(Strings.Common_DeleteSelected);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                            </button>\\r\\n\");\n\n\n            \n            #line 75 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        \");\n\n\n            \n            #line 76 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                   Write(Html.PerPageSelector(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                    </div>\\r\\n\\r\\n                    <div class=\\\"table-responsive\\\"\" +\n\">\\r\\n                        <table class=\\\"table table-hover\\\" aria-describedby=\\\"pa\" +\n\"ge-title\\\">\\r\\n                            <thead>\\r\\n                               \" +\n\" <tr>\\r\\n\");\n\n\n            \n            #line 83 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                     if (!IsReadOnly)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <th class=\\\"min-width\\\">\\r\\n                 \" +\n\"                           <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-select-all\" +\n\"\\\"/>\\r\\n                                        </th>\\r\\n\");\n\n\n            \n            #line 88 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 89 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                                     Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                    <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 90 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                                     Write(Strings.Common_State);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                    <th>\");\n\n\n            \n            #line 91 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                   Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                    <th>\");\n\n\n            \n            #line 92 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                   Write(Strings.Common_Reason);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                    <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 93 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                                       Write(Strings.Common_Retry);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                    <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 94 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                                       Write(Strings.Common_Created);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                </tr>\\r\\n                            </thead\" +\n\">\\r\\n                            <tbody>\\r\\n\");\n\n\n            \n            #line 98 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                 foreach (var jobId in jobIds)\n                                {\n                                    JobData jobData;\n                                    StateData stateData;\n\n                                    using (var connection = Storage.GetReadOnlyConnection())\n                                    {\n                                        jobData = connection.GetJobData(jobId);\n                                        stateData = connection.GetStateData(jobId);\n                                    }\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <tr class=\\\"js-jobs-list-row \");\n\n\n            \n            #line 109 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                                            Write(jobData != null ? \"hover\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 110 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                         if (!IsReadOnly)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <td>\\r\\n                               \" +\n\"                 <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-checkbox\\\" name=\\\"jobs\" +\n\"[]\\\" value=\\\"\");\n\n\n            \n            #line 113 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                                                                                                     Write(jobId);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"/>\\r\\n                                            </td>\\r\\n\");\n\n\n            \n            #line 115 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td class=\\\"min-width\\\">\\r\\n                 \" +\n\"                           \");\n\n\n            \n            #line 117 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                       Write(Html.JobIdLink(jobId));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                        </td>\\r\\n\");\n\n\n            \n            #line 119 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                         if (jobData == null)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <td colspan=\\\"5\\\"><em>Job expired.</em>\" +\n\"</td>\\r\\n\");\n\n\n            \n            #line 122 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                        }\n                                        else\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <td class=\\\"min-width\\\">\\r\\n             \" +\n\"                                   \");\n\n\n            \n            #line 126 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                           Write(Html.StateLabel(jobData.State));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                            </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                            <td class=\\\"word-break\\\">\\r\\n            \" +\n\"                                    \");\n\n\n            \n            #line 129 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                           Write(Html.JobNameLink(jobId, jobData.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                            </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                            <td>\\r\\n                               \" +\n\"                 \");\n\n\n            \n            #line 132 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                            Write(stateData?.Reason);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                            </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                            <td class=\\\"align-right\\\">\\r\\n\");\n\n\n            \n            #line 135 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                                 if (stateData != null && stateData.Data.ContainsKey(\"EnqueueAt\"))\n                                                {\n                                                    \n            \n            #line default\n            #line hidden\n            \n            #line 137 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                               Write(Html.RelativeTime(JobHelper.DeserializeDateTime(stateData.Data[\"EnqueueAt\"])));\n\n            \n            #line default\n            #line hidden\n            \n            #line 137 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                                                                                                                  \n                                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                            <td class=\\\"align-right\\\">\\r\\n           \" +\n\"                                     \");\n\n\n            \n            #line 141 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                           Write(Html.RelativeTime(jobData.CreatedAt));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                            </td>\\r\\n\");\n\n\n            \n            #line 143 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </tr>\\r\\n\");\n\n\n            \n            #line 145 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            </tbody>\\r\\n                        </table>\\r\\n         \" +\n\"           </div>\\r\\n\\r\\n                    \");\n\n\n            \n            #line 150 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n               Write(Html.Paginator(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n\");\n\n\n            \n            #line 152 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"        </div>\\r\\n    </div>\\r\\n\");\n\n\n            \n            #line 155 \"..\\..\\Dashboard\\Pages\\RetriesPage.cshtml\"\n}\n            \n            #line default\n            #line hidden\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/ScheduledJobsPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using Hangfire\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.ScheduledJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.ScheduledCount());\n    var scheduledJobs = monitor.ScheduledJobs(pager.FromRecord, pager.RecordsPerPage);\n}\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        @Html.JobsSidebar()\n    </div>\n    <div class=\"col-md-9\">\n        <h1 id=\"page-title\" class=\"page-header\">@Strings.ScheduledJobsPage_Title</h1>\n\n        @if (pager.TotalPageCount == 0)\n        {\n            <div class=\"alert alert-info\">\n                @Strings.ScheduledJobsPage_NoJobs\n            </div>\n        }\n        else\n        {\n            <div class=\"js-jobs-list\">\n                <div class=\"btn-toolbar btn-toolbar-top\">\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-primary\"\n                                data-url=\"@Url.To(\"/jobs/scheduled/enqueue\")\"\n                                data-loading-text=\"@Strings.Common_Enqueueing\"\n                                disabled=\"disabled\">\n                            <span class=\"glyphicon glyphicon-play\"></span>\n                            @Strings.ScheduledJobsPage_EnqueueNow\n                        </button>\n                    }\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-default\"\n                                data-url=\"@Url.To(\"/jobs/scheduled/delete\")\"\n                                data-loading-text=\"@Strings.Common_Deleting\"\n                                data-confirm=\"@Strings.Common_DeleteConfirm\"\n                                disabled=\"disabled\">\n                            <span class=\"glyphicon glyphicon-remove\"></span>\n                            @Strings.Common_DeleteSelected\n                        </button>\n                    }\n                    @Html.PerPageSelector(pager)\n                </div>\n\n                <div class=\"table-responsive\">\n                    <table class=\"table\" aria-describedby=\"page-title\">\n                        <thead>\n                            <tr>\n                                @if (!IsReadOnly)\n                                {\n                                    <th class=\"min-width\">\n                                        <input type=\"checkbox\" class=\"js-jobs-list-select-all\"/>\n                                    </th>\n                                }\n                                <th class=\"min-width\">@Strings.Common_Id</th>\n                                <th>@Strings.ScheduledJobsPage_Table_Enqueue</th>\n                                <th>@Strings.Common_Job</th>\n                                <th class=\"align-right\">@Strings.ScheduledJobsPage_Table_Scheduled</th>\n                            </tr>\n                        </thead>\n                        @foreach (var job in scheduledJobs)\n                        {\n                            <tr class=\"js-jobs-list-row @(job.Value == null || !job.Value.InScheduledState ? \"obsolete-data\" : null) @(job.Value != null && job.Value.InScheduledState ? \"hover\" : null)\">\n                                @if (!IsReadOnly)\n                                {\n                                    <td>\n                                        @if (job.Value != null && job.Value.InScheduledState)\n                                        {\n                                            <input type=\"checkbox\" class=\"js-jobs-list-checkbox\" name=\"jobs[]\" value=\"@job.Key\"/>\n                                        }\n                                    </td>\n                                }\n                                <td class=\"min-width\">\n                                    @Html.JobIdLink(job.Key)\n                                    @if (job.Value != null && !job.Value.InScheduledState)\n                                    {\n                                        <span title=\"@Strings.Common_JobStateChanged_Text\" class=\"glyphicon glyphicon-question-sign\"></span>\n                                    }\n                                </td>\n                                @if (job.Value == null)\n                                {\n                                    <td colspan=\"3\"><em>@Strings.Common_JobExpired</em></td>\n                                }\n                                else\n                                {\n                                    <td class=\"min-width\">\n                                        @Html.RelativeTime(job.Value.EnqueueAt)\n                                    </td>\n                                    <td class=\"word-break\">\n                                        @Html.JobNameLink(job.Key, job.Value.Job)\n                                    </td>\n                                    <td class=\"align-right\">\n                                        @if (job.Value.ScheduledAt.HasValue)\n                                        {\n                                            @Html.RelativeTime(job.Value.ScheduledAt.Value)\n                                        }\n                                    </td>\n                                }\n                            </tr>\n                        }\n                    </table>\n                </div>\n\n                @Html.Paginator(pager)\n            </div>\n        }\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/ScheduledJobsPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class ScheduledJobsPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n            \n            #line 7 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n  \n    Layout = new LayoutPage(Strings.ScheduledJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.ScheduledCount());\n    var scheduledJobs = monitor.ScheduledJobs(pager.FromRecord, pager.RecordsPerPage);\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-3\\\">\\r\\n        \");\n\n\n            \n            #line 22 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n   Write(Html.JobsSidebar());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"col-md-9\\\">\\r\\n        <h1 id=\\\"page-title\\\" class=\\\"page\" +\n\"-header\\\">\");\n\n\n            \n            #line 25 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                           Write(Strings.ScheduledJobsPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 27 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n         if (pager.TotalPageCount == 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-info\\\">\\r\\n                \");\n\n\n            \n            #line 30 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n           Write(Strings.ScheduledJobsPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 32 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n        }\n        else\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"js-jobs-list\\\">\\r\\n                <div class=\\\"btn-toolbar b\" +\n\"tn-toolbar-top\\\">\\r\\n\");\n\n\n            \n            #line 37 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-primar\" +\n\"y\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 40 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                     Write(Url.To(\"/jobs/scheduled/enqueue\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 41 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                              Write(Strings.Common_Enqueueing);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                disabled=\\\"disabled\\\">\\r\\n                        \" +\n\"    <span class=\\\"glyphicon glyphicon-play\\\"></span>\\r\\n                            \" +\n\"\");\n\n\n            \n            #line 44 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                       Write(Strings.ScheduledJobsPage_EnqueueNow);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 46 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\n\n            \n            #line 47 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-defaul\" +\n\"t\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 50 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                     Write(Url.To(\"/jobs/scheduled/delete\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 51 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                              Write(Strings.Common_Deleting);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-confirm=\\\"\");\n\n\n            \n            #line 52 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                         Write(Strings.Common_DeleteConfirm);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                disabled=\\\"disabled\\\">\\r\\n                        \" +\n\"    <span class=\\\"glyphicon glyphicon-remove\\\"></span>\\r\\n                          \" +\n\"  \");\n\n\n            \n            #line 55 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                       Write(Strings.Common_DeleteSelected);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 57 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    \");\n\n\n            \n            #line 58 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n               Write(Html.PerPageSelector(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n\\r\\n                <div class=\\\"table-responsive\\\">\\r\\n     \" +\n\"               <table class=\\\"table\\\" aria-describedby=\\\"page-title\\\">\\r\\n            \" +\n\"            <thead>\\r\\n                            <tr>\\r\\n\");\n\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                 if (!IsReadOnly)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-select-all\\\"/>\\r\\n   \" +\n\"                                 </th>\\r\\n\");\n\n\n            \n            #line 70 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 71 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                                 Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th>\");\n\n\n            \n            #line 72 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                               Write(Strings.ScheduledJobsPage_Table_Enqueue);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th>\");\n\n\n            \n            #line 73 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                               Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 74 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                                   Write(Strings.ScheduledJobsPage_Table_Scheduled);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            </tr>\\r\\n                        </thead>\\r\\n\");\n\n\n            \n            #line 77 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                         foreach (var job in scheduledJobs)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <tr class=\\\"js-jobs-list-row \");\n\n\n            \n            #line 79 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                                    Write(job.Value == null || !job.Value.InScheduledState ? \"obsolete-data\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 79 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                                                                                                                 Write(job.Value != null && job.Value.InScheduledState ? \"hover\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 80 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                 if (!IsReadOnly)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td>\\r\\n\");\n\n\n            \n            #line 83 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                         if (job.Value != null && job.Value.InScheduledState)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <input type=\\\"checkbox\\\" class=\\\"js-jobs\" +\n\"-list-checkbox\\\" name=\\\"jobs[]\\\" value=\\\"\");\n\n\n            \n            #line 85 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                                                                                                 Write(job.Key);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"/>\\r\\n\");\n\n\n            \n            #line 86 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\");\n\n\n            \n            #line 88 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <td class=\\\"min-width\\\">\\r\\n                         \" +\n\"           \");\n\n\n            \n            #line 90 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                               Write(Html.JobIdLink(job.Key));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 91 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                     if (job.Value != null && !job.Value.InScheduledState)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <span title=\\\"\");\n\n\n            \n            #line 93 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                                Write(Strings.Common_JobStateChanged_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"glyphicon glyphicon-question-sign\\\"></span>\\r\\n\");\n\n\n            \n            #line 94 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </td>\\r\\n\");\n\n\n            \n            #line 96 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                 if (job.Value == null)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td colspan=\\\"3\\\"><em>\");\n\n\n            \n            #line 98 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                                   Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 99 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                }\n                                else\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   \");\n\n\n            \n            #line 103 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                   Write(Html.RelativeTime(job.Value.EnqueueAt));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                    </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                    <td class=\\\"word-break\\\">\\r\\n                    \" +\n\"                    \");\n\n\n            \n            #line 106 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                   Write(Html.JobNameLink(job.Key, job.Value.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                    </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                    <td class=\\\"align-right\\\">\\r\\n\");\n\n\n            \n            #line 109 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                         if (job.Value.ScheduledAt.HasValue)\n                                        {\n                                            \n            \n            #line default\n            #line hidden\n            \n            #line 111 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                       Write(Html.RelativeTime(job.Value.ScheduledAt.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 111 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                                                                           \n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\");\n\n\n            \n            #line 114 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            </tr>\\r\\n\");\n\n\n            \n            #line 116 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    </table>\\r\\n                </div>\\r\\n\\r\\n                \");\n\n\n            \n            #line 120 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n           Write(Html.Paginator(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 122 \"..\\..\\Dashboard\\Pages\\ScheduledJobsPage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/ServersPage.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using System\n@using System.Linq\n@using Hangfire.Common\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.ServersPage_Title);\n    \n    var monitor = Storage.GetMonitoringApi();\n    var servers = monitor.Servers();\n    var now = StorageUtcNow ?? ApplicationUtcNow;\n    var inconclusiveThreshold = DashboardOptions.ServerPossiblyAbortedThreshold;\n    var possiblyAbortedThreshold = TimeSpan.FromSeconds(inconclusiveThreshold.TotalSeconds * 2);\n}\n\n<div class=\"row\">\n    <div class=\"col-md-12\">\n        <h1 id=\"page-title\" class=\"page-header\">@Strings.ServersPage_Title</h1>\n\n        @if (servers.Count == 0)\n        {\n            <div class=\"alert alert-warning\">\n                @Strings.ServersPage_NoServers\n            </div>\n        }\n        else\n        {\n            if (servers.Any(x => x.Heartbeat.HasValue && x.Heartbeat.Value < now.Add(-possiblyAbortedThreshold)))\n            {\n                <div class=\"alert alert-info\">\n                    <h4>@Strings.ServersPage_Note_Title</h4>\n                    @Html.Raw(string.Format(Strings.ServersPage_Note_Text, Url.To(\"/jobs/processing\")))\n                </div>\n            }\n\n            <div class=\"table-responsive\">\n                <table class=\"table\" aria-describedby=\"page-title\">\n                    <thead>\n                        <tr>\n                            <th>@Strings.ServersPage_Table_Name</th>\n                            <th>@Strings.ServersPage_Table_Workers</th>\n                            <th>@Strings.ServersPage_Table_Queues</th>\n                            <th>@Strings.ServersPage_Table_Started</th>\n                            <th>@Strings.ServersPage_Table_Heartbeat</th>\n                        </tr>\n                    </thead>\n                    <tbody>\n                        @foreach (var server in servers)\n                        {\n                            <tr>\n                                <td>\n                                    @if (server.Heartbeat < now.Add(-possiblyAbortedThreshold))\n                                    {\n                                        @:<span class=\"glyphicon glyphicon-alert text-danger\" title=\"@Strings.ServersPage_Possibly_Aborted\"></span>&nbsp;@Html.ServerId(server.Name)\n                                    }\n                                    else if (server.Heartbeat < now.Add(-inconclusiveThreshold))\n                                    {\n                                        @:<span class=\"glyphicon margin-right-14p\"></span>&nbsp;@Html.ServerId(server.Name)\n                                    }\n                                    else\n                                    {\n                                        @:<span class=\"glyphicon glyphicon-ok text-success\" title=\"@Strings.ServersPage_Active\"></span>&nbsp;@Html.ServerId(server.Name)\n                                    }\n                                </td>\n                                <td>@server.WorkersCount</td>\n                                <td>@Html.Raw(String.Join(\", \", server.Queues.Select(Html.QueueLabel)))</td>\n                                <td>@Html.RelativeTime(server.StartedAt)</td>\n                                <td>\n                                    @if (server.Heartbeat.HasValue)\n                                    {\n                                        @Html.RelativeTime(server.Heartbeat.Value)\n                                    }\n                                </td>\n                            </tr>\n                        }\n                    </tbody>\n                </table>\n            </div>\n        }\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/ServersPage.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n    using System;\n    \n    #line default\n    #line hidden\n    using System.Collections.Generic;\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n    using System.Linq;\n    \n    #line default\n    #line hidden\n    using System.Text;\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n    using Hangfire.Common;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 7 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class ServersPage : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n  \n    Layout = new LayoutPage(Strings.ServersPage_Title);\n    \n    var monitor = Storage.GetMonitoringApi();\n    var servers = monitor.Servers();\n    var now = StorageUtcNow ?? ApplicationUtcNow;\n    var inconclusiveThreshold = DashboardOptions.ServerPossiblyAbortedThreshold;\n    var possiblyAbortedThreshold = TimeSpan.FromSeconds(inconclusiveThreshold.TotalSeconds * 2);\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-12\\\">\\r\\n        <h1 id=\\\"page-title\\\" cla\" +\n\"ss=\\\"page-header\\\">\");\n\n\n            \n            #line 21 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                           Write(Strings.ServersPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 23 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n         if (servers.Count == 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-warning\\\">\\r\\n                \");\n\n\n            \n            #line 26 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n           Write(Strings.ServersPage_NoServers);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 28 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n        }\n        else\n        {\n            if (servers.Any(x => x.Heartbeat.HasValue && x.Heartbeat.Value < now.Add(-possiblyAbortedThreshold)))\n            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                <div class=\\\"alert alert-info\\\">\\r\\n                    <h4>\");\n\n\n            \n            #line 34 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                   Write(Strings.ServersPage_Note_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h4>\\r\\n                    \");\n\n\n            \n            #line 35 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n               Write(Html.Raw(string.Format(Strings.ServersPage_Note_Text, Url.To(\"/jobs/processing\"))));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n\");\n\n\n            \n            #line 37 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n            }\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"table-responsive\\\">\\r\\n                <table class=\\\"table\\\" \" +\n\"aria-describedby=\\\"page-title\\\">\\r\\n                    <thead>\\r\\n                   \" +\n\"     <tr>\\r\\n                            <th>\");\n\n\n            \n            #line 43 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                           Write(Strings.ServersPage_Table_Name);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th>\");\n\n\n            \n            #line 44 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                           Write(Strings.ServersPage_Table_Workers);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th>\");\n\n\n            \n            #line 45 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                           Write(Strings.ServersPage_Table_Queues);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th>\");\n\n\n            \n            #line 46 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                           Write(Strings.ServersPage_Table_Started);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            <th>\");\n\n\n            \n            #line 47 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                           Write(Strings.ServersPage_Table_Heartbeat);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                        </tr>\\r\\n                    </thead>\\r\\n             \" +\n\"       <tbody>\\r\\n\");\n\n\n            \n            #line 51 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                         foreach (var server in servers)\n                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                            <tr>\\r\\n                                <td>\\r\\n\");\n\n\n            \n            #line 55 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                     if (server.Heartbeat < now.Add(-possiblyAbortedThreshold))\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        \");\n\nWriteLiteral(\"<span class=\\\"glyphicon glyphicon-alert text-danger\\\" title=\\\"\");\n\n\n            \n            #line 57 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                                                                                Write(Strings.ServersPage_Possibly_Aborted);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"></span>&nbsp;\");\n\n\n            \n            #line 57 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                                                                                                                                    Write(Html.ServerId(server.Name));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 58 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                    }\n                                    else if (server.Heartbeat < now.Add(-inconclusiveThreshold))\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        \");\n\nWriteLiteral(\"<span class=\\\"glyphicon margin-right-14p\\\"></span>&nbsp;\");\n\n\n            \n            #line 61 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                                                                           Write(Html.ServerId(server.Name));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 62 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                    }\n                                    else\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        \");\n\nWriteLiteral(\"<span class=\\\"glyphicon glyphicon-ok text-success\\\" title=\\\"\");\n\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                                                                              Write(Strings.ServersPage_Active);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"></span>&nbsp;\");\n\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                                                                                                                        Write(Html.ServerId(server.Name));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 66 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </td>\\r\\n                                <td>\");\n\n\n            \n            #line 68 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                               Write(server.WorkersCount);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n                                <td>\");\n\n\n            \n            #line 69 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                               Write(Html.Raw(String.Join(\", \", server.Queues.Select(Html.QueueLabel))));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n                                <td>\");\n\n\n            \n            #line 70 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                               Write(Html.RelativeTime(server.StartedAt));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</td>\\r\\n                                <td>\\r\\n\");\n\n\n            \n            #line 72 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                     if (server.Heartbeat.HasValue)\n                                    {\n                                        \n            \n            #line default\n            #line hidden\n            \n            #line 74 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                   Write(Html.RelativeTime(server.Heartbeat.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 74 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                                                                                  \n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </td>\\r\\n                            </tr>\\r\\n\");\n\n\n            \n            #line 78 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    </tbody>\\r\\n                </table>\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 82 \"..\\..\\Dashboard\\Pages\\ServersPage.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/SucceededJobs.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@\n@using System\n@using Hangfire\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Pages\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n@{\n    Layout = new LayoutPage(Strings.SucceededJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.SucceededListCount());\n    var succeededJobs = monitor.SucceededJobs(pager.FromRecord, pager.RecordsPerPage);\n}\n\n<div class=\"row\">\n    <div class=\"col-md-3\">\n        @Html.JobsSidebar()\n    </div>\n    <div class=\"col-md-9\">\n        <h1 id=\"page-title\" class=\"page-header\">@Strings.SucceededJobsPage_Title</h1>\n\n        @if (pager.TotalPageCount == 0)\n        {\n            <div class=\"alert alert-info\">\n                @Strings.SucceededJobsPage_NoJobs\n            </div>\n        }\n        else\n        {\n            <div class=\"js-jobs-list\">\n                <div class=\"btn-toolbar btn-toolbar-top\">\n                    @if (!IsReadOnly)\n                    {\n                        <button class=\"js-jobs-list-command btn btn-sm btn-primary\"\n                                data-url=\"@Url.To(\"/jobs/succeeded/requeue\")\"\n                                data-loading-text=\"@Strings.Common_Enqueueing\"\n                                disabled=\"disabled\">\n                            <span class=\"glyphicon glyphicon-repeat\"></span>\n                            @Strings.Common_RequeueJobs\n                        </button>\n                    }\n                    @Html.PerPageSelector(pager)\n                </div>\n\n                <div class=\"table-responsive\">\n                    <table class=\"table\" aria-describedby=\"page-title\">\n                        <thead>\n                            <tr>\n                                @if (!IsReadOnly)\n                                {\n                                    <th class=\"min-width\">\n                                        <input type=\"checkbox\" class=\"js-jobs-list-select-all\"/>\n                                    </th>\n                                }\n                                <th class=\"min-width\">@Strings.Common_Id</th>\n                                <th>@Strings.Common_Job</th>\n                                @if (succeededJobs.Any(x => x.Value?.StateData != null))\n                                {\n                                    <th class=\"min-width\">@Strings.SucceededJobsPage_Table_Duration</th>\n                                    <th class=\"min-width\">@Strings.SucceededJobsPage_Table_Latency</th>\n                                }\n                                else\n                                {\n                                    <th class=\"min-width\">@Strings.SucceededJobsPage_Table_TotalDuration</th>\n                                }\n                                <th class=\"align-right\">@Strings.SucceededJobsPage_Table_Succeeded</th>\n                            </tr>\n                        </thead>\n                        <tbody>\n                            @foreach (var job in succeededJobs)\n                            {\n                                <tr class=\"js-jobs-list-row @(job.Value == null || !job.Value.InSucceededState ? \"obsolete-data\" : null) @(job.Value != null && job.Value.InSucceededState ? \"hover\" : null)\">\n                                    @if (!IsReadOnly)\n                                    {\n                                        <td>\n                                            @if (job.Value != null && job.Value.InSucceededState)\n                                            {\n                                                <input type=\"checkbox\" class=\"js-jobs-list-checkbox\" name=\"jobs[]\" value=\"@job.Key\"/>\n                                            }\n                                        </td>\n                                    }\n                                    <td class=\"min-width\">\n                                        @Html.JobIdLink(job.Key)\n                                        @if (job.Value != null && !job.Value.InSucceededState)\n                                        {\n                                            <span title=\"@Strings.Common_JobStateChanged_Text\" class=\"glyphicon glyphicon-question-sign\"></span>\n                                        }\n                                    </td>\n\n                                    @if (job.Value == null)\n                                    {\n                                        if (succeededJobs.Any(x => x.Value?.StateData != null))\n                                        {\n                                            <td colspan=\"4\"><em>@Strings.Common_JobExpired</em></td>\n                                        }\n                                        else\n                                        {\n                                            <td colspan=\"3\"><em>@Strings.Common_JobExpired</em></td>\n                                        }\n                                    }\n                                    else\n                                    {\n                                        <td class=\"word-break\">\n                                            @Html.JobNameLink(job.Key, job.Value.Job)\n                                        </td>\n                                        if (job.Value.StateData != null)\n                                        {\n                                            <td class=\"min-width align-right\">\n                                                @if (job.Value.StateData.TryGetValue(\"PerformanceDuration\", out var duration))\n                                                {\n                                                    @Html.ToHumanDuration(TimeSpan.FromMilliseconds(long.Parse(duration, System.Globalization.CultureInfo.InvariantCulture)), false)\n                                                }\n                                            </td>\n                                            <td class=\"min-width align-right\">\n                                                @if (job.Value.StateData.TryGetValue(\"Latency\", out var latency))\n                                                {\n                                                    @Html.ToHumanDuration(TimeSpan.FromMilliseconds(long.Parse(latency, System.Globalization.CultureInfo.InvariantCulture)), false)\n                                                }\n                                            </td>\n                                        }\n                                        else\n                                        {\n                                            <td class=\"min-width align-right\">\n                                                @if (job.Value.TotalDuration.HasValue)\n                                                {\n                                                    @Html.ToHumanDuration(TimeSpan.FromMilliseconds(job.Value.TotalDuration.Value), false)\n                                                }\n                                            </td>\n                                        }\n                                        <td class=\"min-width align-right\">\n                                            @if (job.Value.SucceededAt.HasValue)\n                                            {\n                                                @Html.RelativeTime(job.Value.SucceededAt.Value)\n                                            }\n                                        </td>\n                                    }\n                                </tr>\n                            }\n                        </tbody>\n                    </table>\n                </div>\n\n                @Html.Paginator(pager)\n            </div>\n        }\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/SucceededJobs.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n    using System;\n    \n    #line default\n    #line hidden\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n    using Hangfire;\n    \n    #line default\n    #line hidden\n    \n    #line 4 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 5 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n    using Hangfire.Dashboard.Pages;\n    \n    #line default\n    #line hidden\n    \n    #line 6 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class SucceededJobs : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n\n\n\n            \n            #line 8 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n  \n    Layout = new LayoutPage(Strings.SucceededJobsPage_Title);\n\n    int from, perPage;\n\n    int.TryParse(Query(\"from\"), out from);\n    int.TryParse(Query(\"count\"), out perPage);\n\n    var monitor = Storage.GetMonitoringApi();\n    var pager = new Pager(from, perPage, DashboardOptions.DefaultRecordsPerPage, monitor.SucceededListCount());\n    var succeededJobs = monitor.SucceededJobs(pager.FromRecord, pager.RecordsPerPage);\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n<div class=\\\"row\\\">\\r\\n    <div class=\\\"col-md-3\\\">\\r\\n        \");\n\n\n            \n            #line 23 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n   Write(Html.JobsSidebar());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"col-md-9\\\">\\r\\n        <h1 id=\\\"page-title\\\" class=\\\"page\" +\n\"-header\\\">\");\n\n\n            \n            #line 26 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                           Write(Strings.SucceededJobsPage_Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</h1>\\r\\n\\r\\n\");\n\n\n            \n            #line 28 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n         if (pager.TotalPageCount == 0)\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"alert alert-info\\\">\\r\\n                \");\n\n\n            \n            #line 31 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n           Write(Strings.SucceededJobsPage_NoJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 33 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n        }\n        else\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <div class=\\\"js-jobs-list\\\">\\r\\n                <div class=\\\"btn-toolbar b\" +\n\"tn-toolbar-top\\\">\\r\\n\");\n\n\n            \n            #line 38 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                     if (!IsReadOnly)\n                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <button class=\\\"js-jobs-list-command btn btn-sm btn-primar\" +\n\"y\\\"\\r\\n                                data-url=\\\"\");\n\n\n            \n            #line 41 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                     Write(Url.To(\"/jobs/succeeded/requeue\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                data-loading-text=\\\"\");\n\n\n            \n            #line 42 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                              Write(Strings.Common_Enqueueing);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"\\r\\n                                disabled=\\\"disabled\\\">\\r\\n                        \" +\n\"    <span class=\\\"glyphicon glyphicon-repeat\\\"></span>\\r\\n                          \" +\n\"  \");\n\n\n            \n            #line 45 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                       Write(Strings.Common_RequeueJobs);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </button>\\r\\n\");\n\n\n            \n            #line 47 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                    \");\n\n\n            \n            #line 48 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n               Write(Html.PerPageSelector(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                </div>\\r\\n\\r\\n                <div class=\\\"table-responsive\\\">\\r\\n     \" +\n\"               <table class=\\\"table\\\" aria-describedby=\\\"page-title\\\">\\r\\n            \" +\n\"            <thead>\\r\\n                            <tr>\\r\\n\");\n\n\n            \n            #line 55 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                 if (!IsReadOnly)\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   <input type=\\\"checkbox\\\" class=\\\"js-jobs-list-select-all\\\"/>\\r\\n   \" +\n\"                                 </th>\\r\\n\");\n\n\n            \n            #line 60 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 61 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                 Write(Strings.Common_Id);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                                <th>\");\n\n\n            \n            #line 62 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                               Write(Strings.Common_Job);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n\");\n\n\n            \n            #line 63 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                 if (succeededJobs.Any(x => x.Value?.StateData != null))\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 65 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                     Write(Strings.SucceededJobsPage_Table_Duration);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n\");\n\n\n\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 66 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                     Write(Strings.SucceededJobsPage_Table_Latency);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n\");\n\n\n            \n            #line 67 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                }\n                                else\n                                {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <th class=\\\"min-width\\\">\");\n\n\n            \n            #line 70 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                     Write(Strings.SucceededJobsPage_Table_TotalDuration);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n\");\n\n\n            \n            #line 71 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <th class=\\\"align-right\\\">\");\n\n\n            \n            #line 72 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                   Write(Strings.SucceededJobsPage_Table_Succeeded);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</th>\\r\\n                            </tr>\\r\\n                        </thead>\\r\\n     \" +\n\"                   <tbody>\\r\\n\");\n\n\n            \n            #line 76 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                             foreach (var job in succeededJobs)\n                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                <tr class=\\\"js-jobs-list-row \");\n\n\n            \n            #line 78 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                        Write(job.Value == null || !job.Value.InSucceededState ? \"obsolete-data\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 78 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                                                                                                     Write(job.Value != null && job.Value.InSucceededState ? \"hover\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n\");\n\n\n            \n            #line 79 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                     if (!IsReadOnly)\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td>\\r\\n\");\n\n\n            \n            #line 82 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                             if (job.Value != null && job.Value.InSucceededState)\n                                            {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                                <input type=\\\"checkbox\\\" class=\\\"js-\" +\n\"jobs-list-checkbox\\\" name=\\\"jobs[]\\\" value=\\\"\");\n\n\n            \n            #line 84 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                                                                                     Write(job.Key);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"/>\\r\\n\");\n\n\n            \n            #line 85 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n            \n            #line 87 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    <td class=\\\"min-width\\\">\\r\\n                     \" +\n\"                   \");\n\n\n            \n            #line 89 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                   Write(Html.JobIdLink(job.Key));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 90 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                         if (job.Value != null && !job.Value.InSucceededState)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <span title=\\\"\");\n\n\n            \n            #line 92 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                    Write(Strings.Common_JobStateChanged_Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"glyphicon glyphicon-question-sign\\\"></span>\\r\\n\");\n\n\n            \n            #line 93 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                    </td>\\r\\n\\r\\n\");\n\n\n            \n            #line 96 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                     if (job.Value == null)\n                                    {\n                                        if (succeededJobs.Any(x => x.Value?.StateData != null))\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <td colspan=\\\"4\\\"><em>\");\n\n\n            \n            #line 100 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                           Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 101 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                        }\n                                        else\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <td colspan=\\\"3\\\"><em>\");\n\n\n            \n            #line 104 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                           Write(Strings.Common_JobExpired);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</em></td>\\r\\n\");\n\n\n            \n            #line 105 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                        }\n                                    }\n                                    else\n                                    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td class=\\\"word-break\\\">\\r\\n                \" +\n\"                            \");\n\n\n            \n            #line 110 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                       Write(Html.JobNameLink(job.Key, job.Value.Job));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                                        </td>\\r\\n\");\n\n\n            \n            #line 112 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                        if (job.Value.StateData != null)\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <td class=\\\"min-width align-right\\\">\\r\\n\");\n\n\n            \n            #line 115 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                 if (job.Value.StateData.TryGetValue(\"PerformanceDuration\", out var duration))\n                                                {\n                                                    \n            \n            #line default\n            #line hidden\n            \n            #line 117 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                               Write(Html.ToHumanDuration(TimeSpan.FromMilliseconds(long.Parse(duration, System.Globalization.CultureInfo.InvariantCulture)), false));\n\n            \n            #line default\n            #line hidden\n            \n            #line 117 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                                                                                                                                                    \n                                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            </td>\\r\\n\");\n\n\n\nWriteLiteral(\"                                            <td class=\\\"min-width align-right\\\">\\r\\n\");\n\n\n            \n            #line 121 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                 if (job.Value.StateData.TryGetValue(\"Latency\", out var latency))\n                                                {\n                                                    \n            \n            #line default\n            #line hidden\n            \n            #line 123 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                               Write(Html.ToHumanDuration(TimeSpan.FromMilliseconds(long.Parse(latency, System.Globalization.CultureInfo.InvariantCulture)), false));\n\n            \n            #line default\n            #line hidden\n            \n            #line 123 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                                                                                                                                                   \n                                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            </td>\\r\\n\");\n\n\n            \n            #line 126 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                        }\n                                        else\n                                        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            <td class=\\\"min-width align-right\\\">\\r\\n\");\n\n\n            \n            #line 130 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                 if (job.Value.TotalDuration.HasValue)\n                                                {\n                                                    \n            \n            #line default\n            #line hidden\n            \n            #line 132 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                               Write(Html.ToHumanDuration(TimeSpan.FromMilliseconds(job.Value.TotalDuration.Value), false));\n\n            \n            #line default\n            #line hidden\n            \n            #line 132 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                                                                                                          \n                                                }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                            </td>\\r\\n\");\n\n\n            \n            #line 135 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        <td class=\\\"min-width align-right\\\">\\r\\n\");\n\n\n            \n            #line 137 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                             if (job.Value.SucceededAt.HasValue)\n                                            {\n                                                \n            \n            #line default\n            #line hidden\n            \n            #line 139 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                           Write(Html.RelativeTime(job.Value.SucceededAt.Value));\n\n            \n            #line default\n            #line hidden\n            \n            #line 139 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                                                                               \n                                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                        </td>\\r\\n\");\n\n\n            \n            #line 142 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                                </tr>\\r\\n\");\n\n\n            \n            #line 144 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n                            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        </tbody>\\r\\n                    </table>\\r\\n                <\" +\n\"/div>\\r\\n\\r\\n                \");\n\n\n            \n            #line 149 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n           Write(Html.Paginator(pager));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n            </div>\\r\\n\");\n\n\n            \n            #line 151 \"..\\..\\Dashboard\\Pages\\SucceededJobs.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_BlockMetric.cs",
    "content": "﻿namespace Hangfire.Dashboard.Pages\n{\n    partial class BlockMetric\n    {\n        public BlockMetric(DashboardMetric dashboardMetric)\n        {\n            DashboardMetric = dashboardMetric;\n        }\n\n        public DashboardMetric DashboardMetric { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_BlockMetric.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Resources\n@inherits RazorPage\n@{\n    var metric = DashboardMetric.Func(this);\n    var className = metric == null ? \"metric-null\" : metric.Style.ToClassName();\n    var highlighted = metric != null && metric.Highlighted ? \"highlighted\" : null;\n }\n\n@if (!string.IsNullOrEmpty(DashboardMetric.Url))\n{\n@:<a href=\"@Html.HtmlEncode(DashboardMetric.Url)\" class=\"clickable-metric\">\n}\n<div class=\"metric @className @highlighted\">\n    <div class=\"metric-body\" data-metric=\"@DashboardMetric.Name\">\n        @(metric?.Value)\n    </div>\n    <div class=\"metric-description\">\n        @(Strings.ResourceManager.GetString(DashboardMetric.Title) ?? DashboardMetric.Title)\n    </div>\n</div>\n@if (!string.IsNullOrEmpty(DashboardMetric.Url))\n{\n@:</a>\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_BlockMetric.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class BlockMetric : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n\n            \n            #line 5 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n  \n    var metric = DashboardMetric.Func(this);\n    var className = metric == null ? \"metric-null\" : metric.Style.ToClassName();\n    var highlighted = metric != null && metric.Highlighted ? \"highlighted\" : null;\n \n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 11 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n if (!string.IsNullOrEmpty(DashboardMetric.Url))\n{\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"<a href=\\\"\");\n\n\n            \n            #line 13 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n      Write(Html.HtmlEncode(DashboardMetric.Url));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"clickable-metric\\\">\\r\\n\");\n\n\n            \n            #line 14 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n}\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"<div class=\\\"metric \");\n\n\n            \n            #line 15 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n              Write(className);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 15 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n                         Write(highlighted);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n    <div class=\\\"metric-body\\\" data-metric=\\\"\");\n\n\n            \n            #line 16 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n                                     Write(DashboardMetric.Name);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n        \");\n\n\n            \n            #line 17 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n    Write(metric?.Value);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n    <div class=\\\"metric-description\\\">\\r\\n        \");\n\n\n            \n            #line 20 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n    Write(Strings.ResourceManager.GetString(DashboardMetric.Title) ?? DashboardMetric.Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n</div>\\r\\n\");\n\n\n            \n            #line 23 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n if (!string.IsNullOrEmpty(DashboardMetric.Url))\n{\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</a>\\r\\n\");\n\n\n            \n            #line 26 \"..\\..\\Dashboard\\Pages\\_BlockMetric.cshtml\"\n}\n            \n            #line default\n            #line hidden\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_Breadcrumbs.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Hangfire.Dashboard.Pages\n{\n    partial class Breadcrumbs\n    {\n        public Breadcrumbs(string title, IDictionary<string, string> items)\n        {\n            Title = title;\n            Items = items;\n        }\n\n        public string Title { get; }\n        public IDictionary<string, string> Items { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_Breadcrumbs.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@\n@using Hangfire.Dashboard\n@inherits RazorPage\n\n<ol class=\"breadcrumb\">\n    <li><a href=\"@Url.Home()\"><span class=\"glyphicon glyphicon-home\"></span></a></li>\n    @foreach (var item in Items)\n    {\n        <li><a href=\"@item.Value\">@item.Key</a></li>\n    }\n    <li class=\"active\">@Title</li>\n</ol>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_Breadcrumbs.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\_Breadcrumbs.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class Breadcrumbs : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\nWriteLiteral(\"\\r\\n<ol class=\\\"breadcrumb\\\">\\r\\n    <li><a href=\\\"\");\n\n\n            \n            #line 6 \"..\\..\\Dashboard\\Pages\\_Breadcrumbs.cshtml\"\n            Write(Url.Home());\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\"><span class=\\\"glyphicon glyphicon-home\\\"></span></a></li>\\r\\n\");\n\n\n            \n            #line 7 \"..\\..\\Dashboard\\Pages\\_Breadcrumbs.cshtml\"\n     foreach (var item in Items)\n    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"        <li><a href=\\\"\");\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\_Breadcrumbs.cshtml\"\n                Write(item.Value);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\");\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\_Breadcrumbs.cshtml\"\n                             Write(item.Key);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</a></li>\\r\\n\");\n\n\n            \n            #line 10 \"..\\..\\Dashboard\\Pages\\_Breadcrumbs.cshtml\"\n    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    <li class=\\\"active\\\">\");\n\n\n            \n            #line 11 \"..\\..\\Dashboard\\Pages\\_Breadcrumbs.cshtml\"\n                  Write(Title);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</li>\\r\\n</ol>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_ErrorAlert.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@\n@using Hangfire.Dashboard\n@inherits RazorPage\n\n<div id=\"errorAlert\" class=\"alert alert-danger alert-fixed display-none\" role=\"alert\">\n    <div class=\"container\">\n        <strong id=\"errorAlertTitle\"></strong> \n        <span id=\"errorAlertMessage\"></span>\n    </div>\n</div>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_ErrorAlert.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\_ErrorAlert.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class ErrorAlert : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\nWriteLiteral(\"\\r\\n<div id=\\\"errorAlert\\\" class=\\\"alert alert-danger alert-fixed display-none\\\" role=\\\"\" +\n\"alert\\\">\\r\\n    <div class=\\\"container\\\">\\r\\n        <strong id=\\\"errorAlertTitle\\\"></str\" +\n\"ong> \\r\\n        <span id=\\\"errorAlertMessage\\\"></span>\\r\\n    </div>\\r\\n</div>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_InlineMetric.cs",
    "content": "﻿namespace Hangfire.Dashboard.Pages\n{\n    partial class InlineMetric\n    {\n        public InlineMetric(DashboardMetric dashboardMetric)\n        {\n            DashboardMetric = dashboardMetric;\n        }\n\n        public DashboardMetric DashboardMetric { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_InlineMetric.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@\n@using Hangfire.Dashboard\n@inherits RazorPage\n@{\n    var metric = DashboardMetric.Func(this);\n    var className = metric == null ? \"metric-null\" : metric.Style.ToClassName();\n    var highlighted = metric != null && metric.Highlighted ? \"highlighted\" : null;\n}\n<span data-metric=\"@DashboardMetric.Name\" class=\"metric @className @highlighted\">@(metric?.Value)</span>"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_InlineMetric.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\_InlineMetric.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class InlineMetric : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n            \n            #line 4 \"..\\..\\Dashboard\\Pages\\_InlineMetric.cshtml\"\n  \n    var metric = DashboardMetric.Func(this);\n    var className = metric == null ? \"metric-null\" : metric.Style.ToClassName();\n    var highlighted = metric != null && metric.Highlighted ? \"highlighted\" : null;\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"<span data-metric=\\\"\");\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\_InlineMetric.cshtml\"\n              Write(DashboardMetric.Name);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"metric \");\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\_InlineMetric.cshtml\"\n                                                   Write(className);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\_InlineMetric.cshtml\"\n                                                              Write(highlighted);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\");\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\_InlineMetric.cshtml\"\n                                                                             Write(metric?.Value);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</span>\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_Navigation.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@\n@using Hangfire.Dashboard\n@inherits RazorPage\n@if (NavigationMenu.Items.Count > 0)\n{\n    <ul class=\"nav navbar-nav\">\n        @foreach (var item in NavigationMenu.Items)\n        {\n            var itemValue = item(this);\n\n            if (itemValue == null) { continue; }\n\n            <li class=\"@(itemValue.Active ? \"active\" : null)\">\n                <a href=\"@itemValue.Url\">\n                    @itemValue.Text\n\n                    @foreach (var metric in itemValue.GetAllMetrics())\n                    {\n                        @Html.InlineMetric(metric)\n                    }\n                </a>\n            </li>\n        }\n    </ul>\n}"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_Navigation.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class Navigation : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n            \n            #line 4 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n if (NavigationMenu.Items.Count > 0)\n{\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    <ul class=\\\"nav navbar-nav\\\">\\r\\n\");\n\n\n            \n            #line 7 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n         foreach (var item in NavigationMenu.Items)\n        {\n            var itemValue = item(this);\n\n            if (itemValue == null) { continue; }\n\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <li class=\\\"\");\n\n\n            \n            #line 13 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n                   Write(itemValue.Active ? \"active\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                <a href=\\\"\");\n\n\n            \n            #line 14 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n                    Write(itemValue.Url);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                    \");\n\n\n            \n            #line 15 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n               Write(itemValue.Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n\\r\\n\");\n\n\n            \n            #line 17 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n                     foreach (var metric in itemValue.GetAllMetrics())\n                    {\n                        \n            \n            #line default\n            #line hidden\n            \n            #line 19 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n                   Write(Html.InlineMetric(metric));\n\n            \n            #line default\n            #line hidden\n            \n            #line 19 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n                                                  \n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                </a>\\r\\n            </li>\\r\\n\");\n\n\n            \n            #line 23 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </ul>\\r\\n\");\n\n\n            \n            #line 25 \"..\\..\\Dashboard\\Pages\\_Navigation.cshtml\"\n}\n            \n            #line default\n            #line hidden\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_Paginator.cs",
    "content": "﻿namespace Hangfire.Dashboard.Pages\n{\n    partial class Paginator\n    {\n        private readonly Pager _pager;\n\n        public Paginator(Pager pager)\n        {\n            _pager = pager;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_Paginator.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@\n@using Hangfire.Dashboard\n@using Hangfire.Dashboard.Resources;\n\n@inherits RazorPage\n<div class=\"btn-toolbar\">\n    @if (_pager.TotalPageCount > 1)\n    {\n        <div class=\"btn-group paginator\">\n            @foreach (var page in _pager.PagerItems)\n            {\n                switch (page.Type)\n                {\n                    case Pager.ItemType.Page:\n                        <a href=\"@_pager.PageUrl(page.PageIndex)\" class=\"btn btn-default @(_pager.CurrentPage == page.PageIndex ? \"active\" : null)\">\n                            @page.PageIndex  \n                        </a>\n                        break;\n                    case Pager.ItemType.NextPage:\n                        <a href=\"@_pager.PageUrl(page.PageIndex)\" class=\"btn btn-default @(page.Disabled ? \"disabled\" : null)\">\n                            @Strings.Paginator_Next\n                        </a>\n                        break;\n                    case Pager.ItemType.PrevPage:\n                        <a href=\"@_pager.PageUrl(page.PageIndex)\" class=\"btn btn-default @(page.Disabled ? \"disabled\" : null)\">\n                            @Strings.Paginator_Prev\n                        </a>\n                        break;\n                    case Pager.ItemType.MorePage:\n                        <a href=\"#\" class=\"btn btn-default disabled\">\n                            …\n                        </a>\n                        break;\n                }\n            }\n        </div>\n        <div class=\"btn-toolbar-spacer\"></div>\n    }\n\n    <div class=\"btn-toolbar-label\">\n        @Strings.Paginator_TotalItems: @_pager.TotalRecordCount\n    </div>\n</div>\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_Paginator.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    #line 3 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class Paginator : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\nWriteLiteral(\"\\r\\n\");\n\n\nWriteLiteral(\"<div class=\\\"btn-toolbar\\\">\\r\\n\");\n\n\n            \n            #line 7 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n     if (_pager.TotalPageCount > 1)\n    {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"        <div class=\\\"btn-group paginator\\\">\\r\\n\");\n\n\n            \n            #line 10 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n             foreach (var page in _pager.PagerItems)\n            {\n                switch (page.Type)\n                {\n                    case Pager.ItemType.Page:\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <a href=\\\"\");\n\n\n            \n            #line 15 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                            Write(_pager.PageUrl(page.PageIndex));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"btn btn-default \");\n\n\n            \n            #line 15 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                                                                                     Write(_pager.CurrentPage == page.PageIndex ? \"active\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                            \");\n\n\n            \n            #line 16 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                       Write(page.PageIndex);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"  \\r\\n                        </a>\\r\\n\");\n\n\n            \n            #line 18 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                        break;\n                    case Pager.ItemType.NextPage:\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <a href=\\\"\");\n\n\n            \n            #line 20 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                            Write(_pager.PageUrl(page.PageIndex));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"btn btn-default \");\n\n\n            \n            #line 20 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                                                                                     Write(page.Disabled ? \"disabled\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                            \");\n\n\n            \n            #line 21 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                       Write(Strings.Paginator_Next);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </a>\\r\\n\");\n\n\n            \n            #line 23 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                        break;\n                    case Pager.ItemType.PrevPage:\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <a href=\\\"\");\n\n\n            \n            #line 25 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                            Write(_pager.PageUrl(page.PageIndex));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"btn btn-default \");\n\n\n            \n            #line 25 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                                                                                     Write(page.Disabled ? \"disabled\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                            \");\n\n\n            \n            #line 26 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                       Write(Strings.Paginator_Prev);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                        </a>\\r\\n\");\n\n\n            \n            #line 28 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                        break;\n                    case Pager.ItemType.MorePage:\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                        <a href=\\\"#\\\" class=\\\"btn btn-default disabled\\\">\\r\\n          \" +\n\"                  …\\r\\n                        </a>\\r\\n\");\n\n\n            \n            #line 33 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                        break;\n                }\n            }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"        </div>\\r\\n\");\n\n\n\nWriteLiteral(\"        <div class=\\\"btn-toolbar-spacer\\\"></div>\\r\\n\");\n\n\n            \n            #line 38 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    <div class=\\\"btn-toolbar-label\\\">\\r\\n        \");\n\n\n            \n            #line 41 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n   Write(Strings.Paginator_TotalItems);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\": \");\n\n\n            \n            #line 41 \"..\\..\\Dashboard\\Pages\\_Paginator.cshtml\"\n                                  Write(_pager.TotalRecordCount);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n    </div>\\r\\n</div>\\r\\n\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_PerPageSelector.cs",
    "content": "﻿namespace Hangfire.Dashboard.Pages\n{\n    partial class PerPageSelector\n    {\n        private readonly Pager _pager;\n\n        public PerPageSelector(Pager pager)\n        {\n            _pager = pager;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_PerPageSelector.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@\n@using Hangfire.Dashboard.Resources;\n@inherits Hangfire.Dashboard.RazorPage\n\n@if (_pager.TotalPageCount > 1)\n{\n    <div class=\"btn-group paginator pull-right hidden-xs\">\n        <a href=\"@_pager.PageUrl(_pager.CurrentPage > 1 ? _pager.CurrentPage - 1 : 0)\" class=\"btn btn-default btn-sm @(_pager.CurrentPage == 1 ? \"disabled\" : null)\">\n            @Strings.Paginator_Prev\n        </a>\n        <a href=\"@_pager.PageUrl(_pager.CurrentPage < _pager.TotalPageCount ? _pager.CurrentPage + 1 : 0)\" class=\"btn btn-default btn-sm @(_pager.CurrentPage == _pager.TotalPageCount ? \"disabled\" : null)\">\n            @Strings.Paginator_Next\n        </a>\n    </div>\n}\n    <div class=\"btn-group pull-right paginator\">\n        @foreach (var count in new[] { 10, 20, 50, 100, 500, 1000, 5000 })\n        {\n            <a class=\"btn btn-sm btn-default @(count == _pager.RecordsPerPage ? \"active\" : null) @(count > 999 ? \"visible-lg\" : null)\" \n                   href=\"@_pager.RecordsPerPageUrl(count)\">@count.ToString(\"N0\")</a>    \n        }\n    </div>\n    <div class=\"btn-toolbar-spacer pull-right hidden-xs\"></div>\n    <div class=\"btn-toolbar-label btn-toolbar-label-sm pull-right hidden-xs\">\n        @Strings.PerPageSelector_ItemsPerPage:\n    </div>\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_PerPageSelector.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n    using Hangfire.Dashboard.Resources;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class PerPageSelector : Hangfire.Dashboard.RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n            \n            #line 5 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n if (_pager.TotalPageCount > 1)\n{\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    <div class=\\\"btn-group paginator pull-right hidden-xs\\\">\\r\\n        <a href=\\\"\");\n\n\n            \n            #line 8 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n            Write(_pager.PageUrl(_pager.CurrentPage > 1 ? _pager.CurrentPage - 1 : 0));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"btn btn-default btn-sm \");\n\n\n            \n            #line 8 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n                                                                                                                 Write(_pager.CurrentPage == 1 ? \"disabled\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n            \");\n\n\n            \n            #line 9 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n       Write(Strings.Paginator_Prev);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n        </a>\\r\\n        <a href=\\\"\");\n\n\n            \n            #line 11 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n            Write(_pager.PageUrl(_pager.CurrentPage < _pager.TotalPageCount ? _pager.CurrentPage + 1 : 0));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"btn btn-default btn-sm \");\n\n\n            \n            #line 11 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n                                                                                                                                     Write(_pager.CurrentPage == _pager.TotalPageCount ? \"disabled\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n            \");\n\n\n            \n            #line 12 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n       Write(Strings.Paginator_Next);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n        </a>\\r\\n    </div>\\r\\n\");\n\n\n            \n            #line 15 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n}\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    <div class=\\\"btn-group pull-right paginator\\\">\\r\\n\");\n\n\n            \n            #line 17 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n         foreach (var count in new[] { 10, 20, 50, 100, 500, 1000, 5000 })\n        {\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <a class=\\\"btn btn-sm btn-default \");\n\n\n            \n            #line 19 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n                                         Write(count == _pager.RecordsPerPage ? \"active\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\" \");\n\n\n            \n            #line 19 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n                                                                                             Write(count > 999 ? \"visible-lg\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" \\r\\n                   href=\\\"\");\n\n\n            \n            #line 20 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n                    Write(_pager.RecordsPerPageUrl(count));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\");\n\n\n            \n            #line 20 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n                                                      Write(count.ToString(\"N0\"));\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"</a>    \\r\\n\");\n\n\n            \n            #line 21 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n    <div class=\\\"btn-toolbar-spacer pull-right hidden-xs\\\"></div>\\r\\n    \" +\n\"<div class=\\\"btn-toolbar-label btn-toolbar-label-sm pull-right hidden-xs\\\">\\r\\n     \" +\n\"   \");\n\n\n            \n            #line 25 \"..\\..\\Dashboard\\Pages\\_PerPageSelector.cshtml\"\n   Write(Strings.PerPageSelector_ItemsPerPage);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\":\\r\\n    </div>\\r\\n\");\n\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_SidebarMenu.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard.Pages\n{\n    partial class SidebarMenu\n    {\n        public SidebarMenu([NotNull] IEnumerable<Func<RazorPage, MenuItem>> items)\n        {\n            if (items == null) throw new ArgumentNullException(nameof(items));\n            Items = items;\n        }\n\n        public IEnumerable<Func<RazorPage, MenuItem>> Items { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_SidebarMenu.cshtml",
    "content": "﻿@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@\n@using Hangfire.Dashboard\n@inherits RazorPage\n@if (Items.Any())\n{\n    <div id=\"stats\" class=\"list-group\">\n        @foreach (var item in Items)\n        {\n            var itemValue = item(this);\n            <a href=\"@itemValue.Url\" class=\"list-group-item @(itemValue.Active ? \"active\" : null)\">\n                @itemValue.Text\n                <span class=\"pull-right\">\n                    @foreach (var metric in itemValue.GetAllMetrics())\n                    {\n                        @Html.InlineMetric(metric)\n                    }\n                </span>\n            </a>\n        }\n    </div>\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/Pages/_SidebarMenu.cshtml.cs",
    "content": "#pragma warning disable 1591\n//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Hangfire.Dashboard.Pages\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n    \n    #line 2 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n    using Hangfire.Dashboard;\n    \n    #line default\n    #line hidden\n    \n    [System.CodeDom.Compiler.GeneratedCodeAttribute(\"RazorGenerator\", \"2.0.0.0\")]\n    internal partial class SidebarMenu : RazorPage\n    {\n#line hidden\n\n        public override void Execute()\n        {\n\n\nWriteLiteral(\"\\r\\n\");\n\n\n\n\n            \n            #line 4 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n if (Items.Any())\n{\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    <div id=\\\"stats\\\" class=\\\"list-group\\\">\\r\\n\");\n\n\n            \n            #line 7 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n         foreach (var item in Items)\n        {\n            var itemValue = item(this);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"            <a href=\\\"\");\n\n\n            \n            #line 10 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n                Write(itemValue.Url);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\" class=\\\"list-group-item \");\n\n\n            \n            #line 10 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n                                                        Write(itemValue.Active ? \"active\" : null);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\\">\\r\\n                \");\n\n\n            \n            #line 11 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n           Write(itemValue.Text);\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"\\r\\n                <span class=\\\"pull-right\\\">\\r\\n\");\n\n\n            \n            #line 13 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n                     foreach (var metric in itemValue.GetAllMetrics())\n                    {\n                        \n            \n            #line default\n            #line hidden\n            \n            #line 15 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n                   Write(Html.InlineMetric(metric));\n\n            \n            #line default\n            #line hidden\n            \n            #line 15 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n                                                  \n                    }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"                </span>\\r\\n            </a>\\r\\n\");\n\n\n            \n            #line 19 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n        }\n\n            \n            #line default\n            #line hidden\nWriteLiteral(\"    </div>\\r\\n\");\n\n\n            \n            #line 21 \"..\\..\\Dashboard\\Pages\\_SidebarMenu.cshtml\"\n}\n\n            \n            #line default\n            #line hidden\n\n        }\n    }\n}\n#pragma warning restore 1591\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/RazorPage.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Diagnostics;\nusing System.Net;\nusing System.Text;\nusing Hangfire.Storage;\nusing Hangfire.Storage.Monitoring;\n\nnamespace Hangfire.Dashboard\n{\n    public abstract class RazorPage\n    {\n        private Lazy<StatisticsDto> _statisticsLazy;\n        private Lazy<Tuple<DateTime?, DateTime, TimeSpan?>> _dateTimeLazy;\n\n        private readonly StringBuilder _content = new StringBuilder();\n        private string _body;\n\n        protected RazorPage()\n        {\n            GenerationTime = Stopwatch.StartNew();\n            Html = new HtmlHelper(this);\n        }\n\n        public RazorPage Layout { get; protected set; }\n        public HtmlHelper Html { get; private set; }\n        public UrlHelper Url { get; private set; }\n\n        public JobStorage Storage => Context.Storage;\n        public string AppPath => Context.Options.AppPath;\n        public DashboardOptions DashboardOptions => Context.Options;\n        public Stopwatch GenerationTime { get; private set; }\n\n        public DateTime? StorageUtcNow\n        {\n            get\n            {\n                if (_dateTimeLazy == null) throw new InvalidOperationException(\"Page is not initialized.\");\n                return _dateTimeLazy.Value.Item1;\n            }\n        }\n\n        public DateTime ApplicationUtcNow\n        {\n            get\n            {\n                if (_dateTimeLazy == null) throw new InvalidOperationException(\"Page is not initialized.\");\n                return _dateTimeLazy.Value.Item2;\n            }\n        }\n\n        public TimeSpan? TimeDifference\n        {\n            get\n            {\n                if (_dateTimeLazy == null) throw new InvalidOperationException(\"Page is not initialized.\");\n                return _dateTimeLazy.Value.Item3;\n            }\n        }\n\n        public StatisticsDto Statistics\n        {\n            get\n            {\n                if (_statisticsLazy == null) throw new InvalidOperationException(\"Page is not initialized.\");\n                return _statisticsLazy.Value;\n            }\n        }\n\n        public DashboardContext Context { get; private set; }\n\n        internal DashboardRequest Request => Context.Request;\n        internal DashboardResponse Response => Context.Response;\n\n        public string RequestPath => Request.Path;\n\n        public bool IsReadOnly => Context.IsReadOnly;\n        \n        /// <exclude />\n        public abstract void Execute();\n\n        public string Query(string key)\n        {\n            return Request.GetQuery(key);\n        }\n\n        public override string ToString()\n        {\n            return TransformText(null);\n        }\n\n        /// <exclude />\n        public void Assign(RazorPage parentPage)\n        {\n            Context = parentPage.Context;\n            Url = parentPage.Url;\n\n            GenerationTime = parentPage.GenerationTime;\n            _statisticsLazy = parentPage._statisticsLazy;\n            _dateTimeLazy = parentPage._dateTimeLazy;\n        }\n\n        internal void Assign(DashboardContext context)\n        {\n            Context = context;\n            Url = new UrlHelper(context);\n\n            _statisticsLazy = new Lazy<StatisticsDto>(() =>\n            {\n                var monitoring = Storage.GetMonitoringApi();\n                return monitoring.GetStatistics();\n            });\n\n            _dateTimeLazy = new Lazy<Tuple<DateTime?, DateTime, TimeSpan?>>(() =>\n            {\n                DateTime? storageUtcNow = null;\n                TimeSpan? difference = null;\n\n                if (Storage.HasFeature(JobStorageFeatures.Connection.GetUtcDateTime))\n                {\n                    using (var connection = Storage.GetReadOnlyConnection() as JobStorageConnection)\n                    {\n                        storageUtcNow = connection?.GetUtcDateTime();\n                        \n                    }\n                }\n\n                var applicationUtcNow = DateTime.UtcNow;\n\n                if (storageUtcNow.HasValue)\n                {\n                    difference = applicationUtcNow - storageUtcNow;\n                }\n\n                return new Tuple<DateTime?, DateTime, TimeSpan?>(storageUtcNow, applicationUtcNow, difference);\n            });\n        }\n\n        /// <exclude />\n        protected void WriteLiteral(string textToAppend)\n        {\n            if (string.IsNullOrEmpty(textToAppend))\n                return;\n            _content.Append(textToAppend);\n        }\n\n        /// <exclude />\n        protected virtual void Write(object value)\n        {\n            if (value == null)\n                return;\n            var html = value as NonEscapedString;\n            WriteLiteral(html?.ToString() ?? Encode(value.ToString()));\n        }\n\n        protected virtual object RenderBody()\n        {\n            return new NonEscapedString(_body);\n        }\n\n        private string TransformText(string body)\n        {\n            _body = body;\n            \n            Execute();\n            \n            if (Layout != null)\n            {\n                Layout.Assign(this);\n                return Layout.TransformText(_content.ToString());\n            }\n            \n            return _content.ToString();\n        }\n\n        private static string Encode(string text)\n        {\n            return string.IsNullOrEmpty(text)\n                       ? string.Empty\n                       : WebUtility.HtmlEncode(text);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/RazorPageDispatcher.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Text.RegularExpressions;\nusing System.Threading.Tasks;\n\nnamespace Hangfire.Dashboard\n{\n    internal sealed class RazorPageDispatcher : IDashboardDispatcher\n    {\n        private readonly Func<Match, RazorPage> _pageFunc;\n\n        public RazorPageDispatcher(Func<Match, RazorPage> pageFunc)\n        {\n            _pageFunc = pageFunc;\n        }\n\n        public Task Dispatch(DashboardContext context)\n        {\n            context.Response.ContentType = \"text/html\";\n\n            var page = _pageFunc(context.UriMatch);\n            page.Assign(context);\n\n            return context.Response.WriteAsync(page.ToString());\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/RouteCollection.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text.RegularExpressions;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard\n{\n    [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Naming\", \"CA1711:Identifiers should not have incorrect suffix\", Justification = \"Public API, can not change in minor versions.\")]\n    public class RouteCollection\n    {\n        private readonly List<Tuple<string, IDashboardDispatcher>> _dispatchers\n            = new List<Tuple<string, IDashboardDispatcher>>();\n\n#if FEATURE_OWIN\n        [Obsolete(\"Use the Add(string, IDashboardDispatcher) overload instead. Will be removed in 2.0.0.\")]\n        public void Add([NotNull] string pathTemplate, [NotNull] IRequestDispatcher dispatcher)\n        {\n            if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));\n            if (dispatcher == null) throw new ArgumentNullException(nameof(dispatcher));\n\n            _dispatchers.Add(new Tuple<string, IDashboardDispatcher>(pathTemplate, new RequestDispatcherWrapper(dispatcher)));\n        }\n#endif\n\n        public void Add([NotNull] string pathTemplate, [NotNull] IDashboardDispatcher dispatcher)\n        {\n            if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));\n            if (dispatcher == null) throw new ArgumentNullException(nameof(dispatcher));\n\n            _dispatchers.Add(new Tuple<string, IDashboardDispatcher>(pathTemplate, dispatcher));\n        }\n\n        public Tuple<IDashboardDispatcher, Match> FindDispatcher(string path)\n        {\n            if (path.Length == 0) path = \"/\";\n            else if (path.Length > 1) path = path.TrimEnd('/');\n\n            foreach (var dispatcher in _dispatchers)\n            {\n                var pattern = dispatcher.Item1;\n\n                if (!pattern.StartsWith(\"^\", StringComparison.OrdinalIgnoreCase))\n                    pattern = \"^\" + pattern;\n                if (!pattern.EndsWith(\"$\", StringComparison.OrdinalIgnoreCase))\n                    pattern += \"$\";\n\n                var match = Regex.Match(\n                    path,\n                    pattern,\n                    RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline,\n                    TimeSpan.FromSeconds(1));\n\n                if (match.Success)\n                {\n                    return new Tuple<IDashboardDispatcher, Match>(dispatcher.Item2, match);\n                }\n            }\n            \n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/RouteCollectionExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Text.RegularExpressions;\nusing Hangfire.Annotations;\nusing System.ComponentModel;\n\nnamespace Hangfire.Dashboard\n{\n    public static class RouteCollectionExtensions\n    {\n        public static void AddRazorPage(\n            [NotNull] this RouteCollection routes, \n            [NotNull] string pathTemplate, \n            [NotNull] Func<Match, RazorPage> pageFunc)\n        {\n            if (routes == null) throw new ArgumentNullException(nameof(routes));\n            if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));\n            if (pageFunc == null) throw new ArgumentNullException(nameof(pageFunc));\n\n            routes.Add(pathTemplate, new RazorPageDispatcher(pageFunc));\n        }\n\n#if FEATURE_OWIN\n        [Obsolete(\"Use the AddCommand(RouteCollection, string, Func<DashboardContext, bool>) overload instead. Will be removed in 2.0.0.\")]\n        public static void AddCommand(\n            [NotNull] this RouteCollection routes, \n            [NotNull] string pathTemplate, \n            [NotNull] Func<RequestDispatcherContext, bool> command)\n        {\n            if (routes == null) throw new ArgumentNullException(nameof(routes));\n            if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));\n            if (command == null) throw new ArgumentNullException(nameof(command));\n\n            routes.Add(pathTemplate, new CommandDispatcher(command));\n        }\n#endif\n\n        public static void AddCommand(\n            [NotNull] this RouteCollection routes,\n            [NotNull] string pathTemplate,\n            [NotNull] Func<DashboardContext, bool> command)\n        {\n            if (routes == null) throw new ArgumentNullException(nameof(routes));\n            if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));\n            if (command == null) throw new ArgumentNullException(nameof(command));\n\n            routes.Add(pathTemplate, new CommandDispatcher(command));\n        }\n\n#if FEATURE_OWIN\n        [Obsolete(\"Use the AddBatchCommand(RouteCollection, string, Func<DashboardContext, bool>) overload instead. Will be removed in 2.0.0.\")]\n        public static void AddBatchCommand(\n            [NotNull] this RouteCollection routes, \n            [NotNull] string pathTemplate, \n            [NotNull] Action<RequestDispatcherContext, string> command)\n        {\n            if (routes == null) throw new ArgumentNullException(nameof(routes));\n            if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));\n            if (command == null) throw new ArgumentNullException(nameof(command));\n\n            routes.Add(pathTemplate, new BatchCommandDispatcher(command));\n        }\n#endif\n\n        public static void AddBatchCommand(\n            [NotNull] this RouteCollection routes,\n            [NotNull] string pathTemplate,\n            [NotNull] Action<DashboardContext, string> command)\n        {\n            if (routes == null) throw new ArgumentNullException(nameof(routes));\n            if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));\n            if (command == null) throw new ArgumentNullException(nameof(command));\n\n            routes.Add(pathTemplate, new BatchCommandDispatcher(command));\n        }\n\n        public static void AddClientBatchCommand(\n            this RouteCollection routes,\n            string pathTemplate, \n            [NotNull] Action<IBackgroundJobClient, string> command)\n        {\n            if (command == null) throw new ArgumentNullException(nameof(command));\n\n            routes.AddBatchCommand(pathTemplate, (context, jobId) =>\n            {\n                var client = context.GetBackgroundJobClient();\n                command(client, jobId);\n            });\n        }\n\n        public static void AddRecurringBatchCommand(\n            this RouteCollection routes,\n            string pathTemplate,\n            [NotNull] Action<IRecurringJobManager, string> command)\n        {\n            if (command == null) throw new ArgumentNullException(nameof(command));\n\n            routes.AddBatchCommand(pathTemplate, (context, jobId) =>\n            {\n                var manager = context.GetRecurringJobManager();\n                command(manager, jobId);\n            });\n        }\n        \n        [EditorBrowsable(EditorBrowsableState.Never)]\n        [Obsolete(\"For binary compatibility only. Use overload with Action<IRecurringJobManager, string> instead.\")]\n        public static void AddRecurringBatchCommand(\n            this RouteCollection routes,\n            string pathTemplate,\n            [NotNull] Action<RecurringJobManager, string> command)\n        {\n            if (command == null) throw new ArgumentNullException(nameof(command));\n\n            routes.AddBatchCommand(pathTemplate, (context, jobId) =>\n            {\n                var manager = new RecurringJobManager(context.Storage);\n                command(manager, jobId);\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Dashboard/UrlHelper.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Dashboard\n{\n    public class UrlHelper\n    {\n#if FEATURE_OWIN\n        private readonly Microsoft.Owin.OwinContext _owinContext;\n\n        [Obsolete(\"Please use UrlHelper(DashboardContext) instead. Will be removed in 2.0.0.\")]\n        public UrlHelper([NotNull] IDictionary<string, object> owinEnvironment)\n        {\n            if (owinEnvironment == null) throw new ArgumentNullException(nameof(owinEnvironment));\n            _owinContext = new Microsoft.Owin.OwinContext(owinEnvironment);\n        }\n#endif\n\n        private readonly DashboardContext _context;\n\n        public UrlHelper([NotNull] DashboardContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n            _context = context;\n        }\n\n        public string To(string relativePath)\n        {\n            return _context.Options.PrefixPath +\n                   (\n#if FEATURE_OWIN\n                       _owinContext?.Request.PathBase.Value ??\n#endif\n                       _context.Request.PathBase\n                   ) + relativePath;\n        }\n\n        public string Home()\n        {\n            return To(\"/\");\n        }\n\n        public string JobDetails(string jobId)\n        {\n            return To(\"/jobs/details/\" + jobId);\n        }\n\n        public string LinkToQueues()\n        {\n            return To(\"/jobs/enqueued\");\n        }\n\n        public string Queue(string queue)\n        {\n            return To(\"/jobs/enqueued/\" + queue);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/DashboardOptions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\nusing Hangfire.Dashboard;\n\nnamespace Hangfire\n{\n    public class DashboardOptions\n    {\n        private static readonly IDashboardAuthorizationFilter[] DefaultAuthorization =\n            new[] { new LocalRequestsOnlyAuthorizationFilter() };\n\n        private IEnumerable<IDashboardAsyncAuthorizationFilter> _asyncAuthorization;\n\n        public DashboardOptions()\n        {\n            AppPath = \"/\";\n            PrefixPath = string.Empty;\n            _asyncAuthorization = [];\n            Authorization = DefaultAuthorization;\n            IsReadOnlyFunc = static _ => false;\n            StatsPollingInterval = 2000;\n            DisplayStorageConnectionString = true;\n            DashboardTitle = \"Hangfire Dashboard\";\n            DisplayNameFunc = null;\n            DefaultRecordsPerPage = 20;\n            DarkModeEnabled = true;\n        }\n\n        /// <summary>\n        /// The path for the Back To Site link. Set to <see langword=\"null\" /> in order to hide the Back To Site link.\n        /// </summary>\n        public string AppPath { get; set; }\n        \n        /// <summary>\n        /// The path for the first url prefix link, eg. set \"/admin\", then url is \"{domain}/{PrefixPath}/{hangfire}\"\n        /// </summary>\n        public string PrefixPath { get; set; }\n\n\n#if FEATURE_OWIN\n        [Obsolete(\"Please use `Authorization` property instead. Will be removed in 2.0.0.\")]\n        public IEnumerable<IAuthorizationFilter> AuthorizationFilters { get; set; }\n#endif\n\n        public IEnumerable<IDashboardAuthorizationFilter> Authorization { get; set; }\n\n        public IEnumerable<IDashboardAsyncAuthorizationFilter> AsyncAuthorization\n        {\n            get => _asyncAuthorization;\n            set\n            {\n                _asyncAuthorization = value;\n\n                if (ReferenceEquals(Authorization, DefaultAuthorization))\n                {\n                    Authorization = [];\n                }\n            }\n        }\n\n        public Func<DashboardContext, bool> IsReadOnlyFunc { get; set; }\n        \n        /// <summary>\n        /// The interval the /stats endpoint should be polled with.\n        /// </summary>\n        public int StatsPollingInterval { get; set; }\n\n        public bool DisplayStorageConnectionString { get; set; }\n\n        /// <summary>\n        /// The Title displayed on the dashboard, optionally modify to describe this dashboards purpose.\n        /// </summary>\n        public string DashboardTitle { get; set; }\n\n        /// <summary>\n        /// Display name provider for jobs\n        /// </summary>\n        public Func<DashboardContext, Job, string> DisplayNameFunc { get; set; }\n\n        public bool IgnoreAntiforgeryToken { get; set; }\n\n        public ITimeZoneResolver TimeZoneResolver { get; set; }\n\n        /// <summary>\n        /// Gets or sets the default number of records per page.\n        /// </summary>\n        public int DefaultRecordsPerPage { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether dark mode support is enabled by including\n        /// or excluding the corresponding CSS files.\n        /// </summary>\n        public bool DarkModeEnabled { get; set; }\n\n        /// <summary>\n        /// Optional favicon path\n        /// </summary>\n        public string FaviconPath { get; set; } = string.Empty;\n\n        /// <summary>\n        /// Gets or sets the time threshold after which a warning icon will be shown near a job or a\n        /// server, depending on its last reported heartbeat.\n        /// </summary>\n        /// <remarks>\n        /// It should be larger than a configured <see cref=\"BackgroundJobServerOptions.HeartbeatInterval\"/>\n        /// value, to give servers a chance to report it, but is expected to be lower than a configured\n        /// <see cref=\"BackgroundJobServerOptions.ServerTimeout\"/> value, since this is a heuristic anyway.\n        /// </remarks>\n        public TimeSpan ServerPossiblyAbortedThreshold { get; set; } = TimeSpan.FromMinutes(1);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/DisableConcurrentExecutionAttribute.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Globalization;\nusing System.Linq;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Newtonsoft.Json;\n\nnamespace Hangfire\n{\n    public class DisableConcurrentExecutionAttribute : JobFilterAttribute, IServerFilter\n    {\n        public DisableConcurrentExecutionAttribute(int timeoutInSeconds)\n        {\n            if (timeoutInSeconds < 0) throw new ArgumentException(\"Timeout argument value should be greater that zero.\");\n\n            TimeoutSec = timeoutInSeconds;\n        }\n        \n        [JsonConstructor]\n        public DisableConcurrentExecutionAttribute(string resource, int timeoutSec)\n            : this(timeoutSec)\n        {\n            Resource = resource;\n        }\n\n        [CanBeNull]\n        public string Resource { get; }\n        public int TimeoutSec { get; }\n\n        public void OnPerforming(PerformingContext context)\n        {\n            var resource = GetResource(context.BackgroundJob.Job);\n            var timeout = TimeSpan.FromSeconds(TimeoutSec);\n\n            var distributedLock = context.Connection.AcquireDistributedLock(resource, timeout);\n            context.Items[\"DistributedLock\"] = distributedLock;\n        }\n\n        public void OnPerformed(PerformedContext context)\n        {\n            if (!context.Items.TryGetValue(\"DistributedLock\", out var value))\n            {\n                throw new InvalidOperationException(\"Can not release a distributed lock: it was not acquired.\");\n            }\n\n            var distributedLock = (IDisposable)value;\n            distributedLock.Dispose();\n        }\n\n        private string GetResource(Job job)\n        {\n            if (!String.IsNullOrWhiteSpace(Resource))\n            {\n                try\n                {\n                    return String.Format(CultureInfo.InvariantCulture, Resource, job.Args.ToArray()).ToLowerInvariant();\n                }\n                catch (Exception ex)\n                {\n                    throw new FormatException($\"Unable to obtain resource identifier: {ex.Message}\");\n                }\n            }\n\n            return $\"{job.Type.ToGenericTypeString()}.{job.Method.Name}\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/ExceptionInfo.cs",
    "content": "﻿// This file is part of Hangfire.\n// Copyright © 2020 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Text;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Newtonsoft.Json;\n\nnamespace Hangfire\n{\n    public sealed class ExceptionInfo\n    {\n        public ExceptionInfo([NotNull] Exception exception)\n        {\n            if (exception == null) throw new ArgumentNullException(nameof(exception));\n\n            Message = exception.Message;\n            Type = TypeHelper.CurrentTypeSerializer(exception.GetType());\n\n            if (exception.InnerException != null)\n            {\n                InnerException = new ExceptionInfo(exception.InnerException);\n            }\n        }\n\n        [JsonConstructor]\n        public ExceptionInfo([NotNull] string type, [CanBeNull] string message, [CanBeNull] ExceptionInfo innerException)\n        {\n            Type = type ?? throw new ArgumentNullException(nameof(type));\n            Message = message;\n            InnerException = innerException;\n        }\n\n        [NotNull]\n        [JsonProperty(\"e\")]\n        public string Type { get; }\n\n        [CanBeNull]\n        [JsonProperty(\"m\", NullValueHandling = NullValueHandling.Ignore)]\n        public string Message { get; }\n\n        [CanBeNull]\n        [JsonProperty(\"i\", NullValueHandling = NullValueHandling.Ignore)]\n        public ExceptionInfo InnerException { get; }\n\n        public override string ToString()\n        {\n            var sb = new StringBuilder();\n            var commaIndex = Type.IndexOf(',');\n            sb.Append(commaIndex >= 0 ? Type.Substring(0, commaIndex) : Type);\n            sb.Append(\": \");\n            sb.Append(Message);\n\n            if (InnerException != null)\n            {\n                sb.Append(\" ---> \");\n                sb.AppendLine(InnerException.ToString());\n            }\n\n            return sb.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/ExceptionTypeHelper.cs",
    "content": "// This file is part of Hangfire. Copyright © 2022 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire\n{\n    internal static class ExceptionTypeHelper\n    {\n#if !NETSTANDARD1_3\n        private static readonly Type StackOverflowType = typeof(StackOverflowException);\n#endif\n        private static readonly Type OutOfMemoryType = typeof(OutOfMemoryException);\n \n        internal static bool IsCatchableExceptionType(this Exception e)\n        {\n            var type = e.GetType();\n            return\n#if !NETSTANDARD1_3\n                type != StackOverflowType &&\n#endif\n                type != OutOfMemoryType;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/FromParameterAttribute.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire\n{\n    [AttributeUsage(AttributeTargets.Parameter)]\n    public class FromParameterAttribute : Attribute\n    {\n        public FromParameterAttribute(string parameterName)\n        {\n            ParameterName = parameterName;\n        }\n\n        public string ParameterName { get; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/FromResultAttribute.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire\n{\n    [AttributeUsage(AttributeTargets.Parameter)]\n    public sealed class FromResultAttribute : FromParameterAttribute\n    {\n        public FromResultAttribute() : base(\"AntecedentResult\")\n        {\n        }\n    }\n\n    [AttributeUsage(AttributeTargets.Parameter)]\n    public sealed class FromExceptionAttribute : FromParameterAttribute\n    {\n        public FromExceptionAttribute() : base(\"AntecedentException\")\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/GlobalConfiguration.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\n// ReSharper disable InconsistentNaming\n\nusing System;\nusing System.ComponentModel;\nusing System.Threading;\nusing Hangfire.Annotations;\n\nnamespace Hangfire\n{\n    [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Naming\", \"CA1707:Identifiers should not contain underscores\", Justification = \"Public API, can not touch in minor versions.\")]\n    public enum CompatibilityLevel\n    {\n        Version_110 = 110,\n        Version_170 = 170,\n        Version_180 = 180,\n    }\n\n    public class GlobalConfiguration : IGlobalConfiguration\n    {\n        private static int _compatibilityLevel = (int)CompatibilityLevel.Version_110;\n\n        public static IGlobalConfiguration Configuration { get; } = new GlobalConfiguration();\n\n        internal static CompatibilityLevel CompatibilityLevel\n        {\n            get => (CompatibilityLevel)Volatile.Read(ref _compatibilityLevel);\n            set => Volatile.Write(ref _compatibilityLevel, (int)value);\n        }\n\n        internal static bool HasCompatibilityLevel(CompatibilityLevel level)\n        {\n            return CompatibilityLevel >= level;\n        }\n\n        internal GlobalConfiguration()\n        {\n        }\n    }\n\n    public static class CompatibilityLevelExtensions\n    {\n        public static IGlobalConfiguration SetDataCompatibilityLevel(\n            [NotNull] this IGlobalConfiguration configuration,\n            CompatibilityLevel compatibilityLevel)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n#if !NETSTANDARD1_3\n            if (!Enum.IsDefined(typeof(CompatibilityLevel), compatibilityLevel))\n                throw new InvalidEnumArgumentException(nameof(compatibilityLevel), (int) compatibilityLevel,\n                    typeof(CompatibilityLevel));\n#endif\n\n            GlobalConfiguration.CompatibilityLevel = compatibilityLevel;\n\n            return configuration;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/GlobalConfigurationExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.ComponentModel;\nusing System.Globalization;\nusing System.Reflection;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Dashboard;\nusing Hangfire.Dashboard.Pages;\nusing Hangfire.Logging;\nusing Hangfire.Logging.LogProviders;\nusing Newtonsoft.Json;\n\nnamespace Hangfire\n{\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public static class GlobalConfigurationExtensions\n    {\n        public static IGlobalConfiguration<TStorage> UseStorage<TStorage>(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] TStorage storage)\n            where TStorage : JobStorage\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n\n            return configuration.Use(storage, static x => JobStorage.Current = x);\n        }\n\n        public static IGlobalConfiguration<TStorage> WithJobExpirationTimeout<TStorage>(\n            [NotNull] this IGlobalConfiguration<TStorage> configuration,\n            TimeSpan timeout)\n            where TStorage : JobStorage\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            configuration.Entry.JobExpirationTimeout = timeout;\n            return configuration;\n        }\n\n        public static IGlobalConfiguration<TActivator> UseActivator<TActivator>(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] TActivator activator)\n            where TActivator : JobActivator\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (activator == null) throw new ArgumentNullException(nameof(activator));\n\n            return configuration.Use(activator, static x => JobActivator.Current = x);\n        }\n\n        public static IGlobalConfiguration<JobActivator> UseDefaultActivator(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            return configuration.UseActivator(new JobActivator());\n        }\n\n        public static IGlobalConfiguration<TLogProvider> UseLogProvider<TLogProvider>(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] TLogProvider provider)\n            where TLogProvider : ILogProvider\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (provider == null) throw new ArgumentNullException(nameof(provider));\n            \n            return configuration.Use(provider, static x => LogProvider.SetCurrentLogProvider(x));\n        }\n\n        /// <summary>\n        /// Explicitly disables all the logging in Hangfire. Not recommended to use in a production\n        /// application, because logging provides significant benefits in case of exceptions or other\n        /// problems. But it can be useful for testing-related scenarios.\n        /// </summary>\n        public static IGlobalConfiguration<ILogProvider> UseNoOpLogProvider(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            return configuration.UseLogProvider(LogProvider.NoOpLogProvider.Instance);\n        }\n\n        public static IGlobalConfiguration<NLogLogProvider> UseNLogLogProvider(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            return configuration.UseLogProvider(new NLogLogProvider());\n        }\n\n        public static IGlobalConfiguration<ColouredConsoleLogProvider> UseColouredConsoleLogProvider(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            return configuration.UseLogProvider(new ColouredConsoleLogProvider());\n        }\n\n        public static IGlobalConfiguration<ColouredConsoleLogProvider> UseColouredConsoleLogProvider(\n            [NotNull] this IGlobalConfiguration configuration,\n            LogLevel minLevel)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            return configuration.UseLogProvider(new ColouredConsoleLogProvider(minLevel));\n        }\n\n        public static IGlobalConfiguration<Log4NetLogProvider> UseLog4NetLogProvider(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            return configuration.UseLogProvider(new Log4NetLogProvider());\n        }\n\n#if !NETSTANDARD1_3\n        public static IGlobalConfiguration<ElmahLogProvider> UseElmahLogProvider(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            return configuration.UseLogProvider(new ElmahLogProvider());\n        }\n\n        public static IGlobalConfiguration<ElmahLogProvider> UseElmahLogProvider(\n            [NotNull] this IGlobalConfiguration configuration,\n            LogLevel minLevel)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            return configuration.UseLogProvider(new ElmahLogProvider(minLevel));\n        }\n#endif\n\n#if !NETSTANDARD1_3\n        public static IGlobalConfiguration<EntLibLogProvider> UseEntLibLogProvider(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            return configuration.UseLogProvider(new EntLibLogProvider());\n        }\n#endif\n\n        public static IGlobalConfiguration<SerilogLogProvider> UseSerilogLogProvider(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            return configuration.UseLogProvider(new SerilogLogProvider());\n        }\n\n#if !NETSTANDARD1_3\n        public static IGlobalConfiguration<LoupeLogProvider> UseLoupeLogProvider(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            return configuration.UseLogProvider(new LoupeLogProvider());\n        }\n#endif\n\n        public static IGlobalConfiguration<TFilter> UseFilter<TFilter>(\n            [NotNull] this IGlobalConfiguration configuration, \n            [NotNull] TFilter filter)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (filter == null) throw new ArgumentNullException(nameof(filter));\n\n            return configuration.Use(filter, static x => GlobalJobFilters.Filters.Add(x));\n        }\n\n        public static IGlobalConfiguration<TFilterProvider> UseFilterProvider<TFilterProvider>(\n            [NotNull] this IGlobalConfiguration configuration, \n            [NotNull] TFilterProvider filterProvider)\n            where TFilterProvider : IJobFilterProvider\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (filterProvider == null) throw new ArgumentNullException(nameof(filterProvider));\n\n            return configuration.Use(filterProvider, static x => JobFilterProviders.Providers.Add(x));\n        }\n\n        public static IGlobalConfiguration UseDashboardMetric(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] DashboardMetric metric)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (metric == null) throw new ArgumentNullException(nameof(metric));\n\n            DashboardMetrics.AddMetric(metric);\n            HomePage.Metrics.Add(metric);\n\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseDashboardMetrics(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] params DashboardMetric[] metrics)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (metrics == null) throw new ArgumentNullException(nameof(metrics));\n\n            foreach (var metric in metrics)\n            {\n                DashboardMetrics.AddMetric(metric);\n                HomePage.Metrics.Add(metric);\n            }\n\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseJobDetailsRenderer(\n            [NotNull] this IGlobalConfiguration configuration,\n            int order,\n            [NotNull] Func<JobDetailsRendererDto, NonEscapedString> renderer)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (renderer == null) throw new ArgumentNullException(nameof(renderer));\n\n            JobDetailsRenderer.AddRenderer(order, renderer);\n\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseTypeResolver(\n            [NotNull] this IGlobalConfiguration configuration,\n            [CanBeNull] Func<string, Type> typeResolver)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            TypeHelper.CurrentTypeResolver = typeResolver;\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseTypeSerializer(\n            [NotNull] this IGlobalConfiguration configuration,\n            [CanBeNull] Func<Type, string> typeSerializer)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            TypeHelper.CurrentTypeSerializer = typeSerializer;\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseDefaultTypeResolver(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            return configuration.UseTypeResolver(null);\n        }\n\n        public static IGlobalConfiguration UseDefaultTypeSerializer(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            return configuration.UseTypeSerializer(null);\n        }\n\n        public static IGlobalConfiguration UseSimpleAssemblyNameTypeSerializer(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            return configuration.UseTypeSerializer(TypeHelper.SimpleAssemblyTypeSerializer);\n        }\n\n        public static IGlobalConfiguration UseIgnoredAssemblyVersionTypeResolver(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            return configuration.UseTypeResolver(TypeHelper.IgnoredAssemblyVersionTypeResolver);\n        }\n\n        /// <summary>\n        /// These settings are used to serialize user data like arguments or parameters.\n        /// You can use <see cref=\"SerializationHelper.Serialize{T}(T, SerializationOption)\"/> with <see cref=\"SerializationOption.User\"/> option\n        /// to serialize with specified settings\n        /// </summary>\n        public static IGlobalConfiguration UseSerializerSettings(\n            [NotNull] this IGlobalConfiguration configuration,\n            [CanBeNull] JsonSerializerSettings settings)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            SerializationHelper.SetUserSerializerSettings(settings);\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseRecommendedSerializerSettings(\n            [NotNull] this IGlobalConfiguration configuration)\n        {\n            return UseRecommendedSerializerSettings(configuration, null);\n        }\n\n        public static IGlobalConfiguration UseRecommendedSerializerSettings(\n            [NotNull] this IGlobalConfiguration configuration,\n            [CanBeNull] Action<JsonSerializerSettings> settingsConfiguration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            var settings = SerializationHelper.GetInternalSettings();\n            settingsConfiguration?.Invoke(settings);\n\n            SerializationHelper.SetUserSerializerSettings(settings);\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseResultsInContinuations(this IGlobalConfiguration configuration)\n        {\n            return configuration\n                .UseFilter(new JobParameterInjectionFilter())\n                .UseFilter(new ContinuationsSupportAttribute(pushResults: true));\n        }\n\n        public static IGlobalConfiguration UseMaxLinesInExceptionDetails(\n            [NotNull] this IGlobalConfiguration configuration,\n            [CanBeNull] int? numberOfLines)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (numberOfLines.HasValue && numberOfLines.Value <= 1) throw new ArgumentOutOfRangeException(nameof(numberOfLines), \"Must be either a positive value larger than 1; or a null value.\");\n\n            States.FailedState.MaxLinesInExceptionDetails = numberOfLines;\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseMaxArgumentSizeToRender(\n            [NotNull] this IGlobalConfiguration configuration,\n            int size)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (size < 0) throw new ArgumentOutOfRangeException(nameof(size), \"Must be a positive value\");\n\n            JobMethodCallRenderer.MaxArgumentToRenderSize = size;\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseDefaultCulture(\n            [NotNull] this IGlobalConfiguration configuration,\n            [CanBeNull] CultureInfo culture)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            return configuration.UseFilter(new CaptureCultureAttribute(culture?.Name));\n        }\n\n        public static IGlobalConfiguration UseDefaultCulture(\n            [NotNull] this IGlobalConfiguration configuration,\n            [CanBeNull] CultureInfo culture,\n            bool captureDefault)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            return configuration.UseFilter(new CaptureCultureAttribute(culture?.Name, captureDefault));\n        }\n\n        public static IGlobalConfiguration UseDefaultCulture(\n            [NotNull] this IGlobalConfiguration configuration,\n            [CanBeNull] CultureInfo culture,\n            [CanBeNull] CultureInfo uiCulture)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            return configuration.UseFilter(new CaptureCultureAttribute(culture?.Name, uiCulture?.Name));\n        }\n\n        public static IGlobalConfiguration UseDefaultCulture(\n            [NotNull] this IGlobalConfiguration configuration,\n            [CanBeNull] CultureInfo culture,\n            [CanBeNull] CultureInfo uiCulture,\n            bool captureDefault)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            return configuration.UseFilter(new CaptureCultureAttribute(culture?.Name, uiCulture?.Name, captureDefault));\n        }\n\n        public static IGlobalConfiguration UseDashboardStylesheet(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] Assembly assembly,\n            [NotNull] string resource)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (assembly == null) throw new ArgumentNullException(nameof(assembly));\n            if (resource == null) throw new ArgumentNullException(nameof(resource));\n\n            DashboardRoutes.AddStylesheet(assembly, resource);\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseDashboardStylesheetDarkMode(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] Assembly assembly,\n            [NotNull] string resource)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (assembly == null) throw new ArgumentNullException(nameof(assembly));\n            if (resource == null) throw new ArgumentNullException(nameof(resource));\n\n            DashboardRoutes.AddStylesheetDarkMode(assembly, resource);\n            return configuration;\n        }\n\n        public static IGlobalConfiguration UseDashboardJavaScript(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] Assembly assembly,\n            [NotNull] string resource)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (assembly == null) throw new ArgumentNullException(nameof(assembly));\n            if (resource == null) throw new ArgumentNullException(nameof(resource));\n\n            DashboardRoutes.AddJavaScript(assembly, resource);\n            return configuration;\n        }\n\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public static IGlobalConfiguration<T> Use<T>(\n            [NotNull] this IGlobalConfiguration configuration, T entry,\n            [NotNull] Action<T> entryAction)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            entryAction(entry);\n\n            return new ConfigurationEntry<T>(entry);\n        }\n\n        private sealed class ConfigurationEntry<T> : IGlobalConfiguration<T>\n        {\n            public ConfigurationEntry(T entry)\n            {\n                Entry = entry;\n            }\n\n            public T Entry { get; }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/GlobalJobFilters.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing Hangfire.Common;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Represents the global filter collection.\n    /// </summary>\n    public static class GlobalJobFilters\n    {\n        static GlobalJobFilters()\n        {\n            // ReSharper disable once UseObjectOrCollectionInitializer\n            Filters = new JobFilterCollection();\n\n            // Filters should be added with the `Add` method call: some \n            // of them indirectly use `GlobalJobFilters.Filters` property, \n            // and it is null, when we are using collection initializer.\n            Filters.Add(new CaptureCultureAttribute());\n            Filters.Add(new AutomaticRetryAttribute());\n            Filters.Add(new StatisticsHistoryAttribute());\n            Filters.Add(new ContinuationsSupportAttribute());\n        }\n\n        /// <summary>\n        /// Gets the global filter collection.\n        /// </summary>\n        public static JobFilterCollection Filters { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/GlobalStateHandlers.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    public static class GlobalStateHandlers\n    {\n        static GlobalStateHandlers()\n        {\n            Handlers = new List<IStateHandler>\n            {\n                new SucceededState.Handler(),\n                new ScheduledState.Handler(),\n                new EnqueuedState.Handler(),\n                new DeletedState.Handler(),\n                new AwaitingState.Handler()\n            };\n        }\n\n        public static ICollection<IStateHandler> Handlers { get; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Hangfire.Core.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n  <PropertyGroup>\r\n    <TargetFrameworks>net451;net46;netstandard1.3;netstandard2.0;</TargetFrameworks>\r\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\r\n    <RootNamespace>Hangfire</RootNamespace>\r\n  </PropertyGroup>\r\n  \r\n  <PropertyGroup Condition=\"'$(TargetFramework)'=='net451' or '$(TargetFramework)'=='net46'\">\r\n    <DefineConstants>$(DefineConstants);FEATURE_CRONDESCRIPTOR;FEATURE_OWIN;</DefineConstants>\r\n  </PropertyGroup>\r\n  \r\n  <ItemGroup>\r\n    <PackageReference Include=\"MoreLinq.Source.MoreEnumerable.Pairwise\" Version=\"1.0.1\" PrivateAssets=\"all\" />\r\n    <PackageReference Include=\"LibLog\" Version=\"1.5.0\" PrivateAssets=\"all\" />\r\n    <PackageReference Include=\"Cronos\" Version=\"0.11.1\" />\r\n    <PackageReference Include=\"StackTraceFormatter.Source\" Version=\"1.1.0\" PrivateAssets=\"all\" />\r\n    <PackageReference Include=\"StackTraceParser.Source\" Version=\"1.3.0\" PrivateAssets=\"all\" />\r\n  </ItemGroup>\r\n  \r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net451' or '$(TargetFramework)'=='net46'\">\r\n    <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies\" Version=\"1.0.3\" PrivateAssets=\"all\" />\r\n    <PackageReference Include=\"Owin\" Version=\"1.0\" />\r\n    <PackageReference Include=\"CronExpressionDescriptor\" Version=\"1.21.0\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"5.0.1\" NoWarn=\"NU1903\" />\r\n    <PackageReference Include=\"Microsoft.Owin\" Version=\"4.2.3\" />\r\n  </ItemGroup>\r\n  \r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='netstandard1.3'\">\r\n    <PackageReference Include=\"System.Threading.Thread\" Version=\"4.0.0\" />\r\n    <PackageReference Include=\"System.Threading.ThreadPool\" Version=\"4.0.10\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"9.0.1\" NoWarn=\"NU1903\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='netstandard2.0'\">\r\n    <PackageReference Include=\"Microsoft.CSharp\" Version=\"4.4.0\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"11.0.1\" NoWarn=\"NU1903\" />\r\n  </ItemGroup>\r\n  \r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='netstandard1.3' or '$(TargetFramework)'=='netstandard2.0'\">\r\n    <Compile Remove=\"AppBuilderExtensions.cs\" />\r\n    <Compile Remove=\"Dashboard/Owin/**\" />\r\n    <Compile Remove=\"Obsolete/**\" />\r\n    <Compile Include=\"Obsolete/CreateJobFailedException.cs\" />\r\n    <Compile Include=\"Obsolete/IServerComponent.cs\" />\r\n    <Compile Include=\"Obsolete/IServerProcess.cs\" />\r\n    <Compile Include=\"Obsolete/Job.Obsolete.cs\" />\r\n    <Compile Include=\"Obsolete/ServerWatchdogOptions.cs\" />\r\n    <Compile Include=\"Obsolete/StateContext.cs\" />\r\n  </ItemGroup>\r\n  \r\n  <!-- Full MSBuild is required to generate Razor classes -->\r\n  <PropertyGroup>\r\n    <MSBuild14FullPath>$(MSBuildProgramFiles32)\\..\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe</MSBuild14FullPath>\r\n    <MSBuildCurrentFullPath>$(MSBuildBinPath)\\MSBuild.exe</MSBuildCurrentFullPath>\r\n    <RazorProjectFile>Razor.build</RazorProjectFile>\r\n  </PropertyGroup>\r\n\r\n  <Target Name=\"GenerateRazorClasses\" BeforeTargets=\"Build\">\r\n    <Exec Command=\"&quot;$(MSBuildCurrentFullPath)&quot; $(RazorProjectFile) /v:quiet /nologo\" Condition=\"Exists('$(MSBuildCurrentFullPath)')\" />\r\n    <Exec Command=\"&quot;$(MSBuild14FullPath)&quot; $(RazorProjectFile) /v:quiet /nologo\" Condition=\"Exists('$(MSBuild14FullPath)') and !Exists('$(MSBuildCurrentFullPath)')\" />\r\n    <Warning Text=\"Classes for Razor files (*.cshtml) weren't re-generated: couldn't find the '$(MSBuild14FullPath)' or '$(MSBuildCurrentFullPath)' file\" Condition=\"!Exists('$(MSBuild14FullPath)') and !Exists('$(MSBuildCurrentFullPath)')\" />\r\n  </Target>\r\n  \r\n  <ItemGroup>\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\css\\bootstrap.min.css\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\css\\Chart.min.css\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\css\\hangfire.css\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\css\\hangfire-dark.css\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\fonts\\glyphicons-halflings-regular.eot\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\fonts\\glyphicons-halflings-regular.svg\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\fonts\\glyphicons-halflings-regular.ttf\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\fonts\\glyphicons-halflings-regular.woff\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\fonts\\glyphicons-halflings-regular.woff2\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\js\\bootstrap.min.js\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\js\\hangfire.js\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\js\\jquery-3.7.1.min.js\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\js\\moment-with-locales.min.js\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\js\\Chart.min.js\" />\r\n    <EmbeddedResource Include=\"Dashboard\\Content\\js\\chartjs-plugin-streaming.min.js\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <EmbeddedResource Update=\"Dashboard\\Content\\resx\\Strings.resx\">\r\n      <Generator>PublicResXFileCodeGenerator</Generator>\r\n      <LastGenOutput>Strings.Designer.cs</LastGenOutput>\r\n      <CustomToolNamespace>Hangfire.Dashboard.Resources</CustomToolNamespace>\r\n    </EmbeddedResource>\r\n    <Compile Update=\"Dashboard\\Content\\resx\\Strings.Designer.cs\">\r\n      <DesignTime>True</DesignTime>\r\n      <AutoGen>True</AutoGen>\r\n      <DependentUpon>Strings.resx</DependentUpon>\r\n    </Compile>\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <Compile Update=\"Dashboard\\Pages\\_BlockMetric.cshtml.cs\">\r\n      <DependentUpon>_BlockMetric.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_BlockMetric.cs\">\r\n      <DependentUpon>_BlockMetric.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_Breadcrumbs.cshtml.cs\">\r\n      <DependentUpon>_Breadcrumbs.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_Breadcrumbs.cs\">\r\n      <DependentUpon>_Breadcrumbs.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_ErrorAlert.cshtml.cs\">\r\n      <DependentUpon>_ErrorAlert.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_InlineMetric.cshtml.cs\">\r\n      <DependentUpon>_InlineMetric.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_InlineMetric.cs\">\r\n      <DependentUpon>_InlineMetric.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_Navigation.cshtml.cs\">\r\n      <DependentUpon>_Navigation.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_Paginator.cshtml.cs\">\r\n      <DependentUpon>_Paginator.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_Paginator.cs\">\r\n      <DependentUpon>_Paginator.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_PerPageSelector.cshtml.cs\">\r\n      <DependentUpon>_PerPageSelector.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_PerPageSelector.cs\">\r\n      <DependentUpon>_PerPageSelector.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_SidebarMenu.cshtml.cs\">\r\n      <DependentUpon>_SidebarMenu.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\_SidebarMenu.cs\">\r\n      <DependentUpon>_SidebarMenu.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\AwaitingJobsPage.cshtml.cs\">\r\n      <DependentUpon>AwaitingJobsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\DeletedJobsPage.cshtml.cs\">\r\n      <DependentUpon>DeletedJobsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\EnqueuedJobsPage.cshtml.cs\">\r\n      <DependentUpon>EnqueuedJobsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\EnqueuedJobsPage.cs\">\r\n      <DependentUpon>EnqueuedJobsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\FailedJobsPage.cshtml.cs\">\r\n      <DependentUpon>FailedJobsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\FetchedJobsPage.cshtml.cs\">\r\n      <DependentUpon>FetchedJobsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\FetchedJobsPage.cs\">\r\n      <DependentUpon>FetchedJobsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\HomePage.cshtml.cs\">\r\n      <DependentUpon>HomePage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\HomePage.cs\">\r\n      <DependentUpon>HomePage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\JobDetailsPage.cshtml.cs\">\r\n      <DependentUpon>JobDetailsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\JobDetailsPage.cs\">\r\n      <DependentUpon>JobDetailsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\LayoutPage.cshtml.cs\">\r\n      <DependentUpon>LayoutPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\LayoutPage.cs\">\r\n      <DependentUpon>LayoutPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\ProcessingJobsPage.cshtml.cs\">\r\n      <DependentUpon>ProcessingJobsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\QueuesPage.cshtml.cs\">\r\n      <DependentUpon>QueuesPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\RecurringJobsPage.cshtml.cs\">\r\n      <DependentUpon>RecurringJobsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\RetriesPage.cshtml.cs\">\r\n      <DependentUpon>RetriesPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\ScheduledJobsPage.cshtml.cs\">\r\n      <DependentUpon>ScheduledJobsPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\ServersPage.cshtml.cs\">\r\n      <DependentUpon>ServersPage.cshtml</DependentUpon>\r\n    </Compile>\r\n    <Compile Update=\"Dashboard\\Pages\\SucceededJobs.cshtml.cs\">\r\n      <DependentUpon>SucceededJobs.cshtml</DependentUpon>\r\n    </Compile>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "src/Hangfire.Core/IBackgroundJobClient.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Provides extended methods for creating background jobs and changing\n    /// their states.\n    /// </summary>\n    public interface IBackgroundJobClientV2 : IBackgroundJobClient\n    {\n        /// <summary>\n        /// Gets a storage associated with the current background job client.\n        /// </summary>\n        [NotNull]\n        JobStorage Storage { get; }\n\n        /// <summary>\n        /// Creates a background job within the specified state and given job parameters.\n        /// </summary>\n        /// \n        /// <param name=\"job\">Job that should be processed in background.</param>\n        /// <param name=\"state\">Initial state for a background job.</param>\n        /// <param name=\"parameters\">Job parameters to create.</param>\n        /// <returns>Unique identifier of a created background job <i>-or-</i> \n        ///  <see langword=\"null\"/>, if it was not created.</returns>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"job\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"state\"/> is null.</exception>\n        /// <exception cref=\"BackgroundJobClientException\">Creation failed due to an exception.</exception>\n        [CanBeNull]\n        string Create([NotNull] Job job, [NotNull] IState state, [CanBeNull] IDictionary<string, object> parameters);\n    }\n\n    /// <summary>\n    /// Provides methods for creating background jobs and changing their states.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// <para>Please see the <see cref=\"BackgroundJobClient\"/> class for\n    /// details regarding the implementation.</para>\n    /// </remarks>\n    public interface IBackgroundJobClient\n    {\n        /// <summary>\n        /// Creates a new background job in a specified state.\n        /// </summary>\n        /// \n        /// <param name=\"job\">Job that should be processed in background.</param>\n        /// <param name=\"state\">Initial state for a background job.</param>\n        /// <returns>Unique identifier of a created background job <i>-or-</i> \n        ///  <see langword=\"null\"/>, if it was not created.</returns>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"job\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"state\"/> is null.</exception>\n        /// <exception cref=\"BackgroundJobClientException\">Creation failed due to an exception.</exception>\n        /// \n        /// <remarks>\n        /// <para>The interface allows implementations to return <see langword=\"null\"/> \n        /// value for this method when background job creation has been canceled\n        /// by an implementation under the normal circumstances (not due to an\n        /// exception). For example, the <see cref=\"CreatingContext\"/> class\n        /// contains the <see cref=\"CreatingContext.Canceled\"/> property that\n        /// may be used by a client filter to cancel a background job creation.\n        /// </para>\n        /// \n        /// <para>The interface allows implementations to create a background \n        /// job in a state other than specified. The given state instance also \n        /// may be modified. For example, <see cref=\"ElectStateContext\"/> class\n        /// contains public setter for the <see cref=\"ElectStateContext.CandidateState\"/>\n        /// property allowing to choose completely different state by state\n        /// election filters.</para>\n        /// </remarks>\n        [CanBeNull]\n        string Create([NotNull] Job job, [NotNull] IState state);\n\n        /// <summary>\n        /// Attempts to change a state of a background job with a given\n        /// identifier to a specified one.\n        /// </summary>\n        /// \n        /// <param name=\"jobId\">Identifier of background job, whose state should be changed.</param>\n        /// <param name=\"state\">New state for a background job.</param>\n        /// <param name=\"expectedState\">Expected state assertion, or <see langword=\"null\"/> if unneeded.</param>\n        /// \n        /// <returns><see langword=\"true\"/>, if a <b>given</b> state was applied\n        /// successfully otherwise <see langword=\"false\"/>.</returns>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"jobId\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"state\"/> is null.</exception>\n        /// <exception cref=\"BackgroundJobClientException\">State change failed due to an exception.</exception>\n        /// \n        /// <remarks>\n        /// <para>If <paramref name=\"expectedState\"/> value is not null, state \n        /// change will be performed only if the current state name of a job \n        /// equal to the given value.</para>\n        /// \n        /// <para>The interface allows implementations to change a state of a \n        /// background job to other than specified. The given state instance also\n        /// may be modified. For example, <see cref=\"ElectStateContext\"/> class\n        /// contains public setter for the <see cref=\"ElectStateContext.CandidateState\"/>\n        /// property allowing to choose completely different state by state\n        /// election filters. If a state was changed, <see langword=\"false\"/> \n        /// value will be returned.</para>\n        /// </remarks>\n        bool ChangeState([NotNull] string jobId, [NotNull] IState state, [CanBeNull] string expectedState);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/IGlobalConfiguration.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.ComponentModel;\n\nnamespace Hangfire\n{\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public interface IGlobalConfiguration<out T> : IGlobalConfiguration\n    {\n        [EditorBrowsable(EditorBrowsableState.Advanced)]\n        T Entry { get; }\n    }\n\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public interface IGlobalConfiguration\n    {\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/IJobCancellationToken.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Threading;\n\nnamespace Hangfire\n{\n    public interface IJobCancellationToken\n    {\n        CancellationToken ShutdownToken { get; }\n\n        /// <summary>\n        /// Throws a <see cref=\"System.OperationCanceledException\">OperationCanceledException</see> if\n        /// this token has had cancellation requested.\n        /// </summary>\n        /// <remarks>\n        /// This method provides functionality equivalent to:\n        /// <code>\n        /// if (token.ShutdownToken.IsCancellationRequested)\n        ///    throw new OperationCanceledException(token);\n        /// </code>\n        /// </remarks>\n        /// <exception cref=\"System.OperationCanceledException\">The token has had cancellation requested.</exception>\n        void ThrowIfCancellationRequested();\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/IRecurringJobManager.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing Hangfire.Annotations;\nusing Hangfire.Common;\n\nnamespace Hangfire\n{\n    public interface IRecurringJobManagerV2 : IRecurringJobManager\n    {\n        [NotNull]\n        JobStorage Storage { get; }\n\n        [CanBeNull]\n        string TriggerJob([NotNull] string recurringJobId);\n    }\n\n    public interface IRecurringJobManager\n    {\n        void AddOrUpdate(\n            [NotNull] string recurringJobId, \n            [NotNull] Job job, \n            [NotNull] string cronExpression, \n            [NotNull] RecurringJobOptions options);\n\n        void Trigger([NotNull] string recurringJobId);\n        void RemoveIfExists([NotNull] string recurringJobId);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/ITimeZoneResolver.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\n\nnamespace Hangfire\n{\n    public sealed class DefaultTimeZoneResolver : ITimeZoneResolver\n    {\n        public TimeZoneInfo GetTimeZoneById(string timeZoneId)\n        {\n            return TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);\n        }\n    }\n\n    public interface ITimeZoneResolver\n    {\n        [NotNull]\n        TimeZoneInfo GetTimeZoneById([NotNull] string timeZoneId);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/IdempotentCompletionAttribute.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Common;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    public class IdempotentCompletionAttribute : JobFilterAttribute, IElectStateFilter\n    {\n        public IdempotentCompletionAttribute()\n        {\n            Order = 0;\n        }\n\n        public void OnStateElection(ElectStateContext context)\n        {\n            if (String.IsNullOrEmpty(context.CurrentState)) return;\n\n            var serializedState = context.GetJobParameter<string>(\"Completion\", allowStale: true);\n\n            if (!String.IsNullOrEmpty(serializedState))\n            {\n                if (context.CandidateState is ProcessingState || context.CandidateState.IsFinal)\n                {\n                    context.CandidateState = SerializationHelper.Deserialize<IState>(serializedState, SerializationOption.TypedInternal);\n                }\n            }\n            else if (context.CandidateState.IsFinal)\n            {\n                context.SetJobParameter(\"Completion\", SerializationHelper.Serialize(context.CandidateState, SerializationOption.TypedInternal));\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/JobActivator.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Server;\n\nnamespace Hangfire\n{\n    public class JobActivator\n    {\n        private static JobActivator _current = new JobActivator();\n\n        /// <summary>\n        /// Gets or sets the current <see cref=\"JobActivator\"/> instance \n        /// that will be used to activate jobs during performance.\n        /// </summary>\n        public static JobActivator Current\n        {\n            get { return _current; }\n            set\n            {\n                if (value == null)\n                {\n                    throw new ArgumentNullException(nameof(value));\n                }\n\n                _current = value;\n            }\n        }\n\n        \n        public virtual object ActivateJob(Type jobType)\n        {\n            return Activator.CreateInstance(jobType);\n        }\n\n        [Obsolete(\"Please implement/use the BeginScope(JobActivatorContext) method instead. Will be removed in 2.0.0.\")]\n        public virtual JobActivatorScope BeginScope()\n        {\n            return new SimpleJobActivatorScope(this);\n        }\n\n        public virtual JobActivatorScope BeginScope(JobActivatorContext context)\n        {\n#pragma warning disable 618\n            return BeginScope();\n#pragma warning restore 618\n        }\n\n        public virtual JobActivatorScope BeginScope(PerformContext context)\n        {\n            return this.BeginScope(new JobActivatorContext(context.Connection, context.BackgroundJob, context.CancellationToken));\n        }\n\n        class SimpleJobActivatorScope : JobActivatorScope\n        {\n            private readonly JobActivator _activator;\n            private readonly List<IDisposable> _disposables = new List<IDisposable>();\n\n            public SimpleJobActivatorScope([NotNull] JobActivator activator)\n            {\n                if (activator == null) throw new ArgumentNullException(nameof(activator));\n                _activator = activator;\n            }\n\n            public override object Resolve(Type type)\n            {\n                var instance = _activator.ActivateJob(type);\n                var disposable = instance as IDisposable;\n\n                if (disposable != null)\n                {\n                    _disposables.Add(disposable);\n                }\n\n                return instance;\n            }\n\n            public override void DisposeScope()\n            {\n                foreach (var disposable in _disposables)\n                {\n                    disposable.Dispose();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/JobActivatorContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Storage;\n\nnamespace Hangfire\n{\n    public class JobActivatorContext\n    {\n        public JobActivatorContext(\n            [NotNull] IStorageConnection connection,\n            [NotNull] BackgroundJob backgroundJob,\n            [NotNull] IJobCancellationToken cancellationToken)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            if (backgroundJob == null) throw new ArgumentNullException(nameof(backgroundJob));\n            if (cancellationToken == null) throw new ArgumentNullException(nameof(cancellationToken));\n\n            Connection = connection;\n            BackgroundJob = backgroundJob;\n            CancellationToken = cancellationToken;\n        }\n\n        [NotNull]\n        public BackgroundJob BackgroundJob { get; }\n\n        [NotNull]\n        public IJobCancellationToken CancellationToken { get; }\n\n        [NotNull]\n        public IStorageConnection Connection { get; }\n\n        public void SetJobParameter([NotNull] string name, object value)\n        {\n            if (String.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));\n\n            Connection.SetJobParameter(BackgroundJob.Id, name, SerializationHelper.Serialize(value, SerializationOption.User));\n        }\n\n        public T GetJobParameter<T>([NotNull] string name) => GetJobParameter<T>(name, allowStale: false);\n\n        public T GetJobParameter<T>([NotNull] string name, bool allowStale)\n        {\n            if (String.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));\n\n            try\n            {\n                string value;\n\n                if (allowStale && BackgroundJob.ParametersSnapshot != null)\n                {\n                    BackgroundJob.ParametersSnapshot.TryGetValue(name, out value);\n                }\n                else\n                {\n                    value = Connection.GetJobParameter(BackgroundJob.Id, name);                \n                }\n\n                return SerializationHelper.Deserialize<T>(value, SerializationOption.User);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new InvalidOperationException(\n                    $\"Could not get a value of the job parameter `{name}`. See inner exception for details.\", ex);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/JobActivatorScope.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading;\n\nnamespace Hangfire\n{\n    public abstract class JobActivatorScope : IDisposable\n    {\n        // ReSharper disable once InconsistentNaming\n        private static readonly ThreadLocal<JobActivatorScope> _current\n            = new ThreadLocal<JobActivatorScope>(trackAllValues: false);\n\n        protected JobActivatorScope()\n        {\n            _current.Value = this;\n        }\n\n        public static JobActivatorScope Current => _current.Value;\n\n        [Obsolete(\"This property wasn't implemented and will be removed in Hangfire 2.0.0.\")]\n        public object InnerScope { get; set; }\n\n        public abstract object Resolve(Type type);\n\n        public virtual void DisposeScope()\n        {\n        }\n\n        public void Dispose()\n        {\n            try\n            {\n                DisposeScope();\n            }\n            finally\n            {\n                _current.Value = null;\n            }\n\n            GC.SuppressFinalize(this);\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/JobCancellationToken.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Threading;\n\nnamespace Hangfire\n{\n    public class JobCancellationToken : IJobCancellationToken\n    {\n        private readonly bool _canceled;\n\n        public JobCancellationToken(bool canceled)\n        {\n            _canceled = canceled;\n            ShutdownToken = new CancellationToken(canceled);\n        }\n\n        public CancellationToken ShutdownToken { get; }\n\n        public static IJobCancellationToken Null => null;\n\n        /// <inheritdoc cref=\"IJobCancellationToken.ThrowIfCancellationRequested\" />\n        public void ThrowIfCancellationRequested()\n        {\n            ShutdownToken.ThrowIfCancellationRequested();\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/JobCancellationTokenExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing Hangfire.Server;\n\nnamespace Hangfire\n{\n    internal static class JobCancellationTokenExtensions\n    {\n        public static bool IsAborted(this IJobCancellationToken jobCancellationToken)\n        {\n            if (jobCancellationToken is ServerJobCancellationToken serverJobCancellationToken)\n            {\n                // for ServerJobCancellationToken we may simply check IsAborted property\n                // to prevent unnecessary creation of the linked CancellationTokenSource\n                return serverJobCancellationToken.IsAborted;\n            }\n            \n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/JobContinuationOptions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire\n{\n    [Flags]\n    public enum JobContinuationOptions\n    {\n        OnAnyFinishedState   = 0,\n        OnlyOnSucceededState = 1,\n        OnlyOnDeletedState   = 2,\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/JobDisplayNameAttribute.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2018 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Globalization;\nusing System.Linq;\nusing System.Reflection;\nusing System.Resources;\nusing Hangfire.Common;\nusing Hangfire.Dashboard;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Specifies a display name for a job method.\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]\n    public class JobDisplayNameAttribute : Attribute\n    {\n        private static readonly ConcurrentDictionary<Type, ResourceManager> _resourceManagerCache\n            = new ConcurrentDictionary<Type, ResourceManager>();\n\n        public JobDisplayNameAttribute(string displayName)\n        {\n            if (string.IsNullOrEmpty(displayName))\n                throw new ArgumentException(\"Display name is empty\", nameof(displayName));\n\n            DisplayName = displayName;\n        }\n\n        /// <summary>\n        /// Gets display name for the job.\n        /// </summary>\n        public string DisplayName { get; }\n\n        /// <summary>\n        /// Gets or sets resource type to localize <see cref=\"DisplayName\"/> string.\n        /// </summary>\n        public Type ResourceType { get; set; }\n\n        public virtual string Format(DashboardContext context, Job job)\n        {\n            var format = DisplayName;\n\n            if (ResourceType != null)\n            {\n                format = _resourceManagerCache\n                    .GetOrAdd(ResourceType, InitResourceManager)\n                    .GetString(DisplayName, CultureInfo.CurrentUICulture);\n\n                if (string.IsNullOrEmpty(format))\n                {\n                    // failed to localize display name string, use it as is\n                    format = DisplayName;\n                }\n            }\n            \n            return string.Format(CultureInfo.CurrentCulture, format, job.Args.ToArray());\n        }\n\n        private static ResourceManager InitResourceManager(Type type)\n        {\n            var prop = type.GetTypeInfo().GetDeclaredProperty(\"ResourceManager\");\n            if (prop != null && prop.PropertyType == typeof(ResourceManager) && prop.CanRead && prop.GetMethod.IsStatic)\n            {\n                // use existing resource manager if possible\n                return (ResourceManager)prop.GetValue(null);\n            }\n\n            return new ResourceManager(type);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/JobParameterInjectionFilter.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Reflection;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    public class JobParameterInjectionFilter : IServerFilter\n    {\n        internal static readonly string DefaultException = \"OCE\";\n\n        public void OnPerforming(PerformingContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            var argumentsArray = context.BackgroundJob.Job?.Args as object[];\n            if (argumentsArray == null) return;\n\n            var parameters = context.BackgroundJob.Job.Method.GetParameters();\n\n            for (var index = 0; index < parameters.Length; index++)\n            {\n                var attribute = parameters[index].GetCustomAttribute<FromParameterAttribute>();\n                if (attribute == null) continue;\n\n                var parameterType = parameters[index].ParameterType;\n                var parameterName = attribute.ParameterName;\n\n                if (String.IsNullOrEmpty(parameterName)) continue;\n\n                var serialized = context.Connection.GetJobParameter(context.BackgroundJob.Id, parameterName);\n                if (serialized != null)\n                {\n                    object deserialized;\n\n                    if (parameterType == typeof(ExceptionInfo) && DefaultException.Equals(serialized, StringComparison.Ordinal))\n                    {\n                        deserialized = new ExceptionInfo(DeletedState.DefaultException);\n                    }\n                    else\n                    {\n                        deserialized = SerializationHelper.Deserialize(serialized, parameterType, SerializationOption.User);\n                    }\n\n                    argumentsArray[index] = deserialized;\n                }\n            }\n        }\n\n        public void OnPerformed(PerformedContext context)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/JobStorage.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing Hangfire.Server;\nusing Hangfire.States;\nusing Hangfire.Storage;\n\nnamespace Hangfire\n{\n    public abstract class JobStorage\n    {\n        private static readonly object LockObject = new object();\n        private static JobStorage _current;\n\n        private TimeSpan _jobExpirationTimeout = TimeSpan.FromDays(1);\n\n        public static JobStorage Current\n        {\n            get\n            {\n                lock (LockObject)\n                {\n                    if (_current == null)\n                    {\n                        throw new InvalidOperationException(\n                            \"Current JobStorage instance has not been initialized yet. You must set it before using Hangfire Client or Server API. \" +\n#if NET45 || NET46\n                            \"For NET Framework applications please use GlobalConfiguration.UseXXXStorage method, where XXX is the storage type, like `UseSqlServerStorage`.\"\n#else\n                            \"For .NET Core applications please call the `IServiceCollection.AddHangfire` extension method from Hangfire.NetCore or Hangfire.AspNetCore package depending on your application type when configuring the services and ensure service-based APIs are used instead of static ones, like `IBackgroundJobClient` instead of `BackgroundJob` and `IRecurringJobManager` instead of `RecurringJob`.\"\n#endif\n                            );\n                    }\n\n                    return _current;\n                }\n            }\n            set\n            {\n                lock (LockObject)\n                {\n                    _current = value;\n                }\n            }\n        }\n\n        public TimeSpan JobExpirationTimeout\n        {\n            get\n            {\n                return _jobExpirationTimeout;\n            }\n            set\n            {\n                if (value < TimeSpan.Zero)\n                {\n                    throw new ArgumentException(\"JobStorage.JobExpirationTimeout value should be equal or greater than zero.\", nameof(value));\n                }\n\n                _jobExpirationTimeout = value;\n            }\n        }\n\n        public virtual bool LinearizableReads => false;\n\n        public abstract IMonitoringApi GetMonitoringApi();\n\n        public abstract IStorageConnection GetConnection();\n\n        public virtual IStorageConnection GetReadOnlyConnection()\n        {\n            return GetConnection();\n        }\n\n#pragma warning disable 618\n        [Obsolete($\"Please use the `{nameof(GetStorageWideProcesses)}` and/or `{nameof(GetServerRequiredProcesses)}` methods instead, and enable `{nameof(JobStorageFeatures)}.{nameof(JobStorageFeatures.ProcessesInsteadOfComponents)}`. Will be removed in 2.0.0.\")]\n        public virtual IEnumerable<IServerComponent> GetComponents()\n        {\n            return Enumerable.Empty<IServerComponent>();\n        }\n#pragma warning restore 618\n\n        public virtual IEnumerable<IBackgroundProcess> GetServerRequiredProcesses()\n        {\n            return Enumerable.Empty<IBackgroundProcess>();\n        }\n\n        public virtual IEnumerable<IBackgroundProcess> GetStorageWideProcesses()\n        {\n            return Enumerable.Empty<IBackgroundProcess>();\n        }\n\n        public virtual IEnumerable<IStateHandler> GetStateHandlers()\n        {\n            return Enumerable.Empty<IStateHandler>();\n        }\n\n        public virtual void WriteOptionsToLog(ILog logger)\n        {\n        }\n\n        public virtual bool HasFeature([NotNull] string featureId)\n        {\n            if (featureId == null) throw new ArgumentNullException(nameof(featureId));\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/LatencyTimeoutAttribute.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Represents a job filter that <i>automatically deletes a background job</i>,\n    /// when a certain amount of time elapsed since its creation. Deletion\n    /// is taking place when a <see cref=\"Hangfire.Server.Worker\"/> attempts\n    /// to move a job to the <see cref=\"ProcessingState\"/> state.\n    /// </summary>\n    public sealed class LatencyTimeoutAttribute : JobFilterAttribute, IElectStateFilter\n    {\n        private readonly ILog _logger = LogProvider.For<LatencyTimeoutAttribute>();\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"LatencyTimeoutAttribute\"/>\n        /// class with the given timeout value.\n        /// </summary>\n        /// <param name=\"timeoutInSeconds\">Non-negative timeout value in seconds \n        /// that will be used to determine whether to delete a job.</param>\n        /// \n        /// <exception cref=\"ArgumentOutOfRangeException\">\n        ///   <paramref name=\"timeoutInSeconds\"/> has a negative value.\n        /// </exception>\n        public LatencyTimeoutAttribute(int timeoutInSeconds)\n        {\n            if (timeoutInSeconds < 0)\n            {\n                throw new ArgumentOutOfRangeException(nameof(timeoutInSeconds), \"Timeout value must be equal or greater than zero.\");\n            }\n\n            TimeoutInSeconds = timeoutInSeconds;\n            LogLevel = LogLevel.Debug;\n        }\n\n        /// <summary>\n        /// Gets or sets a level for log message that will be produced, when a\n        /// background job was deleted due to exceeded timeout.\n        /// </summary>\n        public LogLevel LogLevel { get; set; }\n        public int TimeoutInSeconds { get; }\n\n        /// <inheritdoc />\n        public void OnStateElection(ElectStateContext context)\n        {\n            var state = context.CandidateState as ProcessingState;\n            if (state == null)\n            {\n                //this filter only accepts Processing\n                return;\n            }\n\n            var elapsedTime = DateTime.UtcNow - context.BackgroundJob.CreatedAt;\n\n            if (elapsedTime.TotalSeconds > TimeoutInSeconds)\n            {\n                context.CandidateState = new DeletedState\n                {\n                    Reason = $\"Background job has exceeded latency timeout of {TimeoutInSeconds} second(s)\"\n                };\n\n                _logger.Log(\n                    LogLevel,\n                    () => $\"Background job '{context.BackgroundJob.Id}' has exceeded latency timeout of {TimeoutInSeconds} second(s) and will be deleted\");\n            }\n        }\n\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/MisfireHandlingMode.cs",
    "content": "// This file is part of Hangfire.\n// Copyright © 2021 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Specifies how to handle missed schedule when processing server was\n    /// inactive.\n    /// </summary>\n    public enum MisfireHandlingMode\n    {\n        /// <summary>\n        /// Default mode. Specifies that only a single background job will\n        /// be created, no matter how many occurrences were missed. The \"Time\"\n        /// parameter for the background job will point to the time background\n        /// job was scheduled.\n        /// </summary>\n        Relaxed = 0,\n\n        /// <summary>\n        /// Specifies that new background job will be created for every missed\n        /// occurrence, with \"Time\" parameter set to the corresponding schedule\n        /// time. \n        /// </summary>\n        Strict  = 1,\n\n        /// <summary>\n        /// Specifies that no background jobs should be created on missed schedule,\n        /// regardless the number of missed occurrences.\n        /// </summary>\n        Ignorable = 2,\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/MoreLinq/MoreEnumerable.Pairwise.cs",
    "content": "#region License and Terms\n// MoreLINQ - Extensions to LINQ to Objects\n// Copyright (c) 2008 Jonathan Skeet. All rights reserved.\n// \n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n// \n//     http://www.apache.org/licenses/LICENSE-2.0\n// \n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n#endregion\n\nnamespace MoreLinq\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Diagnostics;\n\n    static partial class MoreEnumerable\n    {\n        /// <summary>\n        /// Returns a sequence resulting from applying a function to each \n        /// element in the source sequence and its \n        /// predecessor, with the exception of the first element which is \n        /// only returned as the predecessor of the second element.\n        /// </summary>\n        /// <typeparam name=\"TSource\">The type of the elements of <paramref name=\"source\"/>.</typeparam>\n        /// <typeparam name=\"TResult\">The type of the element of the returned sequence.</typeparam>\n        /// <param name=\"source\">The source sequence.</param>\n        /// <param name=\"resultSelector\">A transform function to apply to \n        /// each pair of sequence.</param>\n        /// <returns>\n        /// Returns the resulting sequence.\n        /// </returns>\n        /// <remarks>\n        /// This operator uses deferred execution and streams its results.\n        /// </remarks>\n        /// <example>\n        /// <code>\n        /// int[] numbers = { 123, 456, 789 };\n        /// IEnumerable&lt;int&gt; result = numbers.Pairwise(5, (a, b) => a + b);\n        /// </code>\n        /// The <c>result</c> variable, when iterated over, will yield \n        /// 579 and 1245, in turn.\n        /// </example>\n        public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TResult> resultSelector)\n        {\n            if (source == null) throw new ArgumentNullException(nameof(source));\n            if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));\n            return PairwiseImpl(source, resultSelector);\n        }\n\n        private static IEnumerable<TResult> PairwiseImpl<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TResult> resultSelector)\n        {\n            Debug.Assert(source != null);\n            Debug.Assert(resultSelector != null);\n\n            using (var e = source.GetEnumerator())\n            {\n                if (!e.MoveNext())\n                    yield break;\n\n                var previous = e.Current;\n                while (e.MoveNext())\n                {\n                    yield return resultSelector(previous, e.Current);\n                    previous = e.Current;\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/BootstrapperConfigurationExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire\n{\n    /// <exclude />\n    [Obsolete(\"Please use `AppBuilderExtensions` class instead. Will be removed in version 2.0.0.\")]\n    public static class BootstrapperConfigurationExtensions\n    {\n        /// <summary>\n        /// Tells bootstrapper to start a job server with default options\n        /// on application start and stop it automatically on application\n        /// shutdown request. Global job storage is used.\n        /// </summary>\n        /// <param name=\"configuration\">Configuration</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireServer` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void UseServer(this IBootstrapperConfiguration configuration)\n        {\n            configuration.UseServer(() => new BackgroundJobServer());\n        }\n\n        /// <summary>\n        /// Tells bootstrapper to start a job server with the given\n        /// amount of workers on application start and stop it automatically\n        /// on application shutdown request. Global job storage is used.\n        /// </summary>\n        /// <param name=\"configuration\">Configuration</param>\n        /// <param name=\"workerCount\">Worker count</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireServer` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void UseServer(\n            this IBootstrapperConfiguration configuration,\n            int workerCount)\n        {\n            var options = new BackgroundJobServerOptions\n            {\n                WorkerCount = workerCount\n            };\n\n            configuration.UseServer(() => new BackgroundJobServer(options));\n        }\n\n        /// <summary>\n        /// Tells bootstrapper to start a job server with the given\n        /// queues array on application start and stop it automatically\n        /// on application shutdown request. Global job storage is used.\n        /// </summary>\n        /// <param name=\"configuration\">Configuration</param>\n        /// <param name=\"queues\">Queues to listen</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireServer` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void UseServer(\n            this IBootstrapperConfiguration configuration,\n            params string[] queues)\n        {\n            var options = new BackgroundJobServerOptions\n            {\n                Queues = queues\n            };\n\n            configuration.UseServer(() => new BackgroundJobServer(options));\n        }\n\n        /// <summary>\n        /// Tells bootstrapper to start a job server with the given\n        /// queues array and specified amount of workers on application\n        /// start and stop it automatically on application shutdown request.\n        /// Global job storage is used.\n        /// </summary>\n        /// <param name=\"configuration\">Configuration</param>\n        /// <param name=\"workerCount\">Worker count</param>\n        /// <param name=\"queues\">Queues to listen</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireServer` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void UseServer(\n            this IBootstrapperConfiguration configuration,\n            int workerCount,\n            params string[] queues)\n        {\n            var options = new BackgroundJobServerOptions\n            {\n                WorkerCount = workerCount,\n                Queues = queues\n            };\n\n            configuration.UseServer(() => new BackgroundJobServer(options));\n        }\n\n        /// <summary>\n        /// Tells bootstrapper to start a job server with the given\n        /// options on application start and stop it automatically\n        /// on application shutdown request. Global job storage is used.\n        /// </summary>\n        /// <param name=\"configuration\">Configuration</param>\n        /// <param name=\"options\">Job server options</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireServer` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void UseServer(\n            this IBootstrapperConfiguration configuration,\n            BackgroundJobServerOptions options)\n        {\n            configuration.UseServer(() => new BackgroundJobServer(options));\n        }\n\n        /// <summary>\n        /// Tells bootstrapper to start a job server, that uses\n        /// the given job storage, on application start and stop\n        /// it automatically on application shutdown request.\n        /// </summary>\n        /// <param name=\"configuration\">Configuration</param>\n        /// <param name=\"storage\">Job storage to use</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireServer` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void UseServer(\n            this IBootstrapperConfiguration configuration,\n            JobStorage storage)\n        {\n            configuration.UseServer(() => new BackgroundJobServer(\n                new BackgroundJobServerOptions(),\n                storage));\n        }\n\n        /// <summary>\n        /// Tells bootstrapper to start a job server with the given\n        /// options that use the specified storage (not the global one) on\n        /// application start and stop it automatically on application\n        /// shutdown request.\n        /// </summary>\n        /// <param name=\"configuration\">Configuration</param>\n        /// <param name=\"storage\">Job storage to use</param>\n        /// <param name=\"options\">Job server options</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireServer` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void UseServer(\n            this IBootstrapperConfiguration configuration,\n            JobStorage storage,\n            BackgroundJobServerOptions options)\n        {\n            configuration.UseServer(() => new BackgroundJobServer(options, storage));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/CreateJobFailedException.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Runtime.Serialization;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Client\n{\n    /// <summary>\n    /// The exception that is thrown when a <see cref=\"BackgroundJobClient\"/> class instance\n    /// could not create a job due to another exception was thrown.\n    /// </summary>\n    [Obsolete(\"Please use the `BackgroundJobClientException` instead. Will be removed in 2.0.0.\")]\n#if !NETSTANDARD1_3\n    [Serializable]\n#endif\n    public class CreateJobFailedException : Exception\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"CreateJobFailedException\"/>\n        /// class with a specified error message and a reference to the\n        /// inner exception that is the cause of this exception.\n        /// </summary>\n        /// <param name=\"message\">The error message that explains the reason for the exception.</param>\n        /// <param name=\"inner\">The exception that is the cause of this exception, not null.</param>\n        public CreateJobFailedException(string message, Exception inner) \n            : base(message, inner)\n        {\n        }\n        \n#if !NETSTANDARD1_3\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"CreateJobFailedException\"/> class\n        /// with serialized data.\n        /// </summary>\n        /// <param name=\"info\">The <see cref=\"SerializationInfo\"/> that holds the serialized object data about the exception being thrown.</param>\n        /// <param name=\"context\">The <see cref=\"StreamingContext\"/> that contains contextual information about the source or destination.</param>\n        protected CreateJobFailedException(SerializationInfo info, StreamingContext context)\n            : base(info, context)\n        {\n        }\n#endif\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/DashboardMiddleware.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Net;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Microsoft.Owin;\n\n#pragma warning disable 618\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Dashboard\n{\n    internal sealed class DashboardMiddleware : OwinMiddleware\n    {\n        private readonly string _appPath;\n        private readonly int _statsPollingInterval;\n        private readonly JobStorage _storage;\n        private readonly RouteCollection _routes;\n        private readonly IEnumerable<IAuthorizationFilter> _authorizationFilters;\n\n        public DashboardMiddleware(\n            OwinMiddleware next,\n            string appPath,\n            int statsPollingInterval,\n            [NotNull] JobStorage storage,\n            [NotNull] RouteCollection routes, \n            [NotNull] IEnumerable<IAuthorizationFilter> authorizationFilters)\n            : base(next)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (routes == null) throw new ArgumentNullException(nameof(routes));\n            if (authorizationFilters == null) throw new ArgumentNullException(nameof(authorizationFilters));\n\n            _appPath = appPath;\n            _statsPollingInterval = statsPollingInterval;\n            _storage = storage;\n            _routes = routes;\n            _authorizationFilters = authorizationFilters;\n        }\n\n        public override Task Invoke(IOwinContext owinContext)\n        {\n            var dispatcher = _routes.FindDispatcher(owinContext.Request.Path.Value);\n            \n            if (dispatcher == null)\n            {\n                return Next.Invoke(owinContext);\n            }\n\n            // ReSharper disable once LoopCanBeConvertedToQuery\n            foreach (var filter in _authorizationFilters)\n            {\n                if (!filter.Authorize(owinContext.Environment))\n                {\n                    owinContext.Response.StatusCode = (int) HttpStatusCode.Unauthorized;\n                    return owinContext.Response.WriteAsync(\"401 Unauthorized\");\n                }\n            }\n            \n            var context = new OwinDashboardContext(\n                _storage,\n                new DashboardOptions { AppPath = _appPath, StatsPollingInterval = _statsPollingInterval, AuthorizationFilters = _authorizationFilters }, \n                owinContext.Environment);\n\n            return dispatcher.Item1.Dispatch(context);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/DashboardOwinExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Microsoft.Owin.Infrastructure;\nusing Owin;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Dashboard\n{\n    /// <exclude />\n    [Obsolete(\"Please use `IAppBuilder.UseHangfireDashboard` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n    public static class DashboardOwinExtensions\n    {\n        internal static readonly IAuthorizationFilter[] DefaultAuthorizationFilters =\n        {\n            new LocalRequestsOnlyAuthorizationFilter()\n        };\n\n        internal static readonly string DefaultDashboardPath = \"/hangfire\";\n        internal static readonly string DefaultAppPath = \"/\";\n\n        /// <summary>\n        /// Maps dashboard to the app builder pipeline at \"/hangfire\"\n        /// with authorization filter that blocks all remote requests\n        /// and <see cref=\"JobStorage.Current\"/> storage instance.\n        /// </summary>\n        /// <param name=\"app\">The app builder</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireDashboard` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void MapHangfireDashboard(this IAppBuilder app)\n        {\n            MapHangfireDashboard(app, DefaultDashboardPath, DefaultAppPath);\n        }\n\n        /// <summary>\n        /// Maps dashboard to the app builder pipeline at the specified\n        /// path with authorization filter that blocks all remote requests\n        /// and <see cref=\"JobStorage.Current\"/> storage instance.\n        /// </summary>\n        /// <param name=\"app\">The app builder</param>\n        /// <param name=\"dashboardPath\">The path to map dashboard</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireDashboard` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void MapHangfireDashboard(\n            this IAppBuilder app,\n            string dashboardPath)\n        {\n            MapHangfireDashboard(app, dashboardPath, DefaultAppPath, DefaultAuthorizationFilters);\n        }\n\n        /// <summary>\n        /// Maps dashboard to the app builder pipeline at the specified\n        /// path with authorization filter that blocks all remote requests\n        /// and <see cref=\"JobStorage.Current\"/> storage instance.\n        /// </summary>\n        /// <param name=\"app\">The app builder</param>\n        /// <param name=\"dashboardPath\">The path to map dashboard</param>\n        /// <param name=\"appPath\">The application path on Back To Site link. Pass null in order to hide the Back To Site link.</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireDashboard` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void MapHangfireDashboard(\n            this IAppBuilder app,\n            string dashboardPath,\n            string appPath)\n        {\n            MapHangfireDashboard(app, dashboardPath, appPath, DefaultAuthorizationFilters);\n        }\n\n        /// <summary>\n        /// Maps dashboard to the app builder pipeline at the specified\n        /// path with given authorization filters that apply to any request\n        /// and <see cref=\"JobStorage.Current\"/> storage instance.\n        /// </summary>\n        /// <param name=\"app\">The app builder</param>\n        /// <param name=\"dashboardPath\">The path to map dashboard</param>\n        /// <param name=\"appPath\">The application path on Back To Site link</param>\n        /// <param name=\"authorizationFilters\">Array of authorization filters</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireDashboard` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void MapHangfireDashboard(\n            this IAppBuilder app, \n            string dashboardPath,\n            string appPath,\n            IEnumerable<IAuthorizationFilter> authorizationFilters)\n        {\n            MapHangfireDashboard(app, dashboardPath, appPath, authorizationFilters, JobStorage.Current);\n        }\n\n        /// <summary>\n        /// Maps dashboard to the app builder pipeline at the specified path\n        /// with given authorization filters that apply to any request and\n        /// storage instance that is used to query the information.\n        /// </summary>\n        /// <param name=\"app\">The app builder</param>\n        /// <param name=\"dashboardPath\">The path to map dashboard</param>\n        /// <param name=\"appPath\">The application path on Back To Site link</param>\n        /// <param name=\"authorizationFilters\">Array of authorization filters</param>\n        /// <param name=\"storage\">The storage instance</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireDashboard` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void MapHangfireDashboard(\n            [NotNull] this IAppBuilder app,\n            string dashboardPath,\n            string appPath,\n            IEnumerable<IAuthorizationFilter> authorizationFilters,\n            JobStorage storage)\n        {\n            if (app == null) throw new ArgumentNullException(nameof(app));\n\n            SignatureConversions.AddConversions(app);\n\n            app.Map(dashboardPath, subApp => subApp.Use<DashboardMiddleware>(\n                appPath,\n                storage,\n                DashboardRoutes.Routes,\n                authorizationFilters));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/IAuthorizationFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Dashboard\n{\n    [Obsolete(\"Please use `IDashboardAuthorizationFilter` instead. Will be removed in 2.0.0.\")]\n    public interface IAuthorizationFilter\n    {\n        bool Authorize([NotNull] IDictionary<string, object> owinEnvironment);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/IBootstrapperConfiguration.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Dashboard;\n\nnamespace Hangfire\n{\n    /// <exclude />\n    /// <summary>\n    /// Represents a configuration class for Hangfire components that\n    /// is used by the <see cref=\"OwinBootstrapper\"/> class.\n    /// </summary>\n    [Obsolete(\"Please use `GlobalConfiguration` class instead. Will be removed in version 2.0.0.\")]\n    public interface IBootstrapperConfiguration\n    {\n        /// <summary>\n        /// Tells bootstrapper to pass the given collection of filters\n        /// to the dashboard middleware to authorize dashboard requests. \n        /// Previous calls to this method are ignored. Empty array \n        /// enables access for all users.\n        /// </summary>\n        /// <param name=\"filters\">Authorization filters</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireDashboard(\\\"/hangfire\\\", new DashboardOptions { AuthorizationFilters = filters })` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        void UseAuthorizationFilters(params IAuthorizationFilter[] filters);\n\n        /// <summary>\n        /// Tells bootstrapper to register the given job filter globally.\n        /// </summary>\n        /// <param name=\"filter\">Job filter instance</param>\n        [Obsolete(\"Please use `GlobalConfiguration.UseFilter` instead. Will be removed in version 2.0.0.\")]\n        void UseFilter(object filter);\n\n        /// <summary>\n        /// Tells bootstrapper to map the dashboard middleware to the\n        /// given path in the OWIN pipeline. \n        /// </summary>\n        /// <param name=\"path\">Dashboard path, '/hangfire' by default</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireDashboard(string pathMatch)` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        void UseDashboardPath(string path);\n\n        /// <summary>\n        /// Tells bootstrapper to use the given path on Back To Site link in the dashboard.\n        /// </summary>\n        /// <param name=\"path\">Back To Site path, '/' by default</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireDashboard(\\\"/hangfire\\\", new DashboardOptions { AppPath = path })` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        void UseAppPath(string path);\n\n        /// <summary>\n        /// Tells bootstrapper to register the given instance of the\n        /// <see cref=\"JobStorage\"/> class globally.\n        /// </summary>\n        /// <param name=\"storage\">Job storage</param>\n        [Obsolete(\"Please use `GlobalConfiguration.UseStorage` instead. Will be removed in version 2.0.0.\")]\n        void UseStorage(JobStorage storage);\n\n        /// <summary>\n        /// Tells bootstrapper to register the given instance of the\n        /// <see cref=\"JobActivator\"/> class globally.\n        /// </summary>\n        /// <param name=\"activator\">Job storage</param>\n        [Obsolete(\"Please use `GlobalConfiguration.UseActivator` instead. Will be removed in version 2.0.0.\")]\n        void UseActivator(JobActivator activator);\n\n        /// <summary>\n        /// Tells bootstrapper to start the given job server on application\n        /// start, and stop it automatically on application shutdown request.\n        /// </summary>\n        /// <param name=\"server\">Job server</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireServer` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        void UseServer(Func<BackgroundJobServer> server);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/IRequestDispatcher.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading.Tasks;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Dashboard\n{\n    [Obsolete(\"Use the `IDashboardDispatcher` interface instead. Will be removed in 2.0.0.\")]\n    public interface IRequestDispatcher\n    {\n        Task Dispatch(RequestDispatcherContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/IServerComponent.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Server\n{\n    /// <exclude />\n    [Obsolete(\"Please use the `IBackgroundProcess` interface where you can. Will be removed in 2.0.0.\")]\n    public interface IServerComponent : IServerProcess\n    {\n        void Execute(CancellationToken cancellationToken);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/IServerProcess.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Server\n{\n    /// <exclude />\n    [Obsolete(\"Please use the `IBackgroundProcess` interface where you can. Will be removed in 2.0.0.\")]\n    public interface IServerProcess\n    {\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/Job.Obsolete.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing Hangfire.Annotations;\nusing Hangfire.Server;\nusing Hangfire.Storage;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Common\n{\n    partial class Job\n    {\n        [Obsolete(\"Please use Job(Type, MethodInfo, object[]) ctor overload instead. Will be removed in 2.0.0.\")]\n        public Job([NotNull] Type type, [NotNull] MethodInfo method, [NotNull] string[] arguments)\n        {\n            if (type == null) throw new ArgumentNullException(nameof(type));\n            if (method == null) throw new ArgumentNullException(nameof(method));\n            if (arguments == null) throw new ArgumentNullException(nameof(arguments));\n\n            Validate(type, nameof(type), method, nameof(method), arguments.Length, nameof(arguments));\n\n            Type = type;\n            Method = method;\n            Args = InvocationData.DeserializeArguments(method, arguments);\n        }\n\n        /// <exclude />\n        [NotNull]\n        [Obsolete(\"Please use `Args` property instead to avoid unnecessary serializations/deserializations. Will be deleted in 2.0.0.\")]\n        public string[] Arguments => InvocationData.SerializeArguments(Method, Args);\n\n        /// <exclude />\n        [Obsolete(\"This method is deprecated. Please use `CoreBackgroundJobPerformer` or `BackgroundJobPerformer` classes instead. Will be removed in 2.0.0.\")]\n        public object Perform(JobActivator activator, IJobCancellationToken cancellationToken)\n        {\n            if (activator == null) throw new ArgumentNullException(nameof(activator));\n            if (cancellationToken == null) throw new ArgumentNullException(nameof(cancellationToken));\n\n            object instance = null;\n\n            object result;\n            try\n            {\n                if (!Method.IsStatic)\n                {\n                    instance = Activate(activator);\n                }\n\n                var arguments = GetArguments(cancellationToken);\n                result = InvokeMethod(instance, arguments, cancellationToken);\n            }\n            finally\n            {\n                Dispose(instance);\n            }\n\n            return result;\n        }\n\n        [Obsolete(\"Will be removed in 2.0.0\")]\n        private object Activate(JobActivator activator)\n        {\n            try\n            {\n                var instance = activator.ActivateJob(Type);\n\n                if (instance == null)\n                {\n                    throw new InvalidOperationException($\"JobActivator returned NULL instance of the '{Type}' type.\");\n                }\n\n                return instance;\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new JobPerformanceException(\n                    \"An exception occurred during job activation.\",\n                    ex);\n            }\n        }\n\n        [Obsolete(\"Will be removed in 2.0.0\")]\n        private object[] GetArguments(IJobCancellationToken cancellationToken)\n        {\n            try\n            {\n                var parameters = Method.GetParameters();\n                var result = new List<object>(Args.Count);\n\n                for (var i = 0; i < parameters.Length; i++)\n                {\n                    var parameter = parameters[i];\n                    var argument = Args[i];\n\n                    object value;\n\n                    if (typeof(IJobCancellationToken).GetTypeInfo().IsAssignableFrom(parameter.ParameterType.GetTypeInfo()))\n                    {\n                        value = cancellationToken;\n                    }\n                    else\n                    {\n                        value = argument;\n                    }\n\n                    result.Add(value);\n                }\n\n                return result.ToArray();\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new JobPerformanceException(\n                    \"An exception occurred during arguments deserialization.\",\n                    ex);\n            }\n        }\n\n        [Obsolete(\"Will be removed in 2.0.0\")]\n        private object InvokeMethod(object instance, object[] deserializedArguments, IJobCancellationToken cancellationToken)\n        {\n            try\n            {\n                return Method.Invoke(instance, deserializedArguments);\n            }\n            catch (TargetInvocationException ex)\n            {\n                CoreBackgroundJobPerformer.HandleJobPerformanceException(ex.InnerException, cancellationToken, null);\n                throw;\n            }\n        }\n\n        [Obsolete(\"Will be removed in 2.0.0\")]\n        private static void Dispose(object instance)\n        {\n            try\n            {\n                var disposable = instance as IDisposable;\n                disposable?.Dispose();\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new JobPerformanceException(\n                    \"Job has been performed, but an exception occurred during disposal.\",\n                    ex);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/OwinBootstrapper.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Hangfire.Dashboard;\nusing Hangfire.Server;\nusing Owin;\n\nnamespace Hangfire\n{\n    /// <exclude />\n    [Obsolete(\"Please use `GlobalConfiguration` class for configuration, or `IAppBuilder.UseHangfireDashboard` and `IAppBuilder.UseHangfireServer` OWIN extension methods instead. Will be removed in version 2.0.0.\")]\n    public static class OwinBootstrapper\n    {\n        /// <summary>\n        /// Bootstraps Hangfire components using the given configuration\n        /// action and maps Hangfire Dashboard to the app builder pipeline\n        /// at the configured path ('/hangfire' by default).\n        /// </summary>\n        /// <param name=\"app\">The app builder</param>\n        /// <param name=\"configurationAction\">Configuration action</param>\n        [Obsolete(\"Please use `GlobalConfiguration` class for configuration, or `IAppBuilder.UseHangfireDashboard` and `IAppBuilder.UseHangfireServer` OWIN extension methods instead. Will be removed in version 2.0.0.\")]\n        public static void UseHangfire(\n            [NotNull] this IAppBuilder app,\n            [NotNull] Action<IBootstrapperConfiguration> configurationAction)\n        {\n            if (app == null) throw new ArgumentNullException(nameof(app));\n            if (configurationAction == null) throw new ArgumentNullException(nameof(configurationAction));\n\n            var configuration = new BootstrapperConfiguration();\n            configurationAction(configuration);\n\n            if (configuration.Activator != null)\n            {\n                JobActivator.Current = configuration.Activator;\n            }\n\n            if (configuration.Storage != null)\n            {\n                JobStorage.Current = configuration.Storage;\n            }\n\n            foreach (var filter in configuration.Filters)\n            {\n                GlobalJobFilters.Filters.Add(filter);\n            }\n\n            foreach (var server in configuration.Servers)\n            {\n                app.RunHangfireServer(server());\n            }\n\n            app.MapHangfireDashboard(configuration.DashboardPath, configuration.AppPath, configuration.AuthorizationFilters);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/RequestDispatcherContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text.RegularExpressions;\nusing Hangfire.Annotations;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Dashboard\n{\n    [Obsolete(\"Use the `DashboardContext` class instead. Will be removed in 2.0.0.\")]\n    public class RequestDispatcherContext\n    {\n        public RequestDispatcherContext(\n            string appPath,\n            int statsPollingInterval,\n            [NotNull] JobStorage jobStorage,\n            [NotNull] IDictionary<string, object> owinEnvironment,\n            [NotNull] Match uriMatch)\n        {\n            if (jobStorage == null) throw new ArgumentNullException(nameof(jobStorage));\n            if (owinEnvironment == null) throw new ArgumentNullException(nameof(owinEnvironment));\n            if (uriMatch == null) throw new ArgumentNullException(nameof(uriMatch));\n\n            AppPath = appPath;\n            StatsPollingInterval = statsPollingInterval;\n            JobStorage = jobStorage;\n            OwinEnvironment = owinEnvironment;\n            UriMatch = uriMatch;\n        }\n\n        public string AppPath { get; }\n        public int StatsPollingInterval { get; }\n        public JobStorage JobStorage { get; }\n        public IDictionary<string, object> OwinEnvironment { get; } \n        public Match UriMatch { get; }\n\n        public static RequestDispatcherContext FromDashboardContext([NotNull] DashboardContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            var owinContext = context as OwinDashboardContext;\n            if (owinContext == null)\n            {\n                throw new NotSupportedException($\"context must be of type '{nameof(OwinDashboardContext)}'\");\n            }\n            \n            return new RequestDispatcherContext(\n                owinContext.Options.AppPath,\n                owinContext.Options.StatsPollingInterval,\n                owinContext.Storage,\n                owinContext.Environment,\n                owinContext.UriMatch);\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/RequestDispatcherWrapper.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Dashboard\n{\n    [Obsolete(\"Use IDashboardDispatcher-based dispatchers instead. Will be removed in 2.0.0.\")]\n    public class RequestDispatcherWrapper : IDashboardDispatcher\n    {\n        private readonly IRequestDispatcher _dispatcher;\n        \n        public RequestDispatcherWrapper([NotNull] IRequestDispatcher dispatcher)\n        {\n            if (dispatcher == null) throw new ArgumentNullException(nameof(dispatcher));\n            _dispatcher = dispatcher;\n        }\n\n        public Task Dispatch(DashboardContext context)\n        {\n            return _dispatcher.Dispatch(RequestDispatcherContext.FromDashboardContext(context));\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/ServerOwinExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Threading;\nusing Microsoft.Owin;\nusing Owin;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Server\n{\n    /// <exclude />\n    [Obsolete(\"Please use `IAppBuilder.UseHangfireServer` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n    public static class ServerOwinExtensions\n    {\n        // Prevent GC to collect background servers in hosts that do not\n        // support shutdown notifications.\n        private static readonly ConcurrentBag<BackgroundJobServer> Servers \n            = new ConcurrentBag<BackgroundJobServer>(); \n\n        /// <summary>\n        /// Starts the specified background job server and registers the call\n        /// to its `Dispose` method at OWIN application's shutdown event.\n        /// </summary>\n        /// <param name=\"app\">The app builder</param>\n        /// <param name=\"server\">The background job server to start</param>\n        [Obsolete(\"Please use `IAppBuilder.UseHangfireServer` OWIN extension method instead. Will be removed in version 2.0.0.\")]\n        public static void RunHangfireServer(\n            this IAppBuilder app,\n            BackgroundJobServer server)\n        {\n            Servers.Add(server);\n\n            server.Start();\n\n            var context = new OwinContext(app.Properties);\n            var token = context.Get<CancellationToken>(\"host.OnAppDisposing\");\n\n            if (token != CancellationToken.None)\n            {\n                token.Register(server.Dispose);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/ServerWatchdogOptions.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Server\n{\n    /// <exclude />\n    [Obsolete(\"Please use `BackgroundJobServerOptions` properties instead. Will be removed in 2.0.0.\")]\n    public class ServerWatchdogOptions\n    {\n        private TimeSpan _serverTimeout;\n        private TimeSpan _checkInterval;\n\n        public ServerWatchdogOptions()\n        {\n            ServerTimeout = ServerWatchdog.DefaultServerTimeout;\n            CheckInterval = ServerWatchdog.DefaultCheckInterval;\n        }\n\n        public TimeSpan ServerTimeout\n        {\n            get { return _serverTimeout; }\n            set\n            {\n                if (value < TimeSpan.Zero || value > ServerWatchdog.MaxServerTimeout)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), $\"ServerTimeout must be either non-negative and equal to or less than {ServerWatchdog.MaxServerTimeout.Hours} hours\");\n                }\n\n                _serverTimeout = value;\n            }\n        }\n\n        public TimeSpan CheckInterval\n        {\n            get { return _checkInterval; }\n            set\n            {\n                if (value < TimeSpan.Zero || value > ServerWatchdog.MaxServerCheckInterval)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), $\"CheckInterval must be either non-negative and equal to or less than {ServerWatchdog.MaxServerCheckInterval.Hours} hours\");\n\n                };\n                _checkInterval = value;\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/StartupConfiguration.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Dashboard;\n\nnamespace Hangfire\n{\n    /// <exclude />\n    [Obsolete]\n    internal sealed class BootstrapperConfiguration : IBootstrapperConfiguration\n    {\n        public BootstrapperConfiguration()\n        {\n            Servers = new List<Func<BackgroundJobServer>>();\n            DashboardPath = DashboardOwinExtensions.DefaultDashboardPath;\n            AppPath = DashboardOwinExtensions.DefaultAppPath;\n\n            AuthorizationFilters = DashboardOwinExtensions.DefaultAuthorizationFilters;\n\n            Filters = new List<object>();\n        }\n\n        public string DashboardPath { get; private set; }\n        public string AppPath { get; private set; }\n        public JobStorage Storage { get; private set; }\n        public JobActivator Activator { get; private set; }\n        public List<Func<BackgroundJobServer>> Servers { get; }\n        public IAuthorizationFilter[] AuthorizationFilters { get; private set; }\n        public List<object> Filters { get; } \n\n        public void UseAuthorizationFilters(params IAuthorizationFilter[] filters)\n        {\n            AuthorizationFilters = filters;\n        }\n\n        public void UseFilter(object filter)\n        {\n            Filters.Add(filter);\n        }\n\n        public void UseDashboardPath(string path)\n        {\n            DashboardPath = path;\n        }\n\n        public void UseAppPath(string path)\n        {\n            AppPath = path;\n        }\n\n        public void UseStorage(JobStorage storage)\n        {\n            Storage = storage;\n        }\n\n        public void UseActivator(JobActivator activator)\n        {\n            Activator = activator;\n        }\n\n        public void UseServer(Func<BackgroundJobServer> server)\n        {\n            Servers.Add(server);\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Obsolete/StateContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.States\n{\n    /// <exclude />\n    [Obsolete(\"This class is here for compatibility reasons. Will be removed in 2.0.0.\")]\n    public abstract class StateContext\n    {\n        [NotNull]\n        public abstract BackgroundJob BackgroundJob { get; }\n\n        [NotNull]\n        [Obsolete(\"Please use BackgroundJob property instead. Will be removed in 2.0.0.\")]\n        public string JobId => BackgroundJob.Id;\n\n        [CanBeNull]\n        [Obsolete(\"Please use BackgroundJob property instead. Will be removed in 2.0.0.\")]\n        public Job Job => BackgroundJob.Job;\n\n        [Obsolete(\"Please use BackgroundJob property instead. Will be removed in 2.0.0.\")]\n        public DateTime CreatedAt => BackgroundJob.CreatedAt;\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Processing/AppDomainUnloadMonitor.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\n#if !NETSTANDARD1_3\nusing System;\nusing System.Threading;\n\nnamespace Hangfire.Processing\n{\n    internal static class AppDomainUnloadMonitor\n    {\n        private static int _initialized;\n        private static bool _isUnloading;\n\n        public static void EnsureInitialized()\n        {\n            if (Interlocked.CompareExchange(ref _initialized, 1, 0) == 0)\n            {\n                AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;\n                AppDomain.CurrentDomain.ProcessExit += OnDomainUnload;\n            }\n        }\n\n        public static bool IsUnloading => Volatile.Read(ref _isUnloading) || Server.AspNetShutdownDetector.DisposingHttpRuntime;\n\n        private static void OnDomainUnload(object sender, EventArgs args)\n        {\n            Volatile.Write(ref _isUnloading, true);\n        }\n    }\n}\n#endif\n"
  },
  {
    "path": "src/Hangfire.Core/Processing/BackgroundDispatcher.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\n#if !NETSTANDARD1_3\nusing System.Diagnostics;\n#endif\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing ThreadState = System.Threading.ThreadState;\n\nnamespace Hangfire.Processing\n{\n    internal sealed class BackgroundDispatcher : IBackgroundDispatcher\n    {\n        private readonly ILog _logger = LogProvider.GetLogger(typeof(BackgroundDispatcher));\n        private readonly CountdownEvent _stopped;\n\n        private readonly IBackgroundExecution _execution;\n        private readonly Action<Guid, object> _action;\n        private readonly object _state;\n\n        public BackgroundDispatcher(\n            [NotNull] IBackgroundExecution execution,\n            [NotNull] Action<Guid, object> action,\n            [CanBeNull] object state,\n            [NotNull] Func<ThreadStart, IEnumerable<Thread>> threadFactory)\n        {\n            if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory));\n\n            _execution = execution ?? throw new ArgumentNullException(nameof(execution));\n            _action = action ?? throw new ArgumentNullException(nameof(action));\n            _state = state;\n\n#if !NETSTANDARD1_3\n            AppDomainUnloadMonitor.EnsureInitialized();\n#endif\n\n            var threads = threadFactory(DispatchLoop)?.ToArray();\n\n            if (threads == null || threads.Length == 0)\n            {\n                throw new ArgumentException(\"At least one unstarted thread should be created.\", nameof(threadFactory));\n            }\n\n            if (threads.Any(static thread => thread == null || (thread.ThreadState & ThreadState.Unstarted) == 0))\n            {\n                throw new ArgumentException(\"All the threads should be non-null and in the ThreadState.Unstarted state.\", nameof(threadFactory));\n            }\n\n            _stopped = new CountdownEvent(threads.Length);\n\n            foreach (var thread in threads)\n            {\n                thread.Start();\n            }\n        }\n\n        public bool Wait(TimeSpan timeout)\n        {\n            return _stopped.WaitHandle.WaitOne(timeout);\n        }\n\n        public async Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)\n        {\n            await _stopped.WaitHandle.WaitOneAsync(timeout, cancellationToken).ConfigureAwait(false);\n        }\n\n        public void Dispose()\n        {\n            _execution.Dispose();\n            _stopped.Dispose();\n        }\n\n        public override string ToString()\n        {\n            return _execution.ToString();\n        }\n\n        private void DispatchLoop()\n        {\n            try\n            {\n                _execution.Run(_action, _state);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n#if !NETSTANDARD1_3\n                if (!(ex is ThreadAbortException) || !AppDomainUnloadMonitor.IsUnloading)\n#endif\n                {\n                    try\n                    {\n                        _logger.FatalException(\"Dispatcher is stopped due to an exception, you need to restart the server manually. Please report it to Hangfire developers.\", ex);\n                    }\n                    catch (Exception inner) when (inner.IsCatchableExceptionType())\n                    {\n#if !NETSTANDARD1_3\n                        Debug.WriteLine($\"Dispatcher is stopped due to an exception, you need to restart the server manually. Please report it to Hangfire developers: {ex}\");\n#endif\n                    }\n                }\n            }\n            finally\n            {\n                try\n                {\n                    _stopped.Signal();\n                }\n                catch (ObjectDisposedException)\n                {\n#if !NETSTANDARD1_3\n                    Debug.WriteLine(\"Unable to signal the stopped event for BackgroundDispatcher: it was already disposed\");\n#endif\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Processing/BackgroundDispatcherAsync.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n#if !NETSTANDARD1_3\nusing System.Diagnostics;\n#endif\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\n\nnamespace Hangfire.Processing\n{\n    internal sealed class BackgroundDispatcherAsync : IBackgroundDispatcher\n    {\n        private readonly ILog _logger = LogProvider.GetLogger(typeof(BackgroundDispatcherAsync));\n        private readonly CountdownEvent _stopped;\n\n        private readonly IBackgroundExecution _execution;\n        private readonly Func<Guid, object, Task> _action;\n        private readonly object _state;\n\n        private readonly TaskScheduler _taskScheduler;\n        private readonly bool _ownsScheduler;\n\n        public BackgroundDispatcherAsync(\n            [NotNull] IBackgroundExecution execution,\n            [NotNull] Func<Guid, object, Task> action,\n            [CanBeNull] object state,\n            [NotNull] TaskScheduler taskScheduler,\n            int maxConcurrency,\n            bool ownsScheduler)\n        {\n            if (maxConcurrency <= 0) throw new ArgumentOutOfRangeException(nameof(maxConcurrency));\n\n            _execution = execution ?? throw new ArgumentNullException(nameof(execution));\n            _action = action ?? throw new ArgumentNullException(nameof(action));\n            _state = state;\n            _taskScheduler = taskScheduler ?? throw new ArgumentNullException(nameof(taskScheduler));\n            _ownsScheduler = ownsScheduler;\n\n#if !NETSTANDARD1_3\n            AppDomainUnloadMonitor.EnsureInitialized();\n#endif\n\n            _stopped = new CountdownEvent(maxConcurrency);\n\n            for (var i = 0; i < maxConcurrency; i++)\n            {\n                Task.Factory.StartNew(\n                    DispatchLoop,\n                    CancellationToken.None,\n                    TaskCreationOptions.None,\n                    _taskScheduler).Unwrap();\n            }\n        }\n\n        public bool Wait(TimeSpan timeout)\n        {\n            return _stopped.WaitHandle.WaitOne(timeout);\n        }\n\n        public async Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)\n        {\n            await _stopped.WaitHandle.WaitOneAsync(timeout, cancellationToken).ConfigureAwait(false);\n        }\n\n        public void Dispose()\n        {\n            _execution.Dispose();\n            _stopped.Dispose();\n\n            if (_ownsScheduler && _taskScheduler is IDisposable disposableScheduler)\n            {\n                disposableScheduler.Dispose();\n            }\n        }\n\n        public override string ToString()\n        {\n            return _execution.ToString();\n        }\n\n        private async Task DispatchLoop()\n        {\n            try\n            {\n                await _execution.RunAsync(_action, _state).ConfigureAwait(true);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n#if !NETSTANDARD1_3\n                if (!(ex is ThreadAbortException) || !AppDomainUnloadMonitor.IsUnloading)\n#endif\n                {\n                    try\n                    {\n                        _logger.FatalException(\"Dispatcher is stopped due to an exception, you need to restart the server manually. Please report it to Hangfire developers.\", ex);\n                    }\n                    catch (Exception inner) when (inner.IsCatchableExceptionType())\n                    {\n#if !NETSTANDARD1_3\n                        Debug.WriteLine($\"Dispatcher is stopped due to an exception, you need to restart the server manually. Please report it to Hangfire developers: {ex}\");\n#endif\n                    }\n                }\n            }\n            finally\n            {\n                try\n                {\n                    _stopped.Signal();\n                }\n                catch (ObjectDisposedException)\n                {\n#if !NETSTANDARD1_3\n                    Debug.WriteLine(\"Unable to signal the stopped event for BackgroundDispatcher: it was already disposed\");\n#endif\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Processing/BackgroundExecution.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing ThreadState = System.Threading.ThreadState;\n\nnamespace Hangfire.Processing\n{\n    internal sealed class BackgroundExecution : IBackgroundExecution\n    {\n        // This fallback strategy is used for defensive purposes, when there are\n        // problems with obtaining retry delays we should not under any circumstances\n        // fall into constant immediate retries, consuming 100% of CPU.\n        private static readonly TimeSpan FallbackRetryDelay = TimeSpan.FromSeconds(5);\n\n        // Execution can be in one of three states: Running, Faulted or Failed. Each\n        // one defines its own logging rules to lower the number of logged messages,\n        // to not to make stress on logging subsystem with thousands of messages in\n        // case of transient faults.\n        private readonly ManualResetEvent _stopped = new ManualResetEvent(false);\n        private Stopwatch _faultedSince;\n        private Stopwatch _failedSince;\n        private Stopwatch _lastException;\n        private int _exceptionsCount;\n\n        private CancellationToken _stopToken;\n        private readonly BackgroundExecutionOptions _options;\n        private readonly ILog _logger;\n\n        private readonly Stopwatch _createdAt;\n        private Stopwatch _stoppedAt;\n        private CancellationTokenRegistration _stopRegistration;\n\n        private volatile bool _disposed;\n\n        public BackgroundExecution([NotNull] BackgroundExecutionOptions options, CancellationToken stopToken)\n        {\n            _options = options ?? throw new ArgumentNullException(nameof(options));\n\n            _logger = LogProvider.GetLogger(GetType());\n            _createdAt = Stopwatch.StartNew();\n\n            _stopToken = stopToken;\n\n            _stopRegistration = _stopToken.Register(SetStoppedAt);\n\n#if !NETSTANDARD1_3\n            AppDomainUnloadMonitor.EnsureInitialized();\n#endif\n        }\n\n        public bool StopRequested => _disposed || _stopToken.IsCancellationRequested;\n\n        public void Run(Action<Guid, object> callback, object state)\n        {\n            if (callback == null) throw new ArgumentNullException(nameof(callback));\n\n            var executionId = Guid.NewGuid();\n\n            // ExecutionId is a custom correlation id for logging purposes. We can use Thread's\n            // ManagedThreadId property here, but it's better to have a single implementation between\n            // sync and async dispatchers - async one can execute related tasks on different threads,\n            // so ManagedThreadId doesn't work there.\n            //using (LogProvider.OpenMappedContext(\"ExecutionId\", executionId.ToString()))\n            {\n#if !NETSTANDARD1_3\n                try\n#endif\n                {\n                    HandleStarted(executionId, out var nextDelay);\n\n                    // There should be no operations between the `while` and `try` blocks to\n                    // avoid unintended stopping due to ThreadAbortException between the loop\n                    // iterations. Even loop condition is placed into the `try` block.\n                    while (true)\n                    {\n                        // Don't place anything here.\n                        try\n                        {\n                            // All possible exceptions should be handled inside this try/catch\n                            // block, including ThreadAbortException (ResetAbort is called when\n                            // possible) and ThreadInterruptedException. The loop could only be\n                            // interrupted by the corresponding cancellation token, or thread\n                            // abort, caused by appdomain unload.\n\n                            // Don't use ThrowIfCancellationRequested here, because it may cause\n                            // infinite looping when ThreadAbortException is raised during app\n                            // domain unloads.\n                            if (StopRequested) break;\n\n                            if (nextDelay != TimeSpan.Zero)\n                            {\n                                if (!HandleDelay(executionId, nextDelay))\n                                {\n                                    // Inability to handle the delay means that execution was\n                                    // already stopped, so we should break the loop.\n                                    break;\n                                }\n                            }\n\n                            callback(executionId, state);\n                            HandleSuccess(out nextDelay);\n                        }\n#if !NETSTANDARD1_3\n                        catch (ThreadAbortException) when (AppDomainUnloadMonitor.IsUnloading)\n                        {\n                            // Our thread is aborted due to AppDomain unload. It's better to give up to\n                            // not to cause the host to be more aggressive.\n                            throw;\n                        }\n#endif\n                        catch (OperationCanceledException ex) when (ex.CancellationToken.Equals(_stopToken) || StopRequested)\n                        {\n                            // We are catching general OCE exception without checking its CancellationToken\n                            // property, because the concrete token may be different than our one, for\n                            // example when using linked token sources. When we get OCE and our stop token\n                            // is canceled, we can simply break the execution loop, because it will be\n                            // broken on next iteration anyway.\n                            break;\n                        }\n                        catch (Exception ex) when (ex.IsCatchableExceptionType())\n                        {\n                            HandleException(executionId, ex, out nextDelay);\n                        }\n                    }\n\n                    HandleStop(executionId);\n                }\n#if !NETSTANDARD1_3\n                catch (ThreadAbortException ex)\n                {\n                    // This is a rude stop. Since we are handling all the thread aborts\n                    // inside the loop, only appdomain unload can bring us there. In this\n                    // case we don't reset thread abort.\n                    HandleThreadAbort(executionId, ex);\n                }\n#endif\n            }\n        }\n\n        public async Task RunAsync(Func<Guid, object, Task> callback, object state)\n        {\n            if (callback == null) throw new ArgumentNullException(nameof(callback));\n\n            var executionId = Guid.NewGuid();\n\n            // We can't use Thread.ManagedThreadId to provide a correlation id for logging subsystem,\n            // because task and its continuations can run in different threads due to work stealing\n            // nature of fetching. So instead we are using a custom identifier, hoping OpenMappedContext\n            // implementation uses AsyncLocal instead of ThreadLocal ;-)\n            //using (LogProvider.OpenMappedContext(\"ExecutionId\", executionId.ToString()))\n            {\n#if !NETSTANDARD1_3\n                try\n#endif\n                {\n                    HandleStarted(executionId, out var nextDelay);\n\n                    // There should be no operations between the `while` and `try` blocks to\n                    // avoid unintended stopping due to ThreadAbortException between the loop\n                    // iterations. Even loop condition is placed into the `try` block.\n                    while (true)\n                    {\n                        // Don't place anything here.\n                        try\n                        {\n                            // All possible exceptions should be handled inside this try/catch\n                            // block, including ThreadAbortException (ResetAbort is called when\n                            // possible) and ThreadInterruptedException. The loop could only be\n                            // interrupted by the corresponding cancellation token, or thread\n                            // abort, caused by appdomain unload.\n\n                            // Don't use ThrowIfCancellationRequested here, because it may cause\n                            // infinite looping when ThreadAbortException is raised during app\n                            // domain unloads.\n                            if (StopRequested) break;\n\n                            if (nextDelay != TimeSpan.Zero)\n                            {\n                                if (!await HandleDelayAsync(executionId, nextDelay).ConfigureAwait(true))\n                                {\n                                    // Inability to handle the delay means that execution was\n                                    // already stopped, so we should break the loop.\n                                    break;\n                                }\n                            }\n\n                            await callback(executionId, state).ConfigureAwait(true);\n                            HandleSuccess(out nextDelay);\n                        }\n#if !NETSTANDARD1_3\n                        catch (ThreadAbortException) when (AppDomainUnloadMonitor.IsUnloading)\n                        {\n                            // Our previous task was aborted due to AppDomain unload. It's better to\n                            // give up to not to cause the host to be more aggressive.\n                            throw;\n                        }\n#endif\n                        catch (OperationCanceledException ex) when (ex.CancellationToken.Equals(_stopToken) || StopRequested)\n                        {\n                            // We are catching general OCE exception without checking its CancellationToken\n                            // property, because the concrete token may be different than our one, for\n                            // example when using linked token sources. When we get OCE and our stop token\n                            // is canceled, we can simply break the execution loop, because it will be\n                            // broken on next iteration anyway.\n                            break;\n                        }\n                        catch (Exception ex) when (ex.IsCatchableExceptionType())\n                        {\n                            HandleException(executionId, ex, out nextDelay);\n                        }\n                    }\n\n                    HandleStop(executionId);\n                }\n#if !NETSTANDARD1_3\n                catch (ThreadAbortException ex)\n                {\n                    // This is a rude stop. Since we are handling all the thread aborts\n                    // inside the loop, only appdomain unload can bring us there. In this\n                    // case we don't reset thread abort.\n                    HandleThreadAbort(executionId, ex);\n                }\n#endif\n            }\n        }\n\n        public void Dispose()\n        {\n            lock (_stopped)\n            {\n                if (_disposed) return;\n                _disposed = true;\n\n                _stopRegistration.Dispose();\n\n                _stopped.Set();\n                _stopped.Dispose();\n            }\n        }\n\n        public void NotifySucceeded()\n        {\n            if (StopRequested) return;\n\n            // This is an optimization to avoid lock acquisitions on every\n            // loop iteration. It is possible that value will become null\n            // upon entering the lock, but this race condition is benign.\n            if (Volatile.Read(ref _faultedSince) != null)\n            {\n                ToRunningState();\n            }\n        }\n\n        public override string ToString()\n        {\n            return _options?.Name ?? GetType().Name;\n        }\n\n        private void HandleStarted(Guid executionId, out TimeSpan initialDelay)\n        {\n            _logger.Debug($\"{GetExecutionLoopTemplate(executionId)} has started in {_createdAt.Elapsed.TotalMilliseconds} ms\");\n\n            // Looks weird, but several times I was initializing the nextDelay variable\n            // inside the execution loop by mistake. This lead to immediate looped invocation\n            // with no delays and 100% of CPU consumption on transient exceptions, that is\n            // completely unacceptable. So this is just a defensive technique.\n            initialDelay = TimeSpan.Zero;\n        }\n\n        private bool HandleDelay(Guid executionId, TimeSpan delay)\n        {\n            try\n            {\n                LogRetry(executionId, delay);\n                return !_stopped.WaitOne(delay, _stopToken);\n            }\n            catch (OperationCanceledException ex) when (ex.CancellationToken.Equals(_stopToken) || StopRequested)\n            {\n                return false;\n            }\n            catch (ObjectDisposedException)\n            {\n                return false;\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                LogUnableWait(executionId, delay, ex);\n                return false;\n            }\n        }\n\n        private async Task<bool> HandleDelayAsync(Guid executionId, TimeSpan delay)\n        {\n            try\n            {\n                LogRetry(executionId, delay);\n                return !await _stopped.WaitOneAsync(delay, _stopToken).ConfigureAwait(true);\n            }\n            catch (OperationCanceledException ex) when (ex.CancellationToken.Equals(_stopToken) || StopRequested)\n            {\n                return false;\n            }\n            catch (ObjectDisposedException)\n            {\n                return false;\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                LogUnableWait(executionId, delay, ex);\n                return false;\n            }\n        }\n\n        private void LogUnableWait(Guid executionId, TimeSpan delay, Exception ex)\n        {\n            _logger.FatalException($\"{GetExecutionLoopTemplate(executionId)} was unable to wait for '{delay}' delay due to an exception. Execution will be stopped.\", ex);\n        }\n\n        private void LogRetry(Guid executionId, TimeSpan delay)\n        {\n            _logger.Debug($\"{GetExecutionLoopTemplate(executionId)} will be retried in {delay}...\");\n        }\n\n        private void NormalizeDelay(ref TimeSpan retryDelay)\n        {\n            if (retryDelay <= TimeSpan.Zero)\n            {\n                _logger.Warn($\"{GetExecutionTemplate()} adjusted the retry delay from {retryDelay} to {FallbackRetryDelay}\");\n                retryDelay = FallbackRetryDelay;\n            }\n        }\n\n        private void HandleSuccess(out TimeSpan nextDelay)\n        {\n            nextDelay = TimeSpan.Zero;\n            NotifySucceeded();\n        }\n\n        private void HandleException(Guid executionId, Exception exception, out TimeSpan delay)\n        {\n#if !NETSTANDARD1_3\n            // Normally, there should be no checks for AppDomain unload condition, because we can't\n            // get here on appdomain unloads. But Mono < 5.4 has an issue with Thread.ResetAbort, and\n            // it can prevent appdomain to be unloaded: https://bugzilla.xamarin.com/show_bug.cgi?id=5804.\n            // It's better to reassure this can't happen under all circumstances.\n            if ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) != 0 && !AppDomainUnloadMonitor.IsUnloading)\n            {\n                try\n                {\n                    Thread.ResetAbort();\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    // .NET Core doesn't support both Thread.Abort and Thread.ResetAbort methods.\n                    // I don't see any possible cases, where thread is aborted, but nevertheless\n                    // we shouldn't hide the original exception.\n                    _logger.ErrorException($\"{GetExecutionLoopTemplate(executionId)} was unable to reset thread abort request due to an exception. Background execution can be prematurely stopped.\", ex);\n                }\n            }\n#endif\n\n            if (StopRequested)\n            {\n                delay = FallbackRetryDelay;\n                return;\n            }\n\n            try\n            {\n                // Some code might cache exception object and throw the same instance\n                // from multiple threads, despite it's not recommended to do. However\n                // bad things happen, and we should have some diagnostic tools to\n                // understand what's happened and what was the original exception which\n                // is being modified.\n                if (!exception.Data.Contains(\"ExecutionId\"))\n                {\n                    exception.Data.Add(\"ExecutionId\", executionId);\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                _logger.WarnException($\"Was unable to add the ExecutionId property to the exception object, please see inner exception for details. Original exception: ${exception.GetType()} (${exception.Message})\", ex);\n            }\n\n            ToFailedState(exception, out delay);\n\n            _logger.DebugException($\"{GetExecutionLoopTemplate(executionId)} caught an exception and will be retried in {delay}\", exception);\n        }\n\n        private void HandleStop(Guid executionId)\n        {\n            var stoppedAt = Volatile.Read(ref _stoppedAt);\n            _logger.Debug($\"{GetExecutionLoopTemplate(executionId)} stopped in {stoppedAt?.Elapsed.TotalMilliseconds ?? 0} ms\");\n        }\n\n#if !NETSTANDARD1_3\n        private void HandleThreadAbort(Guid executionId, Exception exception)\n        {\n            _logger.WarnException($\"{GetExecutionLoopTemplate(executionId)} caught ThreadAbortException, see inner exception for details\", exception);\n        }\n#endif\n\n        private void ToRunningState()\n        {\n            lock (_stopped)\n            {\n                if (_disposed) return;\n\n                if (_failedSince != null)\n                {\n                    // Since we are moving from the Failed state, one or more error messages were\n                    // logged, and we should notify administrators that operations are restored.\n                    _logger.Info($\"{GetExecutionTemplate()} recovered from the Failed state after {_failedSince?.Elapsed} and is in the Running state now\");\n                }\n                else if (_faultedSince != null)\n                {\n                    // We are moving from Faulted state, and there may be no any log messages (unless\n                    // DEBUG level is enabled). But since some operations were delayed, we should notify\n                    // administrators about this event, if a configured threshold is reached (to not to\n                    // log thousands of messages).\n                    _logger.Log(\n                        _faultedSince.Elapsed > _options.WarningThreshold ? LogLevel.Info : LogLevel.Debug,\n                        () => $\"{GetExecutionTemplate()} recovered from the Faulted state after {_faultedSince?.Elapsed} and is in the Running state now\");\n                }\n\n                _exceptionsCount = 0;\n\n                _faultedSince = null;\n                _failedSince = null;\n                _lastException = null;\n            }\n        }\n\n        private void ToFailedState(Exception exception, out TimeSpan retryDelay)\n        {\n            lock (_stopped)\n            {\n                retryDelay = FallbackRetryDelay;\n\n                if (_disposed) return;\n\n                _exceptionsCount++;\n                _lastException = Stopwatch.StartNew();\n\n                var optionsRetryDelay = _options.RetryDelay;\n                if (optionsRetryDelay != null)\n                {\n                    retryDelay = optionsRetryDelay(_exceptionsCount);\n                }\n\n                NormalizeDelay(ref retryDelay);\n\n                if (_faultedSince == null)\n                {\n                    _faultedSince = Stopwatch.StartNew();\n\n                    // If threshold is zero, we'll go to the Failed state directly and log error anyway.\n                    if (_options.ErrorThreshold > TimeSpan.Zero)\n                    {\n                        _logger.DebugException($\"{GetExecutionTemplate()} is in the Faulted state now due to an exception, execution will be retried in no more than {retryDelay}\", exception);\n                    }\n                }\n\n                if (_failedSince == null && _faultedSince.Elapsed > _options.ErrorThreshold)\n                {\n                    // Transition to Failed state, we should log the error message.\n                    _logger.ErrorException($\"{GetExecutionTemplate()} is in the Failed state now due to an exception, execution will be retried in no more than {retryDelay}\", exception);\n                    _failedSince = Stopwatch.StartNew();\n                }\n                else if (_failedSince != null && _lastException.Elapsed >= _options.StillErrorThreshold)\n                {\n                    // Still in the Failed state, we should log the error message as a reminder,\n                    // but shouldn't do this too often, especially for short retry intervals.\n                    _logger.ErrorException($\"{GetExecutionTemplate()} is still in the Failed state for {_failedSince?.Elapsed} due to an exception, will be retried in no more than {retryDelay}\", exception);\n                }\n            }\n        }\n\n        private string GetExecutionLoopTemplate(Guid executionId)\n        {\n            return $\"Execution loop {ToString()}:{executionId.ToString().Substring(0, 8)}\";\n        }\n\n        private string GetExecutionTemplate()\n        {\n            return $\"Execution {ToString()}\";\n        }\n\n        private void SetStoppedAt()\n        {\n            Interlocked.CompareExchange(ref _stoppedAt, Stopwatch.StartNew(), null);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Processing/BackgroundExecutionOptions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.Processing\n{\n    internal sealed class BackgroundExecutionOptions\n    {\n        private static readonly TimeSpan DefaultMaxAttemptDelay = TimeSpan.FromMinutes(5);\n        private TimeSpan _warningThreshold;\n        private TimeSpan _errorThreshold;\n        private TimeSpan _stillErrorThreshold;\n        private Func<int, TimeSpan> _retryDelay;\n\n        public BackgroundExecutionOptions()\n        {\n            WarningThreshold = TimeSpan.FromSeconds(5);\n            ErrorThreshold = TimeSpan.FromSeconds(15);\n            StillErrorThreshold = TimeSpan.FromSeconds(60);\n            RetryDelay = GetBackOffMultiplier;\n        }\n\n        public string Name { get; set; }\n\n        public TimeSpan WarningThreshold\n        {\n            get { return _warningThreshold; }\n            set\n            {\n                if (value < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(value), \"Value should be greater than or equal to TimeSpan.Zero\");\n                _warningThreshold = value;\n            }\n        }\n\n        public TimeSpan ErrorThreshold\n        {\n            get { return _errorThreshold; }\n            set\n            {\n                if (value < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(value), \"Value should be greater than or equal to TimeSpan.Zero\");\n                _errorThreshold = value;\n            }\n        }\n\n        public TimeSpan StillErrorThreshold\n        {\n            get { return _stillErrorThreshold; }\n            set\n            {\n                if (value < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(value), \"Value should be greater than or equal to TimeSpan.Zero\");\n                _stillErrorThreshold = value;\n            }\n        }\n\n        public Func<int, TimeSpan> RetryDelay\n        {\n            get { return _retryDelay; }\n            set\n            {\n                if (value == null) throw new ArgumentNullException(nameof(value));\n                _retryDelay = value;\n            }\n        }\n\n        internal static TimeSpan GetBackOffMultiplier(int retryAttemptNumber)\n        {\n            //exponential/random retry back-off.\n            var rand = new Random(Guid.NewGuid().GetHashCode());\n            var nextTry = rand.Next(\n                (int)Math.Pow(retryAttemptNumber, 2), (int)Math.Pow(retryAttemptNumber, 2) + 1);\n\n            return TimeSpan.FromSeconds(Math.Min(nextTry, DefaultMaxAttemptDelay.TotalSeconds));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Processing/BackgroundTaskScheduler.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing ThreadState = System.Threading.ThreadState;\n\nnamespace Hangfire.Processing\n{\n    /// <summary>Represents a custom implementation of the <see cref=\"TaskScheduler\"/> that uses\n    /// its own threads to execute <see cref=\"Task\"/>-based work items and their continuations.\n    /// The primary purpose of this scheduler is background processing, for other use cases\n    /// consider using the <see cref=\"TaskScheduler.Default\"/> scheduler instead.</summary>\n    /// <remarks>\n    /// <para>You can use this scheduler to offload background tasks to a separate, dedicated\n    /// pool of threads, instead of executing them in ThreadPool's threads.\n    /// Background work items don't usually affect the request/response logic directly, so we\n    /// can afford some additional latency and execute them, when no foreground processing is\n    /// held. This is useful, when you want to minimize your response latencies to their minimum,\n    /// and don't want to allow background processing to affect the foreground one.</para>\n    /// \n    /// <para>It is not possible to offload *all* the work to dedicated threads, because a lot of\n    /// libraries hard-code their usage of the default thread pool in one way or another: by using \n    /// the ConfigureAwait(false) method, by explicitly creating continuations on TaskScheduler.Default,\n    /// or simply by using ThreadPool.QueueUserWorkItem method. So this is a best-effort attempt.</para>\n    /// \n    /// <para>Please note that all unprocessed work items are lost, when the <see cref=\"Dispose\"/>\n    /// method is called or the corresponding AppDomain is unloaded (for example,\n    /// due to process shutdown). In order to survive the process restarts, use different solutions\n    /// with persistence, like <see href=\"https://www.hangfire.io\">Hangfire</see>.</para>\n    /// </remarks>\n    /// \n    /// <threadsafety static=\"true\" instance=\"true\"/>\n    public sealed class BackgroundTaskScheduler : TaskScheduler, IDisposable\n    {\n        // Single global queue is used instead of work stealing one for simplified maintenance,\n        // and because it's enough, when dealing with async/await methods, since they rarely\n        // enqueue continuations to local queues (comparing to ContinueWith-based continuations).\n        private readonly ConcurrentQueue<Task> _queue = new ConcurrentQueue<Task>();\n\n        private readonly Thread[] _threads;\n        private readonly HashSet<int> _ourThreadIds;\n\n        // Regular semaphore is used to perform waits, when there are no tasks to be\n        // processed. It doesn't use any kind of busy waiting to allow \"foreground\"\n        // schedulers to perform useful work, instead of consuming CPU time by spinning\n        // with the hope of a task arrival. It is used with care with no unnecessary\n        // operations involved.\n        private readonly Semaphore _semaphore;\n        private readonly ManualResetEvent _stopped = new ManualResetEvent(false);\n\n        private readonly WaitHandle[] _waitHandles;\n        private readonly Action<Exception> _exceptionHandler;\n\n        private int _disposed;\n\n        /// <summary>Initializes a new instance of the <see cref=\"BackgroundTaskScheduler\"/> with\n        /// the number of threads based on the <see cref=\"Environment.ProcessorCount\"/> property.\n        /// All the created threads will be started to dispatch <see cref=\"Task\"/>\n        /// instances scheduled to run on this scheduler.</summary>\n        public BackgroundTaskScheduler()\n            : this(Environment.ProcessorCount)\n        {\n        }\n\n        /// <summary>Initializes a new instance of the <see cref=\"BackgroundTaskScheduler\"/> with\n        /// the given number of dedicated threads that will be creating using the default thread\n        /// factory. All the created threads will be started to dispatch <see cref=\"Task\"/>\n        /// instances scheduled to run on this scheduler.</summary>\n        /// <param name=\"threadCount\">The number of dedicated threads will be created.</param>\n        /// <exception cref=\"ArgumentOutOfRangeException\"><paramref name=\"threadCount\"/> is less or equal to zero.</exception>\n        public BackgroundTaskScheduler(int threadCount)\n            : this(threadStart => DefaultThreadFactory(threadStart, threadCount), DefaultExceptionHandler)\n        {\n        }\n\n        /// <summary>Initializes a new instance of the <see cref=\"BackgroundTaskScheduler\"/>\n        /// class with the specified <paramref name=\"threadFactory\"/> and an optional exception\n        /// handler. All the created threads will be started to dispatch <see cref=\"Task\"/>\n        /// instances scheduled to run on this scheduler.</summary>\n        /// <param name=\"threadFactory\">Callback that creates one or more dedicated threads.</param>\n        /// <param name=\"exceptionHandler\">Optional callback that is invoked when unhandled exception occurs \n        /// in one of the threads. After this event this instance is considered stopped.</param>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"threadFactory\"/> is <see langword=\"null\"/>.</exception>\n        /// <exception cref=\"ArgumentException\"><paramref name=\"threadFactory\"/> returned <see langword=\"null\"/> or zero threads.</exception>\n        /// <exception cref=\"ArgumentException\"><paramref name=\"threadFactory\"/> returned at least one thread not in the <see cref=\"ThreadState.Unstarted\"/> state.</exception>\n        public BackgroundTaskScheduler(\n            [NotNull] Func<ThreadStart, IEnumerable<Thread>> threadFactory,\n            [CanBeNull] Action<Exception> exceptionHandler)\n        {\n            if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory));\n\n            _exceptionHandler = exceptionHandler;\n            _semaphore = new Semaphore(0, Int32.MaxValue);\n\n            // Stopped event should always be the first in this array, see the DispatchLoop method.\n            _waitHandles = new WaitHandle[] { _stopped, _semaphore };\n\n#if !NETSTANDARD1_3\n            AppDomainUnloadMonitor.EnsureInitialized();\n#endif\n\n            _threads = threadFactory(DispatchLoop)?.ToArray();\n\n            if (_threads == null || _threads.Length == 0)\n            {\n                throw new ArgumentException(\"At least one non-started thread should be created.\", nameof(threadFactory));\n            }\n\n            if (_threads.Any(static thread => thread == null || (thread.ThreadState & ThreadState.Unstarted) == 0))\n            {\n                throw new ArgumentException(\"All the threads should be non-null and in the ThreadState.Unstarted state.\", nameof(threadFactory));\n            }\n\n            foreach (var thread in _threads)\n            {\n                thread.Start();\n            }\n\n            _ourThreadIds = new HashSet<int>(_threads.Select(static x => x.ManagedThreadId));\n        }\n\n        /// <inheritdoc />\n        public override int MaximumConcurrencyLevel => _threads.Length;\n\n        /// <summary>Signals all the threads to be stopped and releases all the unmanaged resources.\n        /// This method should be called only when you are uninterested on the corresponding tasks,\n        /// i.e. during AppDomain unloads, process shutdowns, etc.</summary>\n        public void Dispose()\n        {\n            if (Interlocked.Exchange(ref _disposed, 1) == 1)\n            {\n                return;\n            }\n\n            _stopped.Set();\n\n            // We don't wait for threads here using Thread.Join method, because we can't\n            // guarantee that all the threads will be stopped after a call to the Dispose \n            // method without introducing an infinite block (they are executing user code,\n            // and there can be anything).\n            // Since the Dispose method is usually called from within a protected region\n            // itself (such as when using the `using` statements), we shouldn't block here,\n            // because this may prevent AppDomain from being unloaded, causing unexpected\n            // behavior for applications.\n\n            // Since we don't wait until completion, our threads are responsible for preventing\n            // or catching ObjectDisposedException from these wait handles.\n\n            _stopped.Dispose();\n            _semaphore.Dispose();\n        }\n\n        /// <inheritdoc />\n        protected override void QueueTask(Task task)\n        {\n            // Both ConcurrentQueue and Semaphore may cause a thread to be blocked. During that\n            // transition, CLR is checking whether there are any Thread Interrupts pending and\n            // throws if any. Any exception thrown from this method will lead to an unobserved\n            // exception posted to ThreadPool due to the internal implementation of TaskScheduler,\n            // and this may bring the whole process down.\n            // To prevent this, WaitHandle.WaitAny is required in the work loop.\n\n            _queue.Enqueue(task);\n            _semaphore.Release();\n        }\n\n        /// <inheritdoc />\n        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)\n        {\n            // There's no efficient way to dequeue an arbitrary element from concurrent queue.\n            if (taskWasPreviouslyQueued) return false;\n\n            // This method can be called from other schedulers that want to get some advice\n            // whether or not inline execution of tasks that belongs to the current scheduler.\n            // Since we want to execute as much tasks as possible on our dedicated threads,\n            // we allow to inline only requests from the current scheduler, i.e. just to save\n            // some time, since no queueing will be involved.\n\n            if (!_ourThreadIds.Contains(Environment.CurrentManagedThreadId)) return false;\n\n            return TryExecuteTask(task);\n        }\n\n        /// <inheritdoc />\n        protected override IEnumerable<Task> GetScheduledTasks()\n        {\n            ThrowIfDisposed();\n            return _queue.ToArray();\n        }\n\n        private static IEnumerable<Thread> DefaultThreadFactory(ThreadStart threadStart, int threadCount)\n        {\n            if (threadCount <= 0) throw new ArgumentOutOfRangeException(nameof(threadCount));\n            var threads = new Thread[threadCount];\n\n            for (var i = 0; i < threadCount; i++)\n            {\n                threads[i] = new Thread(threadStart)\n                {\n                    Name = $\"BackgroundThread #{i + 1}\",\n                    IsBackground = true,\n                };\n            }\n\n            return threads;\n        }\n\n        private static void DefaultExceptionHandler(Exception exception)\n        {\n#if !NETSTANDARD1_3\n            Trace.WriteLine(\"An unhandled exception occurred: \" + exception);\n#endif\n        }\n\n        private void DispatchLoop()\n        {\n            try\n            {\n                // The outer loop is needed to keep threads under our control by catching\n                // TIE and TAE exceptions to prevent their destructive behavior of killing\n                // our threads without need to re-create them as implemented in ThreadPool.\n                // Unfortunately we don't have the same reset mechanisms available, because\n                // they are native and aren't exposed to public, so we'll do this on a best\n                // effort basis.\n\n                // TODO: Reset Thread.Name, Thread.CurrentCulture and Thread.Priority?\n                // What about ExecutionContext and SynchronizationContext? But we don't have\n                // public APIs to handle this.\n\n                while (Volatile.Read(ref _disposed) == 0)\n                {\n                    Task task = null;\n\n                    try\n                    {\n                        // Kernel wait is required here, because otherwise any pending thread\n                        // interrupt may kill the whole process, because of internal TaskScheduler\n                        // implementation. By calling WaitHandle.WaitAny here, we are causing\n                        // CLR to check if there are pending interrupts earlier, and in the\n                        // place where we can safely handle it.\n                        \n                        while (WaitHandle.WaitAny(_waitHandles) != 0)\n                        {\n                            if (_queue.TryDequeue(out task))\n                            {\n                                TryExecuteTask(task);\n                            }\n                        }\n                    }\n                    catch (ObjectDisposedException)\n                    {\n                        // There's a benign race, when wait handles are disposed just\n                        // before the call to WaitAny. Other methods don't throw exceptions\n                        // of this type. Since this is an ordinal shutdown, we can skip\n                        // the reporting logic.\n                    }\n#if !NETSTANDARD1_3\n                    catch (Exception ex) when (ex is ThreadAbortException || ex is ThreadInterruptedException)\n                    {\n                        // We don't have methods like IThreadPoolWorkItem.MarkAborted in public\n                        // API, but we should handle non-completed task in case of TIE or TAE,\n                        // because there's a high probability to lose continuations. So we're\n                        // simply re-queueing it.\n                        if (task != null && !task.IsCompleted)\n                        {\n                            QueueTask(task);\n                        }\n                        \n                        // Normally, there should be no check for AppDomain unload condition, because we can't\n                        // get here on appdomain unloads. But Mono < 5.4 has an issue with Thread.ResetAbort, and\n                        // it can prevent appdomain to be unloaded: https://bugzilla.xamarin.com/show_bug.cgi?id=5804.\n                        // It's better to reassure this can't happen under all circumstances.\n                        if ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) != 0 && \n                            !AppDomainUnloadMonitor.IsUnloading)\n                        {\n                            try\n                            {\n                                Thread.ResetAbort();\n                            }\n                            catch (PlatformNotSupportedException)\n                            {\n                            }\n                        }\n                    }\n#endif\n                }\n            }\n#if !NETSTANDARD1_3\n            catch (ThreadAbortException ex)\n            {\n                // ThreadAbortException is expected during AppDomain unloads, so we\n                // don't call the exception handler in this case.\n                if (!AppDomainUnloadMonitor.IsUnloading)\n                {\n                    InvokeUnhandledExceptionHandler(ex);\n                }\n            }\n#endif\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                InvokeUnhandledExceptionHandler(ex);\n            }\n        }\n\n        private void InvokeUnhandledExceptionHandler(Exception exception)\n        {\n            try\n            {\n                var handler = _exceptionHandler;\n                handler?.Invoke(exception);\n            }\n#if !NETSTANDARD1_3\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                Trace.WriteLine(\"Unexpected exception caught in exception handler itself.\" + Environment.NewLine + ex);\n            }\n#else\n            catch\n            {\n            }\n#endif\n        }\n\n        private void ThrowIfDisposed()\n        {\n            if (Volatile.Read(ref _disposed) == 1)\n            {\n                throw new ObjectDisposedException(GetType().FullName);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Processing/IBackgroundDispatcher.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Hangfire.Processing\n{\n    // TODO Replace these methods with WaitHandle property in 2.0.\n    public interface IBackgroundDispatcher : IDisposable\n    {\n        bool Wait(TimeSpan timeout);\n        Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToken);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Processing/IBackgroundExecution.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Processing\n{\n    public interface IBackgroundExecution : IDisposable\n    {\n        void Run([NotNull] Action<Guid, object> callback, [CanBeNull] object state);\n        Task RunAsync([NotNull] Func<Guid, object, Task> callback, [CanBeNull] object state);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Processing/InlineSynchronizationContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Threading;\n\nnamespace Hangfire.Processing\n{\n    internal sealed class InlineSynchronizationContext : SynchronizationContext, IDisposable\n    {\n        private readonly ConcurrentQueue<Tuple<SendOrPostCallback, object>> _queue = new ConcurrentQueue<Tuple<SendOrPostCallback, object>>();\n        private readonly Semaphore _semaphore = new Semaphore(0, Int32.MaxValue);\n\n        public WaitHandle WaitHandle => _semaphore;\n\n        public Tuple<SendOrPostCallback, object> Dequeue()\n        {\n            _queue.TryDequeue(out var tuple);\n            return tuple;\n        }\n\n        public void Dispose()\n        {\n            _semaphore.Dispose();\n        }\n\n        public override void Post(SendOrPostCallback callback, object state)\n        {\n            try\n            {\n                _queue.Enqueue(Tuple.Create(callback, state));\n                _semaphore.Release();\n            }\n            catch (ObjectDisposedException)\n            {\n                base.Post(callback, state);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Processing/TaskExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Diagnostics;\nusing System.Reflection;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Logging;\n\nnamespace Hangfire.Processing\n{\n    internal static class TaskExtensions\n    {\n        private static readonly Type[] EmptyTypes = Type.EmptyTypes;\n        private static readonly WaitHandle InvalidWaitHandleInstance = new InvalidWaitHandle();\n\n        public static bool WaitOne([NotNull] this WaitHandle waitHandle, TimeSpan timeout, CancellationToken token)\n        {\n            if (waitHandle == null) throw new ArgumentNullException(nameof(waitHandle));\n            if (timeout < Timeout.InfiniteTimeSpan) throw new ArgumentOutOfRangeException(nameof(timeout));\n\n            token.ThrowIfCancellationRequested();\n\n            using var ev = token.GetCancellationEvent();\n\n            var waitHandles = new[] { waitHandle, ev.WaitHandle };\n\n            var stopwatch = Stopwatch.StartNew();\n            var waitResult = WaitHandle.WaitAny(waitHandles, timeout);\n            stopwatch.Stop();\n\n            var timeoutThreshold = TimeSpan.FromMilliseconds(1000);\n            var elapsedThreshold = TimeSpan.FromMilliseconds(500);\n            var protectionTime = TimeSpan.FromSeconds(1);\n\n            if (waitResult == 0)\n            {\n                return true;\n            }\n\n            if (!token.IsCancellationRequested &&\n                timeout >= timeoutThreshold &&\n                stopwatch.Elapsed < elapsedThreshold)\n            {\n                try\n                {\n                    var logger = LogProvider.GetLogger(typeof(TaskExtensions));\n                    logger.Error($\"Actual wait time for non-canceled token was '{stopwatch.Elapsed.TotalMilliseconds}' ms instead of '{timeout.TotalMilliseconds}' ms, wait result: {waitResult}, using protective wait. Please report this to Hangfire developers.\");\n                }\n                finally\n                {\n                    Thread.Sleep(protectionTime);\n                }\n            }\n\n            token.ThrowIfCancellationRequested();\n            return false;\n        }\n\n        public static async Task<bool> WaitOneAsync([NotNull] this WaitHandle waitHandle, TimeSpan timeout, CancellationToken token)\n        {\n            if (waitHandle == null) throw new ArgumentNullException(nameof(waitHandle));\n            if (timeout < Timeout.InfiniteTimeSpan) throw new ArgumentOutOfRangeException(nameof(timeout));\n\n            token.ThrowIfCancellationRequested();\n\n            if (waitHandle.WaitOne(TimeSpan.Zero))\n            {\n                return true;\n            }\n\n            var tcs = CreateCompletionSource<bool>();\n            var registration = ThreadPool.RegisterWaitForSingleObject(waitHandle, CallBack, tcs, timeout, executeOnlyOnce: true);\n\n            if (token.CanBeCanceled)\n            {\n                token.Register(Callback, Tuple.Create(registration, tcs, token), useSynchronizationContext: false);\n            }\n\n            return await tcs.Task.ConfigureAwait(false);\n        }\n\n        public static bool IsTaskLike(this Type type, out Func<object, Task> getTaskFunc)\n        {\n            var typeInfo = type.GetTypeInfo();\n\n            // There are no primitive types that behave as Task\n            if (!typeInfo.IsPrimitive)\n            {\n                if (typeof(Task).GetTypeInfo().IsAssignableFrom(typeInfo))\n                {\n                    getTaskFunc = static obj => (Task)obj;\n                    return true;\n                }\n\n                // We are don't relying on GetAwaiter/GetResult methods for ValueTask,\n                // because it's not a valid pattern to use them as written here:\n                // https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/#user-content-valid-consumption-patterns-for-valuetasks\n                // So we're using their `AsTask` method to create a task first, and then\n                // waiting on it.\n                // This method can be replaced with wrapping in a custom awaiter like\n                // in ASP.NET Core, but this step requires using the `await` keyword\n                // to get the result, so can be implemented only in future.\n                if (typeInfo.FullName != null &&\n                    typeInfo.FullName.StartsWith(\"System.Threading.Tasks.ValueTask\", StringComparison.Ordinal))\n                {\n                    var asTask = type.GetRuntimeMethod(\"AsTask\", EmptyTypes);\n\n                    if (asTask != null && asTask.IsPublic && !asTask.IsStatic &&\n                        typeof(Task).GetTypeInfo().IsAssignableFrom(asTask.ReturnType.GetTypeInfo()))\n                    {\n                        getTaskFunc = obj => (Task) asTask.Invoke(obj, null);\n                        return true;\n                    }\n                }\n            }\n\n            getTaskFunc = null;\n            return false;\n        }\n\n        public static object GetTaskLikeResult([NotNull] this Task task, object obj, Type returnType)\n        {\n            if (task == null) throw new ArgumentNullException(nameof(task));\n\n            if (task != obj)\n            {\n                // We shouldn't call GetAwaiter/GetResult on ValueTask directly, because\n                // there may be a race condition as tells us this article:\n                // https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/#user-content-valid-consumption-patterns-for-valuetasks\n                // So we are waiting on task, returned by the AsTask method to ensure it's\n                // completed, before querying for the result.\n                task.GetAwaiter().GetResult();\n            }\n\n            // ReturnType is used instead of task.GetType, because we should return `null` result,\n            // when method is returning non-generic Task. However async state machines may use\n            // Task<VoidTaskResult> or Task<VoidResult> for these cases, and the number of such\n            // void types is pretty high. So it's much safer to call GetAwaiter/GetResult on an\n            // original result object.\n\n            // Awaitable type must have a public parameterless GetAwaiter instance method, ...\n            var getAwaiter = returnType.GetRuntimeMethod(\"GetAwaiter\", EmptyTypes);\n            if (getAwaiter == null || getAwaiter.IsStatic || !getAwaiter.IsPublic) return null;\n\n            var awaiterType = getAwaiter.ReturnType;\n\n            // ... and also have a public parameterless GetResult instance method\n            var getResult = awaiterType.GetRuntimeMethod(\"GetResult\", EmptyTypes);\n            if (getResult == null || getResult.IsStatic || !getResult.IsPublic) return null;\n\n            var awaiter = getAwaiter.Invoke(obj, null);\n            return getResult.Invoke(awaiter, null);\n        }\n\n        private static void CallBack(object state, bool timedOut)\n        {\n            // We do call the Unregister method to prevent race condition between\n            // registered wait and cancellation token registration, so can use the\n            // SetResult safely.\n            ((TaskCompletionSource<bool>)state).SetResult(!timedOut);\n        }\n\n        private static void Callback(object state)\n        {\n            // We need to ensure there's no race condition, where wait handle was\n            // set, but callback wasn't fully completed. In this case handle is\n            // acquired, but task is cancelled.\n            var ctx = (Tuple<RegisteredWaitHandle, TaskCompletionSource<bool>, CancellationToken>)state;\n\n            ctx.Item1.Unregister(InvalidWaitHandleInstance);\n            TrySetCanceled(ctx.Item2, ctx.Item3);\n        }\n\n        private static TaskCompletionSource<T> CreateCompletionSource<T>()\n        {\n            return new TaskCompletionSource<T>(\n#if !NET451\n                TaskCreationOptions.RunContinuationsAsynchronously\n#endif\n            );\n        }\n\n        private static void TrySetCanceled<T>(TaskCompletionSource<T> source, CancellationToken token)\n        {\n            source.TrySetCanceled(\n#if !NET451\n                token\n#endif\n            );\n        }\n\n        private sealed class InvalidWaitHandle : WaitHandle\n        {\n#if !NETSTANDARD1_3\n            [Obsolete(\"Use the SafeWaitHandle property instead.\")]\n            public override IntPtr Handle\n            {\n                get { return InvalidHandle; }\n                set { throw new InvalidOperationException(); }\n            }\n#endif\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Profiling/EmptyProfiler.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.Profiling\n{\n    internal sealed class EmptyProfiler : IProfiler\n    {\n        internal static readonly IProfiler Instance = new EmptyProfiler();\n\n        private EmptyProfiler()\n        {\n        }\n\n        public TResult InvokeMeasured<TInstance, TResult>(\n            TInstance instance, \n            Func<TInstance, TResult> action,\n            Func<TInstance, string> messageFunc)\n        {\n            if (action == null) throw new ArgumentNullException(nameof(action));\n            return action(instance);\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Profiling/IProfiler.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Profiling\n{\n    // TODO: Merge this with logging\n    internal interface IProfiler\n    {\n        // TODO: Replace method with some eventId\n        TResult InvokeMeasured<TInstance, TResult>(\n            [CanBeNull] TInstance instance, \n            [NotNull, InstantHandle] Func<TInstance, TResult> action,\n            [CanBeNull] Func<TInstance, string> messageFunc = null);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Profiling/ProfilerExtensions.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Profiling\n{\n    internal static class ProfilerExtensions\n    {\n        public static void InvokeMeasured<TInstance>(\n            [NotNull] this IProfiler profiler,\n            [CanBeNull] TInstance instance, \n            [NotNull] Action<TInstance> action,\n            [CanBeNull] Func<TInstance, string> messageFunc = null)\n        {\n            if (profiler == null) throw new ArgumentNullException(nameof(profiler));\n            if (action == null) throw new ArgumentNullException(nameof(action));\n\n            profiler.InvokeMeasured(new InstanceAction<TInstance>(instance, action, messageFunc), InvokeAction, MessageCallback);\n        }\n\n        private static bool InvokeAction<TInstance>(InstanceAction<TInstance> tuple)\n        {\n            tuple.Action(tuple.Instance);\n            return true;\n        }\n\n        private static string MessageCallback<TInstance>(InstanceAction<TInstance> action)\n        {\n            return action.MessageFunc?.Invoke(action.Instance);\n        }\n\n        internal struct InstanceAction<TInstance>\n        {\n            public InstanceAction([CanBeNull] TInstance instance, [NotNull] Action<TInstance> action, [CanBeNull] Func<TInstance, string> messageFunc)\n            {\n                if (action == null) throw new ArgumentNullException(nameof(action));\n\n                Instance = instance;\n                Action = action;\n                MessageFunc = messageFunc;\n            }\n\n            [CanBeNull]\n            public TInstance Instance { get; }\n\n            [NotNull]\n            public Action<TInstance> Action { get; }\n            \n            [CanBeNull]\n            public Func<TInstance, string> MessageFunc { get; }\n\n            public override string ToString()\n            {\n                return Instance?.ToString() ?? typeof(TInstance).ToString();\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Profiling/SlowLogProfiler.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Diagnostics;\nusing Hangfire.Logging;\n\nnamespace Hangfire.Profiling\n{\n    internal sealed class SlowLogProfiler : IProfiler\n    {\n        private static readonly TimeSpan DefaultThreshold = TimeSpan.FromMinutes(1);\n\n        private readonly int _thresholdMs;\n        private readonly ILog _logger;\n\n        public SlowLogProfiler(ILog logger)\n            : this(logger, DefaultThreshold)\n        {\n        }\n\n        public SlowLogProfiler(ILog logger, TimeSpan threshold)\n        {\n            _thresholdMs = (int)threshold.TotalMilliseconds;\n            _logger = logger;\n        }\n\n        public TResult InvokeMeasured<TInstance, TResult>(\n            TInstance instance,\n            Func<TInstance, TResult> action,\n            Func<TInstance, string> messageFunc = null)\n        {\n            if (action == null) throw new ArgumentNullException(nameof(action));\n\n            var started = Environment.TickCount;\n\n            try\n            {\n                return action(instance);\n            }\n            finally\n            {\n                var elapsed = unchecked(Environment.TickCount - started); \n                if (elapsed >= _thresholdMs)\n                {\n                    _logger.Warn($\"Slow log: {instance?.ToString() ?? typeof(TInstance).ToString()} performed \\\"{messageFunc?.Invoke(instance)}\\\" in {elapsed / 1000} sec\");\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Properties/Annotations.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\n\n#if NETSTANDARD1_3\nnamespace System.Diagnostics.CodeAnalysis\n{\n    [Conditional(\"DEBUG\")] // don't bloat release assemblies\n    [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)]\n    internal sealed class ExcludeFromCodeCoverageAttribute : Attribute\n    {\n    }\n}\n#endif\n\n#pragma warning disable 1591\n// ReSharper disable UnusedMember.Global\n// ReSharper disable UnusedParameter.Local\n// ReSharper disable MemberCanBePrivate.Global\n// ReSharper disable UnusedAutoPropertyAccessor.Global\n// ReSharper disable IntroduceOptionalParameters.Global\n// ReSharper disable MemberCanBeProtected.Global\n// ReSharper disable InconsistentNaming\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire.Annotations\n{\n  /// <summary>\n  /// Indicates that the value of the marked element could be <c>null</c> sometimes,\n  /// so the check for <c>null</c> is necessary before its usage\n  /// </summary>\n  /// <example><code>\n  /// [CanBeNull] \n  /// public object Test() { return null; }\n  /// \n  /// public void UseTest() \n  /// {\n  ///     var p = Test();\n  ///     var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'\n  /// }\n  /// </code></example>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(\n    AttributeTargets.Method | AttributeTargets.Parameter |\n    AttributeTargets.Property | AttributeTargets.Delegate |\n    AttributeTargets.Field)]\n  public sealed class CanBeNullAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that the value of the marked element could never be <c>null</c>\n  /// </summary>\n  /// <example><code>\n  /// [NotNull] \n  /// public object Foo() \n  /// {\n  ///     return null; // Warning: Possible 'null' assignment\n  /// }\n  /// </code></example>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(\n    AttributeTargets.Method | AttributeTargets.Parameter |\n    AttributeTargets.Property | AttributeTargets.Delegate |\n    AttributeTargets.Field)]\n  public sealed class NotNullAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that the marked method builds string by format pattern and (optional) arguments.\n  /// Parameter, which contains format string, should be given in constructor. The format string\n  /// should be in <see cref=\"string.Format(IFormatProvider,string,object[])\"/>-like form\n  /// </summary>\n  /// <example><code>\n  /// [StringFormatMethod(\"message\")]\n  /// public void ShowError(string message, params object[] args) { /* do something */ }\n  /// \n  /// public void Foo() \n  /// {\n  ///     ShowError(\"Failed: {0}\"); // Warning: Non-existing argument in format string\n  /// }\n  /// </code></example>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(\n    AttributeTargets.Constructor | AttributeTargets.Method)]\n  public sealed class StringFormatMethodAttribute : Attribute\n  {\n    /// <param name=\"formatParameterName\">\n    /// Specifies which parameter of an annotated method should be treated as format-string\n    /// </param>\n    public StringFormatMethodAttribute(string formatParameterName)\n    {\n      FormatParameterName = formatParameterName;\n    }\n\n    public string FormatParameterName { get; private set; }\n  }\n\n  /// <summary>\n  /// Indicates that the function argument should be string literal and match one\n  /// of the parameters of the caller function. For example, ReSharper annotates\n  /// the parameter of <see cref=\"System.ArgumentNullException\"/>\n  /// </summary>\n  /// <example><code>\n  /// public void Foo(string param) \n  /// {\n  ///     if (param == null)\n  ///         throw new ArgumentNullException(\"par\"); // Warning: Cannot resolve symbol\n  /// }\n  /// </code></example>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class InvokerParameterNameAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that the method is contained in a type that implements\n  /// <see cref=\"System.ComponentModel.INotifyPropertyChanged\"/> interface\n  /// and this method is used to notify that some property value changed\n  /// </summary>\n  /// <remarks>\n  /// The method should be non-static and conform to one of the supported signatures:\n  /// <list>\n  /// <item><c>NotifyChanged(string)</c></item>\n  /// <item><c>NotifyChanged(params string[])</c></item>\n  /// <item><c>NotifyChanged{T}(Expression{Func{T}})</c></item>\n  /// <item><c>NotifyChanged{T,U}(Expression{Func{T,U}})</c></item>\n  /// <item><c>SetProperty{T}(ref T, T, string)</c></item>\n  /// </list>\n  /// </remarks>\n  /// <example><code>\n  /// public class Foo : INotifyPropertyChanged \n  /// {\n  ///     public event PropertyChangedEventHandler PropertyChanged;\n  ///     [NotifyPropertyChangedInvocator]\n  ///     protected virtual void NotifyChanged(string propertyName) { ... }\n  ///\n  ///     private string _name;\n  ///     public string Name {\n  ///         get { return _name; }\n  ///         set { _name = value; NotifyChanged(\"LastName\"); /* Warning */ }\n  ///     }\n  /// }\n  /// </code>\n  /// Examples of generated notifications:\n  /// <list>\n  /// <item><c>NotifyChanged(\"Property\")</c></item>\n  /// <item><c>NotifyChanged(() =&gt; Property)</c></item>\n  /// <item><c>NotifyChanged((VM x) =&gt; x.Property)</c></item>\n  /// <item><c>SetProperty(ref myField, value, \"Property\")</c></item>\n  /// </list>\n  /// </example>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute\n  {\n    public NotifyPropertyChangedInvocatorAttribute() { }\n    public NotifyPropertyChangedInvocatorAttribute(string parameterName)\n    {\n      ParameterName = parameterName;\n    }\n\n    public string ParameterName { get; private set; }\n  }\n\n  /// <summary>\n  /// Describes dependency between method input and output\n  /// </summary>\n  /// <syntax>\n  /// <p>Function Definition Table syntax:</p>\n  /// <list>\n  /// <item>FDT      ::= FDTRow [;FDTRow]*</item>\n  /// <item>FDTRow   ::= Input =&gt; Output | Output &lt;= Input</item>\n  /// <item>Input    ::= ParameterName: Value [, Input]*</item>\n  /// <item>Output   ::= [ParameterName: Value]* {halt|stop|void|nothing|Value}</item>\n  /// <item>Value    ::= true | false | null | notnull | canbenull</item>\n  /// </list>\n  /// If method has single input parameter, it's name could be omitted.<br/>\n  /// Using <c>halt</c> (or <c>void</c>/<c>nothing</c>, which is the same)\n  /// for method output means that the methos doesn't return normally.<br/>\n  /// <c>canbenull</c> annotation is only applicable for output parameters.<br/>\n  /// You can use multiple <c>[ContractAnnotation]</c> for each FDT row,\n  /// or use single attribute with rows separated by semicolon.<br/>\n  /// </syntax>\n  /// <examples><list>\n  /// <item><code>\n  /// [ContractAnnotation(\"=> halt\")]\n  /// public void TerminationMethod()\n  /// </code></item>\n  /// <item><code>\n  /// [ContractAnnotation(\"halt &lt;= condition: false\")]\n  /// public void Assert(bool condition, string text) // regular assertion method\n  /// </code></item>\n  /// <item><code>\n  /// [ContractAnnotation(\"s:null => true\")]\n  /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()\n  /// </code></item>\n  /// <item><code>\n  /// // A method that returns null if the parameter is null, and not null if the parameter is not null\n  /// [ContractAnnotation(\"null => null; notnull => notnull\")]\n  /// public object Transform(object data) \n  /// </code></item>\n  /// <item><code>\n  /// [ContractAnnotation(\"s:null=>false; =>true,result:notnull; =>false, result:null\")]\n  /// public bool TryParse(string s, out Person result)\n  /// </code></item>\n  /// </list></examples>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]\n  public sealed class ContractAnnotationAttribute : Attribute\n  {\n    public ContractAnnotationAttribute([NotNull] string contract)\n      : this(contract, false) { }\n\n    public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)\n    {\n      Contract = contract;\n      ForceFullStates = forceFullStates;\n    }\n\n    public string Contract { get; private set; }\n    public bool ForceFullStates { get; private set; }\n  }\n\n  /// <summary>\n  /// Indicates that marked element should be localized or not\n  /// </summary>\n  /// <example><code>\n  /// [LocalizationRequiredAttribute(true)]\n  /// public class Foo \n  /// {\n  ///     private string str = \"my string\"; // Warning: Localizable string\n  /// }\n  /// </code></example>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(AttributeTargets.All)]\n  public sealed class LocalizationRequiredAttribute : Attribute\n  {\n    public LocalizationRequiredAttribute() : this(true) { }\n    public LocalizationRequiredAttribute(bool required)\n    {\n      Required = required;\n    }\n\n    public bool Required { get; private set; }\n  }\n\n  /// <summary>\n  /// Indicates that the value of the marked type (or its derivatives)\n  /// cannot be compared using '==' or '!=' operators and <c>Equals()</c>\n  /// should be used instead. However, using '==' or '!=' for comparison\n  /// with <c>null</c> is always permitted.\n  /// </summary>\n  /// <example><code>\n  /// [CannotApplyEqualityOperator]\n  /// class NoEquality { }\n  /// class UsesNoEquality \n  /// {\n  ///     public void Test() \n  ///     {\n  ///         var ca1 = new NoEquality();\n  ///         var ca2 = new NoEquality();\n  ///         if (ca1 != null) // OK\n  ///         { \n  ///             bool condition = ca1 == ca2; // Warning\n  ///         }\n  ///     }\n  /// }\n  /// </code></example>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(\n    AttributeTargets.Interface | AttributeTargets.Class |\n    AttributeTargets.Struct)]\n  public sealed class CannotApplyEqualityOperatorAttribute : Attribute { }\n\n  /// <summary>\n  /// When applied to a target attribute, specifies a requirement for any type marked\n  /// with the target attribute to implement or inherit specific type or types.\n  /// </summary>\n  /// <example><code>\n  /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement\n  /// public class ComponentAttribute : Attribute { }\n  /// [Component] // ComponentAttribute requires implementing IComponent interface\n  /// public class MyComponent : IComponent { }\n  /// </code></example>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]\n  [BaseTypeRequired(typeof(Attribute))]\n  public sealed class BaseTypeRequiredAttribute : Attribute\n  {\n    public BaseTypeRequiredAttribute([NotNull] Type baseType)\n    {\n      BaseType = baseType;\n    }\n\n    [NotNull] public Type BaseType { get; private set; }\n  }\n\n  /// <summary>\n  /// Indicates that the marked symbol is used implicitly\n  /// (e.g. via reflection, in external library), so this symbol\n  /// will not be marked as unused (as well as by other usage inspections)\n  /// </summary>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(AttributeTargets.All)]\n  public sealed class UsedImplicitlyAttribute : Attribute\n  {\n    public UsedImplicitlyAttribute()\n      : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }\n\n    public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)\n      : this(useKindFlags, ImplicitUseTargetFlags.Default) { }\n\n    public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)\n      : this(ImplicitUseKindFlags.Default, targetFlags) { }\n\n    public UsedImplicitlyAttribute(\n      ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)\n    {\n      UseKindFlags = useKindFlags;\n      TargetFlags = targetFlags;\n    }\n\n    public ImplicitUseKindFlags UseKindFlags { get; private set; }\n    public ImplicitUseTargetFlags TargetFlags { get; private set; }\n  }\n\n  /// <summary>\n  /// Should be used on attributes and causes ReSharper\n  /// to not mark symbols marked with such attributes as unused\n  /// (as well as by other usage inspections)\n  /// </summary>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(AttributeTargets.Class)]\n  public sealed class MeansImplicitUseAttribute : Attribute\n  {\n    public MeansImplicitUseAttribute() \n      : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }\n\n    public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags)\n      : this(useKindFlags, ImplicitUseTargetFlags.Default) { }\n\n    public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags)\n      : this(ImplicitUseKindFlags.Default, targetFlags) { }\n\n    public MeansImplicitUseAttribute(\n      ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)\n    {\n      UseKindFlags = useKindFlags;\n      TargetFlags = targetFlags;\n    }\n\n    [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; }\n    [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; }\n  }\n  \n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [Flags]\n  [SuppressMessage(\"Naming\", \"CA1711:Identifiers should not have incorrect suffix\", Justification = \"Public API, can not change in minor versions.\")]\n  public enum ImplicitUseKindFlags\n  {\n    Default = Access | Assign | InstantiatedWithFixedConstructorSignature,\n    /// <summary>Only entity marked with attribute considered used</summary>\n    Access = 1,\n    /// <summary>Indicates implicit assignment to a member</summary>\n    Assign = 2,\n    /// <summary>\n    /// Indicates implicit instantiation of a type with fixed constructor signature.\n    /// That means any unused constructor parameters won't be reported as such.\n    /// </summary>\n    InstantiatedWithFixedConstructorSignature = 4,\n    /// <summary>Indicates implicit instantiation of a type</summary>\n    InstantiatedNoFixedConstructorSignature = 8,\n  }\n\n  /// <summary>\n  /// Specify what is considered used implicitly\n  /// when marked with <see cref=\"MeansImplicitUseAttribute\"/>\n  /// or <see cref=\"UsedImplicitlyAttribute\"/>\n  /// </summary>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [Flags]\n  [SuppressMessage(\"Naming\", \"CA1711:Identifiers should not have incorrect suffix\", Justification = \"Public API, can not change in minor versions.\")]\n  public enum ImplicitUseTargetFlags\n  {\n    Default = Itself,\n    [SuppressMessage(\"Design\", \"CA1069:Enums values should not be duplicated\", Justification = \"Public API, can not change in minor versions.\")]\n    Itself = 1,\n    /// <summary>Members of entity marked with attribute are considered used</summary>\n    Members = 2,\n    /// <summary>Entity marked with attribute and all its members considered used</summary>\n    WithMembers = Itself | Members\n  }\n\n  /// <summary>\n  /// This attribute is intended to mark publicly available API\n  /// which should not be removed and so is treated as used\n  /// </summary>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(AttributeTargets.All)]\n  [MeansImplicitUse]\n  public sealed class PublicAPIAttribute : Attribute\n  {\n    public PublicAPIAttribute() : this(String.Empty) { }\n    public PublicAPIAttribute([NotNull] string comment)\n    {\n      Comment = comment;\n    }\n\n    [NotNull] public string Comment { get; private set; }\n  }\n\n  /// <summary>\n  /// Tells code analysis engine if the parameter is completely handled\n  /// when the invoked method is on stack. If the parameter is a delegate,\n  /// indicates that delegate is executed while the method is executed.\n  /// If the parameter is an enumerable, indicates that it is enumerated\n  /// while the method is executed\n  /// </summary>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(AttributeTargets.Parameter)]\n  public sealed class InstantHandleAttribute : Attribute { }\n\n  /// <summary>\n  /// Indicates that a method does not make any observable state changes.\n  /// The same as <c>System.Diagnostics.Contracts.PureAttribute</c>\n  /// </summary>\n  /// <example><code>\n  /// [Pure] private int Multiply(int x, int y) { return x * y; }\n  /// public void Foo() {\n  ///   const int a = 2, b = 2;\n  ///   Multiply(a, b); // Waring: Return value of pure method is not used\n  /// }\n  /// </code></example>\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(AttributeTargets.Method)]\n  public sealed class PureAttribute : Attribute { }\n\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(\n    AttributeTargets.Parameter | AttributeTargets.Property |\n    AttributeTargets.Field)]\n  public sealed class HtmlElementAttributesAttribute : Attribute\n  {\n    public HtmlElementAttributesAttribute() : this(String.Empty) { }\n    public HtmlElementAttributesAttribute([NotNull] string name)\n    {\n      Name = name;\n    }\n\n    [NotNull] public string Name { get; private set; }\n  }\n\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  [AttributeUsage(\n    AttributeTargets.Parameter | AttributeTargets.Field |\n    AttributeTargets.Property)]\n  public sealed class HtmlAttributeValueAttribute : Attribute\n  {\n    public HtmlAttributeValueAttribute([NotNull] string name)\n    {\n      Name = name;\n    }\n\n    [NotNull] public string Name { get; private set; }\n  }\n}"
  },
  {
    "path": "src/Hangfire.Core/Properties/AssemblyInfo.cs",
    "content": "﻿using System;\nusing System.Reflection;\nusing System.Resources;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n[assembly: AssemblyTitle(\"Hangfire\")]\n[assembly: AssemblyDescription(\"Core classes of Hangfire that are independent of any framework.\")]\n[assembly: Guid(\"4deecd4f-19f6-426b-aa87-6cd1a03eaa48\")]\n[assembly: CLSCompliant(true)]\n[assembly: InternalsVisibleTo(\"Hangfire.Core.Tests\")]\n[assembly: NeutralResourcesLanguage(\"en\")]\n\n// Allow the generation of mocks for internal types\n[assembly: InternalsVisibleTo(\"DynamicProxyGenAssembly2\")]"
  },
  {
    "path": "src/Hangfire.Core/Properties/NamespaceDoc.cs",
    "content": "﻿// ReSharper disable CheckNamespace\n\nusing System.Runtime.CompilerServices;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// The <see cref=\"Hangfire\"/> namespace contains high-level types for configuring,\n    /// creating and processing background jobs, such as <see cref=\"GlobalConfiguration\"/>,\n    /// <see cref=\"BackgroundJob\"/> and <see cref=\"BackgroundJobServer\"/>.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.Annotations\n{\n    /// <summary>\n    /// The <see cref=\"Hangfire.Annotations\"/> namespace contains attributes that enable\n    /// additional code inspections in design time with JetBrains ReSharper.\n    /// </summary>\n    /// <remarks>\n    /// To enable annotations, open ReSharper options → Code Inspections → Code Annotations \n    /// and add the <see cref=\"Hangfire.Annotations\"/> namespace to the corresponding list.\n    /// </remarks>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.Client\n{\n    /// <summary>\n    /// The <see cref=\"Hangfire.Client\"/> namespace contains types that allow you to\n    /// customize the background job creation pipeline using the <see cref=\"IClientFilter\"/>,\n    /// or define your own creation process by implementing the <see cref=\"IBackgroundJobFactory\"/>\n    /// interface.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.Common\n{\n    /// <summary>\n    /// The <see cref=\"Hangfire.Common\"/> namespace provides base types for background\n    /// job filters, such as <see cref=\"JobFilterAttribute\"/>, and some helper classes.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.Dashboard\n{\n    /// <summary>\n    /// The <see cref=\"Hangfire.Dashboard\"/> namespace contains types that allow you to\n    /// restrict an access to the Dashboard UI by implementing the <see cref=\"IDashboardAuthorizationFilter\"/>\n    /// interface, as well as customize it by adding new pages, menu items, metrics, routes.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.Dashboard.Pages\n{\n    /// <summary>\n    /// The <see cref=\"Hangfire.Dashboard.Pages\"/> namespace contains the <see cref=\"LayoutPage\"/>\n    /// class, layout for all the Dashboard UI pages.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.Logging\n{\n    /// <summary>\n    /// The Hangfire.Logging namespaces contain types that allow you to \n    /// integrate Hangfire's logging with your projects as well as use it \n    /// to log custom messages.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceGroupDoc\n    {\n    }\n\n    /// <summary>\n    /// The <see cref=\"Hangfire.Logging\"/> namespace contains types that allow you to \n    /// integrate Hangfire's logging with your projects as well as use it \n    /// to log custom messages.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.Logging.LogProviders\n{\n    /// <summary>\n    /// The <see cref=\"Hangfire.Logging.LogProviders\"/> namespace contains types for \n    /// supporting most popular logging frameworks to simplify the logging integration \n    /// with your projects.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// The <see cref=\"Hangfire.Server\"/> namespace contains types that are responsible\n    /// for background processing. You may use them to customize your processing pipeline\n    /// by implementing the <see cref=\"IServerFilter\"/> interface or define your own \n    /// continuously-running background processes by implementing the <see cref=\"IBackgroundProcess\"/> \n    /// as well as create completely custom instances of <see cref=\"BackgroundProcessingServer\"/>.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// The <see cref=\"Hangfire.States\"/> namespace contains types that describe\n    /// background job states and the transitions between them. You can implement\n    /// custom <see cref=\"IElectStateFilter\"/> or <see cref=\"IApplyStateFilter\"/>\n    /// to customize the state changing pipeline, or define your own state by \n    /// implementing the  <see cref=\"IState\"/> interface.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.Storage\n{\n    /// <summary>\n    /// The Hangfire.Storage namespaces contain abstract types like <see cref=\"JobStorage\"/>,\n    /// <see cref=\"IStorageConnection\"/> and <see cref=\"IWriteOnlyTransaction\"/> for\n    /// querying and modifying the underlying background job storage. \n    /// These types are also used to implement support for other persistent storages.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceGroupDoc\n    {\n    }\n\n    /// <summary>\n    /// The Hangfire.Storage namespaces contain abstract types like <see cref=\"JobStorage\"/>,\n    /// <see cref=\"IStorageConnection\"/> and <see cref=\"IWriteOnlyTransaction\"/> for\n    /// querying and modifying the underlying background job storage. \n    /// These types are also used to implement support for other persistent storages.\n    /// </summary>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}\n\nnamespace Hangfire.Storage.Monitoring\n{\n    /// <summary>\n    /// The <see cref=\"Hangfire.Storage.Monitoring\"/> provides data transfer objects \n    /// for the <see cref=\"IMonitoringApi\"/> interface. \n    /// </summary>\n    /// <remarks>\n    /// I have no idea why I placed these types to a separate namespace, they should \n    /// be moved to the parent <see cref=\"Hangfire.Storage\"/> namespace in version 2.0.\n    /// </remarks>\n    [CompilerGenerated]\n    class NamespaceDoc\n    {\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/QueueAttribute.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Globalization;\nusing System.Linq;\nusing Hangfire.Common;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Represents attribute, that is used to determine queue name\n    /// for background jobs. It can be applied to the methods and classes. \n    /// If the attribute is not applied neither to the method, nor the class, \n    /// then default queue will be used.\n    /// </summary>\n    /// \n    /// <example><![CDATA[\n    /// \n    /// [Queue(\"high\")]\n    /// public class ErrorService\n    /// {\n    ///     public void ReportError(string message) { }\n    /// \n    ///     [Queue(\"critical\")]\n    ///     public void ReportFatal(string message) { }\n    /// }\n    /// \n    /// // Background job will be placed on the 'high' queue.\n    /// BackgroundJob.Enqueue<ErrorService>(x => x.ReportError(\"Something bad happened\"));\n    /// \n    /// // Background job will be placed on the 'critical' queue.\n    /// BackgroundJob.Enqueue<ErrorService>(x => x.ReportFatal(\"Really bad thing!\"));\n    /// \n    /// ]]></example>\n    public sealed class QueueAttribute : JobFilterAttribute, IElectStateFilter\n    {\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"QueueAttribute\"/> class\n        /// using the specified queue name.\n        /// </summary>\n        /// <param name=\"queue\">Queue name.</param>\n        public QueueAttribute(string queue)\n        {\n            Queue = queue;\n            Order = Int32.MaxValue;\n        }\n\n        /// <summary>\n        /// Gets the queue name that will be used for background jobs.\n        /// </summary>\n        public string Queue { get; }\n\n        public void OnStateElection(ElectStateContext context)\n        {\n            if (context.CandidateState is EnqueuedState enqueuedState)\n            {\n                enqueuedState.Queue = String.Format(CultureInfo.InvariantCulture, Queue, context.BackgroundJob.Job.Args.ToArray());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Razor.build",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"PrecompileRazorFiles\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"..\\..\\packages\\RazorGenerator.MsBuild.2.5.0\\build\\RazorGenerator.MsBuild.targets\" />\n  <PropertyGroup>\n    <RootNamespace>Hangfire</RootNamespace>\n    <GeneratePrettyNames>true</GeneratePrettyNames>\n    <RazorViewsCodeGenDirectory>$(MsBuildProjectDirectory)\\</RazorViewsCodeGenDirectory>\n  </PropertyGroup>\n</Project>\n"
  },
  {
    "path": "src/Hangfire.Core/RecurringJob.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Linq.Expressions;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    public static class RecurringJob\n    {\n        private static readonly Func<RecurringJobManager> DefaultFactory = static () => new RecurringJobManager();\n        private static readonly object ManagerFactoryLock = new object();\n\n        private static Func<RecurringJobManager> _managerFactory;\n\n        internal static Func<RecurringJobManager> ManagerFactory\n        {\n            get\n            {\n                lock (ManagerFactoryLock)\n                {\n                    return _managerFactory ?? DefaultFactory;\n                }\n            }\n            set\n            {\n                lock (ManagerFactoryLock)\n                {\n                    _managerFactory = value;\n                }\n            }\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter and RecurringJobOptions instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(methodCall, cronExpression(), timeZone, queue);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter and RecurringJobOptions instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(methodCall, cronExpression(), timeZone, queue);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter and RecurringJobOptions instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] string cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            var job = Job.FromExpression(methodCall);\n            var id = GetRecurringJobId(job);\n\n            ManagerFactory().AddOrUpdate(id, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            var job = Job.FromExpression(methodCall);\n            var id = GetRecurringJobId(job);\n\n            ManagerFactory().AddOrUpdate(id, job, cronExpression, options);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter and RecurringJobOptions instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            var job = Job.FromExpression(methodCall);\n            var id = GetRecurringJobId(job);\n\n            ManagerFactory().AddOrUpdate(id, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            var job = Job.FromExpression(methodCall);\n            var id = GetRecurringJobId(job);\n\n            ManagerFactory().AddOrUpdate(id, job, cronExpression, options);\n        }\n\n        [Obsolete(\"Please use AddOrUpdate(string, Expression<Action>, Func<string>, RecurringJobOptions) instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, methodCall, cronExpression(), timeZone, queue);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, methodCall, cronExpression(), options);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use AddOrUpdate<T>(string, Expression<Action<T>>, Func<string>, RecurringJobOptions) instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, methodCall, cronExpression(), timeZone, queue);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, methodCall, cronExpression(), options);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use AddOrUpdate(string, Expression<Action>, string, RecurringJobOptions) instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] string cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            var job = Job.FromExpression(methodCall);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            var job = Job.FromExpression(methodCall);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var job = Job.FromExpression(methodCall, queue);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        [Obsolete(\"Please use AddOrUpdate<T>(string, Expression<Action<T>>, string, RecurringJobOptions) instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            var job = Job.FromExpression(methodCall);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            var job = Job.FromExpression(methodCall);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var job = Job.FromExpression(methodCall, queue);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter and RecurringJobOptions instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(methodCall, cronExpression(), timeZone, queue);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter and RecurringJobOptions instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(methodCall, cronExpression(), timeZone, queue);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter and RecurringJobOptions instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            var job = Job.FromExpression(methodCall);\n            var id = GetRecurringJobId(job);\n\n            ManagerFactory().AddOrUpdate(id, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            var job = Job.FromExpression(methodCall);\n            var id = GetRecurringJobId(job);\n\n            ManagerFactory().AddOrUpdate(id, job, cronExpression, options);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter and RecurringJobOptions instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            var job = Job.FromExpression(methodCall);\n            var id = GetRecurringJobId(job);\n\n            ManagerFactory().AddOrUpdate(id, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        [Obsolete(\"Please use an overload with the explicit recurringJobId parameter instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            var job = Job.FromExpression(methodCall);\n            var id = GetRecurringJobId(job);\n\n            ManagerFactory().AddOrUpdate(id, job, cronExpression, options);\n        }\n\n        [Obsolete(\"Please use AddOrUpdate(string, Expression<Func<Task>>, Func<string>, RecurringJobOptions) instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, methodCall, cronExpression(), timeZone, queue);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, methodCall, cronExpression(), options);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use AddOrUpdate<T>(string, Expression<Func<T, Task>>, Func<string>, RecurringJobOptions) instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, methodCall, cronExpression(), timeZone, queue);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, methodCall, cronExpression(), options);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use AddOrUpdate(string, Expression<Func<Task>>, string, RecurringJobOptions) instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            var job = Job.FromExpression(methodCall);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            var job = Job.FromExpression(methodCall);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var job = Job.FromExpression(methodCall, queue);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        [Obsolete(\"Please use AddOrUpdate<T>(string, Expression<Func<T, Task>>, string, RecurringJobOptions) instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression,\n            [CanBeNull] TimeZoneInfo timeZone = null,\n            [NotNull] string queue = EnqueuedState.DefaultQueue)\n        {\n            var job = Job.FromExpression(methodCall);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            var job = Job.FromExpression(methodCall);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull, InstantHandle] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var job = Job.FromExpression(methodCall, queue);\n            ManagerFactory().AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        public static void RemoveIfExists([NotNull] string recurringJobId)\n        {\n            ManagerFactory().RemoveIfExists(recurringJobId);\n        }\n\n        [Obsolete(\"Please use the TriggerJob method instead. Will be removed in 2.0.0.\")]\n        public static void Trigger([NotNull] string recurringJobId)\n        {\n            ManagerFactory().Trigger(recurringJobId);\n        }\n\n        public static string TriggerJob([NotNull] string recurringJobId)\n        {\n            return ManagerFactory().TriggerJob(recurringJobId);\n        }\n\n        private static string GetRecurringJobId(Job job)\n        {\n            return $\"{job.Type.ToGenericTypeString()}.{job.Method.Name}\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/RecurringJobEntity.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing Cronos;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\n\nnamespace Hangfire\n{\n    internal sealed class RecurringJobEntity\n    {\n        private static readonly char[] SeparatorCharacters = new[] { ' ', '\\t' };\n\n        private readonly IDictionary<string, string> _recurringJob;\n\n        public RecurringJobEntity(\n            [NotNull] string recurringJobId,\n            [NotNull] IDictionary<string, string> recurringJob)\n        {\n            _recurringJob = recurringJob ?? throw new ArgumentNullException(nameof(recurringJob));\n\n            RecurringJobId = recurringJobId ?? throw new ArgumentNullException(nameof(recurringJobId));\n\n            if (recurringJob.TryGetValue(\"Queue\", out var queue) && !String.IsNullOrWhiteSpace(queue))\n            {\n                Queue = queue;\n            }\n\n            if (recurringJob.TryGetValue(\"TimeZoneId\", out var timeZoneId) && !String.IsNullOrWhiteSpace(timeZoneId))\n            {\n                TimeZoneId = timeZoneId;\n            }\n            else\n            {\n                TimeZoneId = TimeZoneInfo.Utc.Id;\n            }\n\n            if (recurringJob.TryGetValue(\"Cron\", out var cron) && !String.IsNullOrWhiteSpace(cron))\n            {\n                Cron = cron;\n            }\n            \n            if (recurringJob.TryGetValue(\"Job\", out var job) && !String.IsNullOrWhiteSpace(job))\n            {\n                Job = job;\n            }\n\n            if (recurringJob.TryGetValue(\"LastJobId\", out var lastJobId) && !String.IsNullOrWhiteSpace(lastJobId))\n            {\n                LastJobId = lastJobId;\n            }\n\n            if (recurringJob.TryGetValue(\"LastExecution\", out var lastExecution) && !String.IsNullOrWhiteSpace(lastExecution))\n            {\n                LastExecution = JobHelper.DeserializeDateTime(lastExecution);\n            }\n\n            if (recurringJob.TryGetValue(\"NextExecution\", out var nextExecution) && !String.IsNullOrWhiteSpace(nextExecution))\n            {\n                NextExecution = JobHelper.DeserializeDateTime(nextExecution);\n            }\n\n            if (recurringJob.TryGetValue(\"CreatedAt\", out var createdAt) && !String.IsNullOrWhiteSpace(createdAt))\n            {\n                CreatedAt = JobHelper.DeserializeDateTime(createdAt);\n            }\n\n            if (recurringJob.TryGetValue(\"Misfire\", out var misfireStr))\n            {\n                MisfireHandling = (MisfireHandlingMode)Enum.Parse(typeof(MisfireHandlingMode), misfireStr);\n                if (!Enum.IsDefined(typeof(MisfireHandlingMode), MisfireHandling))\n                {\n                    throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture, \"Misfire option '{0}' is not supported.\", (int)MisfireHandling));\n                }\n            }\n            else\n            {\n                MisfireHandling = MisfireHandlingMode.Relaxed;\n            }\n\n            if (recurringJob.TryGetValue(\"V\", out var version) && !String.IsNullOrWhiteSpace(version))\n            {\n                Version = int.Parse(version, CultureInfo.InvariantCulture);\n            }\n\n            if (recurringJob.TryGetValue(\"RetryAttempt\", out var attemptString) &&\n                int.TryParse(attemptString, out var retryAttempt))\n            {\n                RetryAttempt = retryAttempt;\n            }\n\n            if (recurringJob.TryGetValue(\"Error\", out var error) && !String.IsNullOrWhiteSpace(error))\n            {\n                Error = error;\n            }\n        }\n\n        public string RecurringJobId { get; }\n\n        public string Queue { get; set; }\n        public string Cron { get; set; }\n        public string TimeZoneId { get; set; }\n        public string Job { get; set; }\n        public MisfireHandlingMode MisfireHandling { get; set; }\n\n        public DateTime? CreatedAt { get; }\n        public DateTime? NextExecution { get; private set; }\n\n        public DateTime? LastExecution { get; set; }\n        public string LastJobId { get; set; }\n        public int? Version { get; private set; }\n        public int RetryAttempt { get; set; }\n        public string Error { get; set; }\n\n        public void ScheduleNext(ITimeZoneResolver timeZoneResolver, DateTime from)\n        {\n            ScheduleNext(timeZoneResolver, from, from, TimeSpan.Zero);\n        }\n\n        public IEnumerable<DateTime> ScheduleNext(\n            ITimeZoneResolver timeZoneResolver,\n            DateTime from,\n            DateTime now,\n            TimeSpan precision)\n        {\n            if (timeZoneResolver == null) throw new ArgumentNullException(nameof(timeZoneResolver));\n\n            var result = new List<DateTime>();\n            var cron = ParseCronExpression(Cron);\n            var timeZone = timeZoneResolver.GetTimeZoneById(TimeZoneId);\n\n            DateTime? next = from;\n\n            while ((next = cron.GetNextOccurrence(next.Value, timeZone, inclusive: false)) <= now)\n            {\n                if (next == now)\n                {\n                    result.Add(next.Value);\n                }\n                else\n                {\n                    switch (MisfireHandling)\n                    {\n                        case MisfireHandlingMode.Relaxed:\n                            next = now;\n                            result.Add(next.Value);\n                            break;\n                        case MisfireHandlingMode.Strict:\n                            result.Add(next.Value);\n                            break;\n                        case MisfireHandlingMode.Ignorable:\n                            if (now.Add(precision.Negate()) <= next && next <= now)\n                            {\n                                result.Add(next.Value);\n                            }\n\n                            break;\n                    }\n                }\n            }\n\n            NextExecution = next;\n            Error = null;\n            return result;\n        }\n\n        public bool IsChanged(DateTime now, out IReadOnlyDictionary<string, string> changedFields)\n        {\n            changedFields = GetChangedFields(now);\n            return changedFields.Count > 0;\n        }\n\n        public void ScheduleRetry(DateTime nextAttempt, string error)\n        {\n            RetryAttempt++;\n            Error = error;\n            NextExecution = nextAttempt;\n        }\n\n        public void Disable(string error)\n        {\n            NextExecution = null;\n            Error = error;\n        }\n\n        private IReadOnlyDictionary<string, string> GetChangedFields(DateTime now)\n        {\n            var result = new Dictionary<string, string>();\n\n            if ((_recurringJob.TryGetValue(\"Queue\", out var queue) ? queue : null) != Queue)\n            {\n                result.Add(\"Queue\", Queue);\n            }\n\n            if ((_recurringJob.TryGetValue(\"Cron\", out var cron) ? cron : null) != Cron)\n            {\n                result.Add(\"Cron\", Cron);\n            }\n\n            if ((_recurringJob.TryGetValue(\"TimeZoneId\", out var timeZoneId) ? timeZoneId : null) != TimeZoneId)\n            {\n                result.Add(\"TimeZoneId\", TimeZoneId);\n            }\n\n            if ((_recurringJob.TryGetValue(\"Job\", out var job) ? job : null) != Job)\n            {\n                result.Add(\"Job\", Job);\n            }\n\n            if (!_recurringJob.ContainsKey(\"CreatedAt\"))\n            {\n                result.Add(\"CreatedAt\", JobHelper.SerializeDateTime(now));\n            }\n\n            var serializedLastExecution = LastExecution.HasValue ? JobHelper.SerializeDateTime(LastExecution.Value) : null;\n\n            if ((_recurringJob.TryGetValue(\"LastExecution\", out var lastExecution) ? lastExecution : null) !=\n                serializedLastExecution)\n            {\n                result.Add(\"LastExecution\", serializedLastExecution ?? String.Empty);\n            }\n\n            var serializedNextExecution = NextExecution.HasValue ? JobHelper.SerializeDateTime(NextExecution.Value) : null;\n            if ((_recurringJob.TryGetValue(\"NextExecution\", out var next) ? next : null) !=\n                serializedNextExecution)\n            {\n                result.Add(\"NextExecution\", serializedNextExecution ?? String.Empty);\n            }\n\n            if ((_recurringJob.TryGetValue(\"LastJobId\", out var last) && !String.IsNullOrWhiteSpace(last) ? last : null) != LastJobId)\n            {\n                result.Add(\"LastJobId\", LastJobId ?? String.Empty);\n            }\n\n            var misfireHandlingValue = MisfireHandling.ToString(\"D\");\n            if ((!_recurringJob.ContainsKey(\"Misfire\") && MisfireHandling != MisfireHandlingMode.Relaxed) ||\n                (_recurringJob.TryGetValue(\"Misfire\", out var misfire) && misfire != misfireHandlingValue))\n            {\n                result.Add(\"Misfire\", misfireHandlingValue);\n            }\n\n            if (!_recurringJob.ContainsKey(\"V\"))\n            {\n                result.Add(\"V\", \"2\");\n            }\n\n            if ((_recurringJob.TryGetValue(\"Error\", out var error) && !String.IsNullOrWhiteSpace(error) ? error : null) != Error)\n            {\n                result.Add(\"Error\", Error ?? String.Empty);\n            }\n\n            var retryAttemptValue = RetryAttempt.ToString(CultureInfo.InvariantCulture);\n            if ((_recurringJob.TryGetValue(\"RetryAttempt\", out var retryAttempt) ? retryAttempt : null) != retryAttemptValue)\n            {\n                if (_recurringJob.ContainsKey(\"RetryAttempt\") || retryAttemptValue != \"0\")\n                {\n                    result.Add(\"RetryAttempt\", retryAttemptValue);\n                }\n            }\n\n            return result;\n        }\n\n        public override string ToString()\n        {\n            return String.Join(\";\", _recurringJob.Select(static x => $\"{x.Key}:{x.Value}\"));\n        }\n\n        public static CronExpression ParseCronExpression([NotNull] string cronExpression)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n\n            var format = CronFormat.Standard;\n\n            if (!cronExpression.StartsWith(\"@\", StringComparison.OrdinalIgnoreCase))\n            {\n                var parts = cronExpression.Split(SeparatorCharacters, StringSplitOptions.RemoveEmptyEntries);\n                if (parts.Length == 6)\n                {\n                    format |= CronFormat.IncludeSeconds;\n                }\n                else if (parts.Length != 5)\n                {\n                    throw new CronFormatException(\n                        $\"Wrong number of parts in the `{cronExpression}` cron expression, you can only use 5 or 6 (with seconds) part-based expressions.\");\n                }\n            }\n\n            return CronExpression.Parse(cronExpression, format);\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/RecurringJobExtensions.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Profiling;\nusing Hangfire.States;\nusing Hangfire.Storage;\n\nnamespace Hangfire\n{\n    internal static class RecurringJobExtensions\n    {\n        public static IDisposable AcquireDistributedRecurringJobLock(\n            [NotNull] this IStorageConnection connection,\n            [NotNull] string recurringJobId,\n            TimeSpan timeout)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));\n\n            return connection.AcquireDistributedLock($\"lock:recurring-job:{recurringJobId}\", timeout);\n        }\n\n        public static RecurringJobEntity GetRecurringJob(\n            [NotNull] this IStorageConnection connection,\n            [NotNull] string recurringJobId)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));\n\n            var recurringJob = connection.GetAllEntriesFromHash($\"recurring-job:{recurringJobId}\");\n            if (recurringJob == null || recurringJob.Count == 0) return null;\n\n            return new RecurringJobEntity(recurringJobId, recurringJob);\n        }\n\n        public static RecurringJobEntity GetOrCreateRecurringJob(\n            [NotNull] this IStorageConnection connection,\n            [NotNull] string recurringJobId)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));\n\n            var recurringJob = connection.GetAllEntriesFromHash($\"recurring-job:{recurringJobId}\");\n            if (recurringJob == null || recurringJob.Count == 0) recurringJob = new Dictionary<string, string>();\n\n            return new RecurringJobEntity(recurringJobId, recurringJob);\n        }\n\n        public static void UpdateRecurringJob(\n            [NotNull] this IWriteOnlyTransaction transaction,\n            [NotNull] RecurringJobEntity recurringJob,\n            [NotNull] IReadOnlyDictionary<string, string> changedFields,\n            [CanBeNull] ILog logger)\n        {\n            if (transaction == null) throw new ArgumentNullException(nameof(transaction));\n            if (recurringJob == null) throw new ArgumentNullException(nameof(recurringJob));\n            if (changedFields == null) throw new ArgumentNullException(nameof(changedFields));\n\n            if (changedFields.Count > 0)\n            {\n                transaction.SetRangeInHash($\"recurring-job:{recurringJob.RecurringJobId}\", changedFields);\n            }\n\n            var score = recurringJob.NextExecution.HasValue ? JobHelper.ToTimestamp(recurringJob.NextExecution.Value) : -1.0D;\n\n            if (logger != null && logger.IsTraceEnabled())\n            {\n                logger.Trace($\"Recurring job '{recurringJob.RecurringJobId}' is being updated. RecurringJob: ({recurringJob}), Changes: ({String.Join(\";\", changedFields.Select(static x => $\"{x.Key}:{x.Value}\"))})\");\n            }\n\n            transaction.AddToSet(\n                \"recurring-jobs\",\n                recurringJob.RecurringJobId,\n                score);\n        }\n\n        public static BackgroundJob TriggerRecurringJob(\n            [NotNull] this IBackgroundJobFactory factory,\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [NotNull] IProfiler profiler,\n            [NotNull] RecurringJobEntity recurringJob,\n            DateTime now)\n        {\n            if (factory == null) throw new ArgumentNullException(nameof(factory));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            if (profiler == null) throw new ArgumentNullException(nameof(profiler));\n            if (recurringJob == null) throw new ArgumentNullException(nameof(recurringJob));\n\n            if (recurringJob.Job == null)\n            {\n                throw new InvalidOperationException(\"The 'Job' field has a null or empty value\");\n            }\n\n            var job = InvocationData.DeserializePayload(recurringJob.Job).DeserializeJob();\n\n            var context = new CreateContext(storage, connection, job, null, null, profiler, null);\n            context.Parameters[\"RecurringJobId\"] = recurringJob.RecurringJobId;\n            context.Parameters[\"Time\"] = JobHelper.ToTimestamp(now);\n\n            var backgroundJob = factory.Create(context);\n\n            recurringJob.LastExecution = now;\n            recurringJob.LastJobId = backgroundJob?.Id;\n\n            return backgroundJob;\n        }\n\n        public static void EnqueueBackgroundJob(\n            [NotNull] this IStateMachine stateMachine,\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [NotNull] IWriteOnlyTransaction transaction,\n            [NotNull] RecurringJobEntity recurringJob,\n            [NotNull] BackgroundJob backgroundJob,\n            [CanBeNull] string reason,\n            [NotNull] IProfiler profiler)\n        {\n            if (stateMachine == null) throw new ArgumentNullException(nameof(stateMachine));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            if (transaction == null) throw new ArgumentNullException(nameof(transaction));\n            if (recurringJob == null) throw new ArgumentNullException(nameof(recurringJob));\n            if (backgroundJob == null) throw new ArgumentNullException(nameof(backgroundJob));\n            if (profiler == null) throw new ArgumentNullException(nameof(profiler));\n\n            var state = new EnqueuedState { Reason = reason };\n\n            if (recurringJob.Queue != null)\n            {\n                state.Queue = recurringJob.Queue;\n            }\n\n            stateMachine.ApplyState(new ApplyStateContext(\n                storage,\n                connection,\n                transaction,\n                backgroundJob,\n                state,\n                null,\n                profiler,\n                stateMachine));\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/RecurringJobManager.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Profiling;\nusing Hangfire.Storage;\n\nnamespace Hangfire\n{\n    /// <summary>\n    /// Represents a recurring job manager that allows to create, update\n    /// or delete recurring jobs.\n    /// </summary>\n    public class RecurringJobManager : IRecurringJobManager, IRecurringJobManagerV2\n    {\n        private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(15);\n\n        private readonly ILog _logger = LogProvider.GetLogger(typeof(RecurringJobManager));\n\n        private readonly JobStorage _storage;\n        private readonly IBackgroundJobFactory _factory;\n        private readonly Func<DateTime> _nowFactory;\n        private readonly ITimeZoneResolver _timeZoneResolver;\n\n        public RecurringJobManager()\n            : this(JobStorage.Current)\n        {\n        }\n\n        public RecurringJobManager([NotNull] JobStorage storage)\n            : this(storage, JobFilterProviders.Providers)\n        {\n        }\n\n        public RecurringJobManager([NotNull] JobStorage storage, [NotNull] IJobFilterProvider filterProvider)\n            : this(storage, filterProvider, new DefaultTimeZoneResolver())\n        {\n        }\n\n        public RecurringJobManager(\n            [NotNull] JobStorage storage, \n            [NotNull] IJobFilterProvider filterProvider,\n            [NotNull] ITimeZoneResolver timeZoneResolver)\n            : this(storage, filterProvider, timeZoneResolver, static () => DateTime.UtcNow)\n        {\n        }\n\n        public RecurringJobManager(\n            [NotNull] JobStorage storage, \n            [NotNull] IJobFilterProvider filterProvider, \n            [NotNull] ITimeZoneResolver timeZoneResolver,\n            [NotNull] Func<DateTime> nowFactory)\n            : this(storage, new BackgroundJobFactory(filterProvider), timeZoneResolver, nowFactory)\n        {\n        }\n\n        public RecurringJobManager([NotNull] JobStorage storage, [NotNull] IBackgroundJobFactory factory)\n            : this(storage, factory, new DefaultTimeZoneResolver())\n        {\n        }\n\n        public RecurringJobManager([NotNull] JobStorage storage, [NotNull] IBackgroundJobFactory factory, [NotNull] ITimeZoneResolver timeZoneResolver)\n            : this(storage, factory, timeZoneResolver, static () => DateTime.UtcNow)\n        {\n        }\n\n        internal RecurringJobManager(\n            [NotNull] JobStorage storage, \n            [NotNull] IBackgroundJobFactory factory,\n            [NotNull] ITimeZoneResolver timeZoneResolver,\n            [NotNull] Func<DateTime> nowFactory)\n        {\n            _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n            _factory = factory ?? throw new ArgumentNullException(nameof(factory));\n            _timeZoneResolver = timeZoneResolver ?? throw new ArgumentNullException(nameof(timeZoneResolver));\n            _nowFactory = nowFactory ?? throw new ArgumentNullException(nameof(nowFactory));\n        }\n\n        public JobStorage Storage => _storage;\n\n        public void AddOrUpdate(string recurringJobId, Job job, string cronExpression, RecurringJobOptions options)\n        {\n            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));\n            if (job == null) throw new ArgumentNullException(nameof(job));\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            ValidateCronExpression(cronExpression);\n\n            if (job.Queue != null && !Storage.HasFeature(JobStorageFeatures.JobQueueProperty))\n            {\n                throw new NotSupportedException(\"Current storage doesn't support specifying queues directly for a specific job. Please use the QueueAttribute instead.\");\n            }\n\n            using (var connection = _storage.GetConnection())\n            using (connection.AcquireDistributedRecurringJobLock(recurringJobId, DefaultTimeout))\n            {\n                var now = _nowFactory();\n                var recurringJob = connection.GetOrCreateRecurringJob(recurringJobId);\n                var scheduleChanged = false;\n\n                recurringJob.Job = InvocationData.SerializeJob(job).SerializePayload();\n\n                if (!cronExpression.Equals(recurringJob.Cron, StringComparison.OrdinalIgnoreCase))\n                {\n                    recurringJob.Cron = cronExpression;\n                    scheduleChanged = true;\n                }\n\n                if (!options.TimeZone.Id.Equals(recurringJob.TimeZoneId, StringComparison.OrdinalIgnoreCase))\n                {\n                    recurringJob.TimeZoneId = options.TimeZone.Id;\n                    scheduleChanged = true;\n                }\n\n#pragma warning disable 618\n                recurringJob.Queue = options.QueueName;\n#pragma warning restore 618\n                recurringJob.MisfireHandling = options.MisfireHandling;\n                recurringJob.RetryAttempt = 0;\n\n                if (scheduleChanged || recurringJob.Error != null)\n                {\n                    recurringJob.ScheduleNext(_timeZoneResolver, now.AddSeconds(-1));\n                }\n\n                if (recurringJob.IsChanged(now, out var changedFields))\n                {\n                    using (var transaction = connection.CreateWriteTransaction())\n                    {\n                        transaction.UpdateRecurringJob(recurringJob, changedFields, _logger);\n                        transaction.Commit();\n                    }\n                }\n            }\n        }\n \n        private static void ValidateCronExpression(string cronExpression)\n        {\n            try\n            {\n                RecurringJobEntity.ParseCronExpression(cronExpression);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new ArgumentException(\n                    \"CRON expression is invalid. Please see the inner exception for details.\",\n                    nameof(cronExpression),\n                    ex);\n            }\n        }\n\n        public void Trigger(string recurringJobId) => TriggerJob(recurringJobId);\n\n        [Obsolete(\"Please use the `TriggerJob` method instead. Will be removed in 2.0.0.\")]\n        public string TriggerExecution(string recurringJobId) => TriggerJob(recurringJobId);\n\n        public string TriggerJob(string recurringJobId)\n        {\n            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));\n\n            using (var connection = _storage.GetConnection())\n            using (connection.AcquireDistributedRecurringJobLock(recurringJobId, DefaultTimeout))\n            {\n                var now = _nowFactory();\n\n                var recurringJob = connection.GetRecurringJob(recurringJobId);\n                if (recurringJob == null) return null;\n\n                BackgroundJob backgroundJob;\n\n                try\n                {\n                    backgroundJob = _factory.TriggerRecurringJob(_storage, connection, EmptyProfiler.Instance, recurringJob, now);\n                    recurringJob.ScheduleNext(_timeZoneResolver, now);\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    // TODO: Preserving backward compatibility, should be removed in 2.0.0.\n                    throw new AggregateException(ex);\n                }\n\n                if (recurringJob.IsChanged(now, out var changedFields))\n                {\n                    using (var transaction = connection.CreateWriteTransaction())\n                    {\n                        if (backgroundJob != null)\n                        {\n                            _factory.StateMachine.EnqueueBackgroundJob(\n                                _storage,\n                                connection,\n                                transaction,\n                                recurringJob,\n                                backgroundJob,\n                                \"Triggered using recurring job manager\",\n                                EmptyProfiler.Instance);\n                        }\n\n                        transaction.UpdateRecurringJob(recurringJob, changedFields, _logger);\n                        transaction.Commit();\n                    }\n\n                    return backgroundJob?.Id;\n                }\n\n                return null;\n            }\n        }\n\n        public void RemoveIfExists(string recurringJobId)\n        {\n            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));\n\n            using (var connection = _storage.GetConnection())\n            using (connection.AcquireDistributedRecurringJobLock(recurringJobId, DefaultTimeout))\n            using (var transaction = connection.CreateWriteTransaction())\n            {\n                transaction.RemoveHash($\"recurring-job:{recurringJobId}\");\n                transaction.RemoveFromSet(\"recurring-jobs\", recurringJobId);\n\n                transaction.Commit();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/RecurringJobManagerExtensions.cs",
    "content": "// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Linq.Expressions;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    public static class RecurringJobManagerExtensions\n    {\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Job job,\n            [NotNull] string cronExpression)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, new RecurringJobOptions\n            {\n                TimeZone = TimeZoneInfo.Utc\n            });\n        }\n\n        [Obsolete(\"Please use the AddOrUpdate(string, Job, string, RecurringJobOptions) method instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Job job,\n            [NotNull] string cronExpression,\n            [NotNull] TimeZoneInfo timeZone)\n        {\n            AddOrUpdate(manager, recurringJobId, job, cronExpression, timeZone, EnqueuedState.DefaultQueue);\n        }\n\n        [Obsolete(\"Please use the AddOrUpdate(string, Job, string, RecurringJobOptions) method instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Job job,\n            [NotNull] string cronExpression,\n            [NotNull] TimeZoneInfo timeZone,\n            [NotNull] string queue)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n            if (timeZone == null) throw new ArgumentNullException(nameof(timeZone));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            manager.AddOrUpdate(\n                recurringJobId,\n                job,\n                cronExpression,\n                new RecurringJobOptions { QueueName = queue, TimeZone = timeZone });\n        }\n\n        [Obsolete(\"Please use the AddOrUpdate(string, Expression<Action>, Func<string>, RecurringJobOptions) extension method instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression,\n            TimeZoneInfo timeZone = null,\n            string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression(), timeZone, queue);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression(), options);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Action> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use the AddOrUpdate<T>(string, Expression<Action<T>>, Func<string>, RecurringJobOptions) extension method instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            TimeZoneInfo timeZone = null,\n            string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression(), timeZone, queue);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression(), options);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Action<T>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use the AddOrUpdate(string, Expression<Action>, string, RecurringJobOptions) extension method instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action> methodCall,\n            [NotNull] string cronExpression,\n            TimeZoneInfo timeZone = null,\n            string queue = EnqueuedState.DefaultQueue)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));\n            if (methodCall == null) throw new ArgumentNullException(nameof(methodCall));\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n\n            var job = Job.FromExpression(methodCall);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n\n            var job = Job.FromExpression(methodCall);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Action> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Action> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var job = Job.FromExpression(methodCall, queue);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        [Obsolete(\"Please use the AddOrUpdate<T>(string, Expression<Action<T>>, string, RecurringJobOptions) extension method instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression,\n            TimeZoneInfo timeZone = null,\n            string queue = EnqueuedState.DefaultQueue)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));\n            if (methodCall == null) throw new ArgumentNullException(nameof(methodCall));\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n\n            var job = Job.FromExpression(methodCall);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n\n            var job = Job.FromExpression(methodCall);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Action<T>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var job = Job.FromExpression(methodCall, queue);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        [Obsolete(\"Please use the AddOrUpdate(string, Expression<Func<Task>>, Func<string>, RecurringJobOptions) extension method instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            TimeZoneInfo timeZone = null,\n            string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression(), timeZone, queue);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression(), options);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Func<Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use the AddOrUpdate<T>(string, Expression<Func<T, Task>>, Func<string>, RecurringJobOptions) extension method instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            TimeZoneInfo timeZone = null,\n            string queue = EnqueuedState.DefaultQueue)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression(), timeZone, queue);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression(), options);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Func<T, Task>> methodCall,\n            [NotNull] Func<string> cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression(), options);\n        }\n\n        [Obsolete(\"Please use the AddOrUpdate(string, Expression<Func<Task>>, string, RecurringJobOptions) extension method instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression,\n            TimeZoneInfo timeZone = null,\n            string queue = EnqueuedState.DefaultQueue)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));\n            if (methodCall == null) throw new ArgumentNullException(nameof(methodCall));\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n\n            var job = Job.FromExpression(methodCall);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n\n            var job = Job.FromExpression(methodCall);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Func<Task>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var job = Job.FromExpression(methodCall, queue);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        [Obsolete(\"Please use the AddOrUpdate<T>(string, Expression<Func<T, Task>>, string, RecurringJobOptions) extension method instead. Will be removed in 2.0.0.\")]\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression,\n            TimeZoneInfo timeZone = null,\n            string queue = EnqueuedState.DefaultQueue)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n            if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId));\n            if (methodCall == null) throw new ArgumentNullException(nameof(methodCall));\n            if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));\n\n            var job = Job.FromExpression(methodCall);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n\n            var job = Job.FromExpression(methodCall);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression)\n        {\n            AddOrUpdate(manager, recurringJobId, queue, methodCall, cronExpression, new RecurringJobOptions());\n        }\n\n        public static void AddOrUpdate<T>(\n            [NotNull] this IRecurringJobManager manager,\n            [NotNull] string recurringJobId,\n            [NotNull] string queue,\n            [NotNull] Expression<Func<T, Task>> methodCall,\n            [NotNull] string cronExpression,\n            [NotNull] RecurringJobOptions options)\n        {\n            if (manager == null) throw new ArgumentNullException(nameof(manager));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var job = Job.FromExpression(methodCall, queue);\n            manager.AddOrUpdate(recurringJobId, job, cronExpression, options);\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/RecurringJobOptions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    public class RecurringJobOptions\n    {\n        private TimeZoneInfo _timeZone;\n        private string _queueName;\n\n        public RecurringJobOptions()\n        {\n            TimeZone = TimeZoneInfo.Utc;\n#pragma warning disable 618\n            QueueName = EnqueuedState.DefaultQueue;\n#pragma warning restore 618\n            MisfireHandling = MisfireHandlingMode.Relaxed;\n        }\n\n        [NotNull]\n        public TimeZoneInfo TimeZone\n        {\n            get { return _timeZone; }\n            set\n            {\n                if (value == null) throw new ArgumentNullException(nameof(value));\n\n                _timeZone = value;\n            }\n        }\n\n        [Obsolete(\"Please use non-obsolete AddOrUpdate with the explicit `queue` parameter instead. Will be removed in 2.0.0.\")]\n        [NotNull]\n        public string QueueName\n        {\n            get { return _queueName; }\n            set\n            {\n                EnqueuedState.ValidateQueueName(nameof(value), value);\n                _queueName = value;\n            }\n        }\n\n        public MisfireHandlingMode MisfireHandling { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/AspNetShutdownDetector.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2020 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing System.Threading;\nusing Hangfire.Logging;\n\nnamespace Hangfire.Server\n{\n    internal static class AspNetShutdownDetector\n    {\n        private static readonly TimeSpan CheckForShutdownTimerInterval = TimeSpan.FromMilliseconds(250);\n\n        [SuppressMessage(\"SonarLint\", \"S2930:IDisposablesShouldBeDisposed\", Justification = \"Has static lifetime, disposed on process shutdown.\")]\n        private static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();\n\n#if !NETSTANDARD1_3\n        private static int _isInitialized;\n        private static bool _isSucceeded;\n        // ReSharper disable once NotAccessedField.Local\n        private static Thread _checkForShutdownThread;\n        private static Func<string> _shutdownReasonFunc;\n        private static Func<bool> _checkConfigChangedFunc;\n        private static Func<bool> _disposingHttpRuntime;\n#endif\n\n        public static bool IsSucceeded =>\n#if !NETSTANDARD1_3\n            _isSucceeded\n#else\n            false\n#endif\n        ;\n\n        public static CancellationToken GetShutdownToken()\n        {\n#if !NETSTANDARD1_3\n            EnsureInitialized();\n#endif\n            return CancellationTokenSource.Token;\n        }\n\n        public static bool DisposingHttpRuntime =>\n#if !NETSTANDARD1_3\n            _disposingHttpRuntime != null && _disposingHttpRuntime()\n#else\n            false\n#endif\n            ;\n\n#if !NETSTANDARD1_3\n        private static void EnsureInitialized()\n        {\n            if (Interlocked.Exchange(ref _isInitialized, 1) != 0) return;\n\n            try\n            {\n                // Normally when ASP.NET is stopping our web application, IRegisteredObject.Stop\n                // method is called for all the registered objects, and it is the recommended\n                // way for handling shutdowns when we have some custom background threads.\n                // \n                // Hangfire uses OWIN's \"host.OnAppDisposing\" and \"server.OnDispose\" keys that\n                // provide a cancellation token which is canceled after OWIN's own registered\n                // object is stopped, and this method works most of the time. But...\n\n                // Long-running web requests can prevent ASP.NET from stopping the registered\n                // objects, because it waits for all the requests to end before shutting down\n                // the application. But since .NET Framework 4.5.1, ASP.NET triggers the\n                // StopListening event in these cases, so we can listen it and initiate a\n                // shutdown once our application stopped to listen for the new requests.\n                RegisterForStopListeningEvent(ref _isSucceeded);\n\n                // Overlapped recycles feature is turned on by default in IIS, which cause\n                // both old and new application to be active at the same time during app\n                // domain recycles, which may cause old servers to process background jobs\n                // from the new ones, resulting in exceptions when new methods added.\n                // Also during deployments we can get numerous of startup/shutdown attempts,\n                // because file updates aren't transactional, and since deploys often touch\n                // multiple files.\n                // Unfortunately during such deploys registered objects often don't stopped\n                // carefully, especially when \"autostart providers\" feature is used, perhaps\n                // due to some race conditions in ASP.NET.\n                // After investigating source code of ASP.NET I've found that there's no better\n                // solution for this issue other than to check the shutdown reason from time\n                // to time.\n                InitializeShutdownReason(ref _isSucceeded);\n\n                // This check is based on the HttpRuntime._disposingHttpRuntime field that's\n                // modified just before app domain is being unloaded, when new app domain was\n                // already created.\n                InitializeDisposingHttpRuntime(ref _isSucceeded);\n\n                // And the last method to check for application shutdown is implemented in\n                // SignalR and Kudu service by checking the UnsafeIISMethods.MgdHasConfigChanged\n                // method. But I was failed to find this method in the recent ASP.NET sources.\n                // But nevertheless it may be useful to have it for older versions.\n                InitializeMgdHasConfigChanged(ref _isSucceeded);\n\n                if (_isSucceeded)\n                {\n                    _checkForShutdownThread = new Thread(CheckForAppDomainShutdown)\n                    {\n                        Name = \"AspNetShutdownDetector\",\n                        IsBackground = true,\n                    };\n                    _checkForShutdownThread.Start();\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                GetLogger().ErrorException(\"Failed to initialize shutdown triggers for ASP.NET application.\", ex);\n            }\n        }\n\n        private static void CheckForAppDomainShutdown(object state)\n        {\n            try\n            {\n                while (!CancellationTokenSource.IsCancellationRequested)\n                {\n                    if (_checkConfigChangedFunc != null && _checkConfigChangedFunc())\n                    {\n                        Cancel(\"UnsafeIISMethods.MgdHasConfigChanged\");\n                        break;\n                    }\n\n                    var shutdownReason = _shutdownReasonFunc?.Invoke();\n                    if (shutdownReason != null)\n                    {\n                        Cancel($\"HostingEnvironment.ShutdownReason: {shutdownReason}\");\n                        break;\n                    }\n\n                    if (_disposingHttpRuntime != null && _disposingHttpRuntime())\n                    {\n                        Cancel(\"HttpRuntime._disposingHttpRuntime\");\n                        break;\n                    }\n\n                    Thread.Sleep(CheckForShutdownTimerInterval);\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                GetLogger().ErrorException(\n                    \"An exception occurred while checking for ASP.NET shutdown, will not able to do the checks properly.\",\n                    ex);\n            }\n        }\n\n        private static void Cancel(string reason)\n        {\n            GetLogger().Info($\"ASP.NET application is shutting down: {reason}.\");\n\n            try\n            {\n                CancellationTokenSource.Cancel(throwOnFirstException: false);\n            }\n            catch (ObjectDisposedException)\n            {\n            }\n            catch (AggregateException ag)\n            {\n                GetLogger().ErrorException(\"One or more exceptions were thrown during app pool shutdown: \", ag);\n            }\n        }\n\n        private static void RegisterForStopListeningEvent(ref bool success)\n        {\n            try\n            {\n                var hostingEnvironmentType = Type.GetType(\"System.Web.Hosting.HostingEnvironment, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\", false);\n                if (hostingEnvironmentType == null) return;\n\n                var stopEvent = hostingEnvironmentType.GetEvent(\"StopListening\", BindingFlags.Static | BindingFlags.Public);\n                if (stopEvent == null) return;\n\n                stopEvent.AddEventHandler(null, new EventHandler(StopListening));\n                GetLogger().Debug(\"HostingEnvironment.StopListening shutdown trigger initialized successfully.\");\n                success = true;\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                GetLogger().DebugException(\"Unable to initialize HostingEnvironment.StopListening shutdown trigger\", ex);\n            }\n        }\n\n        private static void StopListening(object sender, EventArgs e)\n        {\n            Cancel(\"HostingEnvironment.StopListening\");\n        }\n\n        private static void InitializeShutdownReason(ref bool success)\n        {\n            try\n            {\n                var hostingEnvironment = Type.GetType(\"System.Web.Hosting.HostingEnvironment, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\", false);\n                if (hostingEnvironment == null) return;\n\n                var shutdownReason = hostingEnvironment.GetProperty(\"ShutdownReason\", BindingFlags.Static | BindingFlags.Public);\n                if (shutdownReason == null) return;\n\n                _shutdownReasonFunc = ShutdownReasonFunc;\n\n                GetLogger().Debug(\"HostingEnvironment.ShutdownReason shutdown trigger initialized successfully.\");\n                success = true;\n\n                string ShutdownReasonFunc()\n                {\n                    try\n                    {\n                        var shutdownReasonValue = shutdownReason.GetValue(null);\n\n                        if (shutdownReasonValue != null && (int)shutdownReasonValue != 0)\n                        {\n                            return shutdownReasonValue.ToString();\n                        }\n                    }\n                    catch (Exception ex) when (ex.IsCatchableExceptionType())\n                    {\n                        GetLogger().TraceException(\"Unable to call the HostingEnvironment.ShutdownReason property due to an exception.\", ex);\n                    }\n\n                    return null;\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                GetLogger().DebugException(\"Unable to initialize HostingEnvironment.ShutdownReason shutdown trigger\", ex);\n            }\n        }\n\n        private static void InitializeMgdHasConfigChanged(ref bool success)\n        {\n            try\n            {\n                var type = Type.GetType(\"System.Web.Hosting.UnsafeIISMethods, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\", false);\n                if (type == null) return;\n\n                var methodInfo = type.GetMethod(\"MgdHasConfigChanged\", BindingFlags.NonPublic  | BindingFlags.Static);\n                if (methodInfo == null) return;\n\n                _checkConfigChangedFunc = (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), methodInfo);\n\n                GetLogger().Debug(\"UnsafeIISMethods.MgdHasConfigChanged shutdown trigger initialized successfully.\");\n                success = true;\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                GetLogger().DebugException(\"Unable to initialize UnsafeIISMethods.MgdHasConfigChanged shutdown trigger\", ex);\n            }\n        }\n\n        private static void InitializeDisposingHttpRuntime(ref bool success)\n        {\n            try\n            {\n                var type = Type.GetType(\"System.Web.HttpRuntime, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\", false);\n                if (type == null) return;\n\n                var theRuntimeInfo = type.GetField(\"_theRuntime\", BindingFlags.NonPublic | BindingFlags.Static);\n                if (theRuntimeInfo == null) return;\n\n                var disposingHttpRuntimeInfo = type.GetField(\"_disposingHttpRuntime\", BindingFlags.NonPublic | BindingFlags.Instance);\n                if (disposingHttpRuntimeInfo == null) return;\n\n                var theRuntime = CreateGetStaticFieldDelegate<object>(theRuntimeInfo);\n                var disposingHttpRuntime = CreateGetFieldDelegate<bool>(disposingHttpRuntimeInfo, type);\n\n                _disposingHttpRuntime = () => disposingHttpRuntime(theRuntime());\n\n                GetLogger().Debug(\"HttpRuntime._disposingHttpRuntime shutdown trigger initialized successfully.\");\n                success = true;\n            }\n            catch (Exception ex)\n            {\n                GetLogger().DebugException(\"Unable to initialize HttpRuntime._disposingHttpRuntime shutdown trigger\", ex);\n            }\n        }\n\n        private static Func<T> CreateGetStaticFieldDelegate<T>(FieldInfo fieldInfo)\n        {\n            var fieldExp = Expression.Field(null, fieldInfo);\n            return Expression.Lambda<Func<T>>(fieldExp).Compile();\n        }\n\n        private static Func<object, T> CreateGetFieldDelegate<T>(FieldInfo fieldInfo, Type type)\n        {\n            var instExp = Expression.Parameter(typeof(object));\n            var convExp = Expression.Convert(instExp, type);\n            var fieldExp = Expression.Field(convExp, fieldInfo);\n            return Expression.Lambda<Func<object, T>>(fieldExp, instExp).Compile();\n        }\n#endif\n\n        private static ILog GetLogger()\n        {\n            return LogProvider.GetLogger(typeof(AspNetShutdownDetector));\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/BackgroundJobPerformer.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Profiling;\n\nnamespace Hangfire.Server\n{\n    public class BackgroundJobPerformer : IBackgroundJobPerformer\n    {\n        internal static readonly string ContextCanceledKey = \"X_HF_Canceled\";\n\n        private readonly IJobFilterProvider _filterProvider;\n        private readonly IBackgroundJobPerformer _innerPerformer;\n\n        public BackgroundJobPerformer()\n            : this(JobFilterProviders.Providers)\n        {\n        }\n\n        public BackgroundJobPerformer([NotNull] IJobFilterProvider filterProvider)\n            : this(filterProvider, JobActivator.Current)\n        {\n        }\n\n        public BackgroundJobPerformer(\n            [NotNull] IJobFilterProvider filterProvider,\n            [NotNull] JobActivator activator)\n            : this(filterProvider, activator, TaskScheduler.Default)\n        {\n        }\n\n        public BackgroundJobPerformer(\n            [NotNull] IJobFilterProvider filterProvider,\n            [NotNull] JobActivator activator,\n            [CanBeNull] TaskScheduler taskScheduler)\n            : this(filterProvider, new CoreBackgroundJobPerformer(activator, taskScheduler))\n        {\n        }\n\n        internal BackgroundJobPerformer(\n            [NotNull] IJobFilterProvider filterProvider, \n            [NotNull] IBackgroundJobPerformer innerPerformer)\n        {\n            if (filterProvider == null) throw new ArgumentNullException(nameof(filterProvider));\n            if (innerPerformer == null) throw new ArgumentNullException(nameof(innerPerformer));\n\n            _filterProvider = filterProvider;\n            _innerPerformer = innerPerformer;\n        }\n\n        public object Perform(PerformContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            var filterInfo = GetFilters(context.BackgroundJob.Job);\n\n            try\n            {\n                context.Performer = this;\n                return PerformJobWithFilters(context, filterInfo.ServerFilters);\n            }\n            catch (JobAbortedException)\n            {\n                throw;\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                // TODO: Catch only JobPerformanceException, and pass InnerException to filters in 2.0.0.\n\n                if (ex is OperationCanceledException && context.CancellationToken.ShutdownToken.IsCancellationRequested)\n                {\n                    throw;\n                }\n\n                var exceptionContext = new ServerExceptionContext(context, ex);\n                InvokeServerExceptionFilters(exceptionContext, filterInfo.ServerExceptionFiltersReversed);\n\n                if (!exceptionContext.ExceptionHandled)\n                {\n                    throw;\n                }\n            }\n            finally\n            {\n                context.Performer = null;\n            }\n\n            return null;\n        }\n\n        private JobFilterInfo GetFilters(Job job)\n        {\n            return new JobFilterInfo(_filterProvider.GetFilters(job));\n        }\n\n        private object PerformJobWithFilters(PerformContext context, JobFilterInfo.FilterCollection<IServerFilter> filters)\n        {\n            var preContext = new PerformingContext(context);\n            var enumerator = filters.GetEnumerator();\n\n            return InvokeNextServerFilter(ref enumerator, _innerPerformer, context, preContext).Result;\n        }\n\n        private static PerformedContext InvokeNextServerFilter(\n            ref JobFilterInfo.FilterCollection<IServerFilter>.Enumerator enumerator,\n            IBackgroundJobPerformer innerPerformer,\n            PerformContext context,\n            PerformingContext preContext)\n        {\n            if (enumerator.MoveNext())\n            {\n                return InvokeServerFilter(ref enumerator, innerPerformer, context, preContext);\n            }\n\n            var result = innerPerformer.Perform(context);\n            return new PerformedContext(context, result, false, null);\n        }\n\n        private static PerformedContext InvokeServerFilter(\n            ref JobFilterInfo.FilterCollection<IServerFilter>.Enumerator enumerator,\n            IBackgroundJobPerformer innerPerformer,\n            PerformContext context,\n            PerformingContext preContext)\n        {\n            var filter = enumerator.Current!;\n\n            preContext.Profiler.InvokeMeasured(\n                new KeyValuePair<IServerFilter, PerformingContext>(filter, preContext),\n                InvokeOnPerforming,\n                static ctx => $\"OnPerforming for {ctx.Value.BackgroundJob.Id}\" );\n            \n            if (preContext.Canceled)\n            {\n                if (!preContext.Items.ContainsKey(ContextCanceledKey))\n                {\n                    preContext.Items.Add(ContextCanceledKey, filter.GetType().Name);\n                }\n                \n                return new PerformedContext(preContext, null, true, null);\n            }\n\n            var wasError = false;\n            PerformedContext postContext;\n            try\n            {\n                postContext = InvokeNextServerFilter(ref enumerator, innerPerformer, context, preContext);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                wasError = true;\n                postContext = new PerformedContext(\n                    preContext, null, false, ex);\n\n                postContext.Profiler.InvokeMeasured(\n                    new KeyValuePair<IServerFilter, PerformedContext>(filter, postContext),\n                    InvokeOnPerformed,\n                    static ctx => $\"OnPerformed for {ctx.Value.BackgroundJob.Id}\");\n\n                if (!postContext.ExceptionHandled)\n                {\n                    throw;\n                }\n            }\n\n            if (!wasError)\n            {\n                postContext.Profiler.InvokeMeasured(\n                    new KeyValuePair<IServerFilter, PerformedContext>(filter, postContext),\n                    InvokeOnPerformed,\n                    static ctx => $\"OnPerformed for {ctx.Value.BackgroundJob.Id}\");\n            }\n\n            return postContext;\n        }\n\n        private static void InvokeOnPerforming(KeyValuePair<IServerFilter, PerformingContext> ctx)\n        {\n            try\n            {\n                ctx.Key.OnPerforming(ctx.Value);\n            }\n            catch (Exception filterException) when (filterException.IsCatchableExceptionType())\n            {\n                CoreBackgroundJobPerformer.HandleJobPerformanceException(\n                    filterException,\n                    ctx.Value.CancellationToken, ctx.Value.BackgroundJob);\n            }\n        }\n\n        private static void InvokeOnPerformed(KeyValuePair<IServerFilter, PerformedContext> ctx)\n        {\n            try\n            {\n                ctx.Key.OnPerformed(ctx.Value);\n            }\n            catch (Exception filterException) when (filterException.IsCatchableExceptionType())\n            {\n                CoreBackgroundJobPerformer.HandleJobPerformanceException(\n                    filterException,\n                    ctx.Value.CancellationToken, ctx.Value.BackgroundJob);\n            }\n        }\n\n        private static void InvokeServerExceptionFilters(\n            ServerExceptionContext context,\n            JobFilterInfo.ReversedFilterCollection<IServerExceptionFilter> filters)\n        {\n            foreach (var filter in filters)\n            {\n                context.Profiler.InvokeMeasured(\n                    new KeyValuePair<IServerExceptionFilter, ServerExceptionContext>(filter, context),\n                    InvokeOnServerException,\n                    static ctx => $\"OnServerException for {ctx.Value.BackgroundJob.Id}\");\n            }\n        }\n\n        private static void InvokeOnServerException(KeyValuePair<IServerExceptionFilter, ServerExceptionContext> ctx)\n        {\n            try\n            {\n                ctx.Key.OnServerException(ctx.Value);\n            }\n            catch (Exception filterException) when (filterException.IsCatchableExceptionType())\n            {\n                CoreBackgroundJobPerformer.HandleJobPerformanceException(\n                    filterException,\n                    ctx.Value.CancellationToken, ctx.Value.BackgroundJob);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/BackgroundProcessContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\n\nnamespace Hangfire.Server\n{\n    public class BackgroundProcessContext\n    {\n        [Obsolete(\"This constructor overload is deprecated and will be removed in 2.0.0.\")]\n        public BackgroundProcessContext(\n            [NotNull] string serverId,\n            [NotNull] JobStorage storage,\n            [NotNull] IDictionary<string, object> properties,\n            CancellationToken cancellationToken)\n            : this(serverId, storage, properties, Guid.NewGuid(), cancellationToken, cancellationToken, cancellationToken)\n        {\n        }\n\n        public BackgroundProcessContext(\n            [NotNull] string serverId,\n            [NotNull] JobStorage storage, \n            [NotNull] IDictionary<string, object> properties, \n            Guid executionId,\n            CancellationToken stoppingToken,\n            CancellationToken stoppedToken,\n            CancellationToken shutdownToken)\n        {\n            if (serverId == null) throw new ArgumentNullException(nameof(serverId));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (properties == null) throw new ArgumentNullException(nameof(properties));\n\n            ServerId = serverId;\n            Storage = storage;\n            ExecutionId = executionId;\n            Properties = new Dictionary<string, object>(properties, StringComparer.OrdinalIgnoreCase);\n            StoppingToken = stoppingToken;\n            StoppedToken = stoppedToken;\n            ShutdownToken = shutdownToken;\n        }\n        \n        [NotNull]\n        public string ServerId { get; }\n\n        [NotNull]\n        public IReadOnlyDictionary<string, object> Properties { get; }\n\n        [NotNull]\n        public JobStorage Storage { get; }\n\n        public Guid ExecutionId { get; }\n\n        [Obsolete(\"Please use the StoppingToken property instead, will be removed in 2.0.0.\")]\n        public CancellationToken CancellationToken => StoppingToken;\n\n        public CancellationToken StoppingToken { get; }\n        public CancellationToken StoppedToken { get; }\n        public CancellationToken ShutdownToken { get; }\n\n        public bool IsStopping => StoppingToken.IsCancellationRequested || StoppedToken.IsCancellationRequested || ShutdownToken.IsCancellationRequested;\n        public bool IsStopped => StoppedToken.IsCancellationRequested || ShutdownToken.IsCancellationRequested;\n\n        [Obsolete(\"Please use IsStopping or IsStopped properties instead. Will be removed in 2.0.0.\")]\n        public bool IsShutdownRequested => StoppingToken.IsCancellationRequested;\n\n        public void Wait(TimeSpan timeout)\n        {\n            StoppingToken.WaitOrThrow(timeout);\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/BackgroundProcessDispatcherBuilder.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Processing;\n\nnamespace Hangfire.Server\n{\n    internal sealed class BackgroundProcessDispatcherBuilder : IBackgroundProcessDispatcherBuilder\n    {\n        private readonly IBackgroundProcess _process;\n        private readonly Func<ThreadStart, IEnumerable<Thread>> _threadFactory;\n\n        public BackgroundProcessDispatcherBuilder(\n            [NotNull] IBackgroundProcess process,\n            [NotNull] Func<ThreadStart, IEnumerable<Thread>> threadFactory)\n        {\n            if (process == null) throw new ArgumentNullException(nameof(process));\n            if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory));\n\n            _process = process;\n            _threadFactory = threadFactory;\n        }\n\n        public IBackgroundDispatcher Create(BackgroundServerContext context, BackgroundProcessingServerOptions options)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            var execution = new BackgroundExecution(\n                new BackgroundExecutionOptions\n                {\n                    Name = _process.GetType().Name,\n                    RetryDelay = options.RetryDelay\n                },\n                context.StoppingToken);\n\n            return new BackgroundDispatcher(\n                execution,\n                ExecuteProcess,\n                Tuple.Create(_process, context, execution),\n                _threadFactory);\n        }\n\n        public override string ToString()\n        {\n            return _process.GetType().Name;\n        }\n\n        private static void ExecuteProcess(Guid executionId, object state)\n        {\n            var tuple = (Tuple<IBackgroundProcess, BackgroundServerContext, BackgroundExecution>)state;\n            var serverContext = tuple.Item2;\n\n            var context = new BackgroundProcessContext(\n                serverContext.ServerId,\n                serverContext.Storage,\n                serverContext.Properties.ToDictionary(static x => x.Key, static x => x.Value),\n                executionId,\n                serverContext.StoppingToken,\n                serverContext.StoppedToken,\n                serverContext.ShutdownToken);\n\n            while (!context.IsStopping)\n            {\n                tuple.Item1.Execute(context);\n                tuple.Item3.NotifySucceeded();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/BackgroundProcessDispatcherBuilderAsync.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Processing;\n\nnamespace Hangfire.Server\n{\n    internal sealed class BackgroundProcessDispatcherBuilderAsync : IBackgroundProcessDispatcherBuilder\n    {\n        private readonly int _maxConcurrency;\n        private readonly bool _ownsScheduler;\n        private readonly Func<TaskScheduler> _taskScheduler;\n        private readonly IBackgroundProcessAsync _process;\n\n        public BackgroundProcessDispatcherBuilderAsync(\n            [NotNull] IBackgroundProcessAsync process,\n            [NotNull] Func<TaskScheduler> taskScheduler,\n            int maxConcurrency,\n            bool ownsScheduler)\n        {\n            if (process == null) throw new ArgumentNullException(nameof(process));\n            if (taskScheduler == null) throw new ArgumentNullException(nameof(taskScheduler));\n            if (maxConcurrency <= 0) throw new ArgumentOutOfRangeException(nameof(maxConcurrency));\n\n            _process = process;\n            _taskScheduler = taskScheduler;\n            _maxConcurrency = maxConcurrency;\n            _ownsScheduler = ownsScheduler;\n        }\n\n        public IBackgroundDispatcher Create(BackgroundServerContext context, BackgroundProcessingServerOptions options)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            var execution = new BackgroundExecution(\n                new BackgroundExecutionOptions\n                {\n                    Name = _process.GetType().Name,\n                    RetryDelay = options.RetryDelay\n                },\n                context.StoppingToken);\n\n            return new BackgroundDispatcherAsync(\n                execution,\n                ExecuteProcess,\n                Tuple.Create(_process, context, execution),\n                _taskScheduler(),\n                _maxConcurrency,\n                _ownsScheduler);\n        }\n\n        public override string ToString()\n        {\n            return _process.GetType().Name;\n        }\n\n        private static async Task ExecuteProcess(Guid executionId, object state)\n        {\n            var tuple = (Tuple<IBackgroundProcessAsync, BackgroundServerContext, BackgroundExecution>)state;\n            var serverContext = tuple.Item2;\n\n            var context = new BackgroundProcessContext(\n                serverContext.ServerId, \n                serverContext.Storage,\n                serverContext.Properties.ToDictionary(static x => x.Key, static x => x.Value), \n                executionId, \n                serverContext.StoppingToken, \n                serverContext.StoppedToken,\n                serverContext.ShutdownToken);\n\n            while (!context.IsStopping)\n            {\n                await tuple.Item1.ExecuteAsync(context).ConfigureAwait(true);\n                tuple.Item3.NotifySucceeded();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/BackgroundProcessExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing Hangfire.Processing;\n\nnamespace Hangfire.Server\n{\n    public static class BackgroundProcessExtensions\n    {\n        public static IBackgroundProcessDispatcherBuilder UseBackgroundPool(\n            [NotNull] this IBackgroundProcess process)\n        {\n            return UseBackgroundPool(process, Environment.ProcessorCount);\n        }\n\n        public static IBackgroundProcessDispatcherBuilder UseBackgroundPool(\n            [NotNull] this IBackgroundProcess process,\n            int threadCount)\n        {\n            return UseBackgroundPool(process, threadCount, null);\n        }\n\n        public static IBackgroundProcessDispatcherBuilder UseBackgroundPool(\n            [NotNull] this IBackgroundProcess process,\n            int threadCount,\n            [CanBeNull] Action<Thread> threadConfig)\n        {\n            if (process == null) throw new ArgumentNullException(nameof(process));\n            if (threadCount <= 0) throw new ArgumentOutOfRangeException(nameof(threadCount));\n\n            return UseBackgroundPool(\n                process,\n                (threadName, threadStart) => DefaultThreadFactory(threadCount, threadName, threadStart, threadConfig));\n        }\n\n        public static IBackgroundProcessDispatcherBuilder UseBackgroundPool(\n            [NotNull] this IBackgroundProcess process,\n            [NotNull] Func<string, ThreadStart, IEnumerable<Thread>> threadFactory)\n        {\n            if (process == null) throw new ArgumentNullException(nameof(process));\n            if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory));\n\n            return new BackgroundProcessDispatcherBuilder(\n                process,\n                threadStart => threadFactory(process.GetType().Name, threadStart));\n        }\n\n        public static IBackgroundProcessDispatcherBuilder UseBackgroundPool(\n            [NotNull] this IBackgroundProcessAsync process)\n        {\n            return UseBackgroundPool(process, Environment.ProcessorCount);\n        }\n\n        public static IBackgroundProcessDispatcherBuilder UseBackgroundPool(\n            [NotNull] this IBackgroundProcessAsync process,\n            int maxConcurrency)\n        {\n            return UseBackgroundPool(\n                process,\n                maxConcurrency,\n                maxConcurrency < Environment.ProcessorCount ? maxConcurrency : Environment.ProcessorCount);\n        }\n\n        public static IBackgroundProcessDispatcherBuilder UseBackgroundPool(\n            [NotNull] this IBackgroundProcessAsync process,\n            int maxConcurrency,\n            int threadCount)\n        {\n            if (process == null) throw new ArgumentNullException(nameof(process));\n            if (maxConcurrency <= 0) throw new ArgumentOutOfRangeException(nameof(maxConcurrency));\n            if (threadCount <= 0) throw new ArgumentOutOfRangeException(nameof(threadCount));\n\n            return UseBackgroundPool(\n                process,\n                maxConcurrency,\n                (threadName, threadStart) => DefaultThreadFactory(threadCount, threadName, threadStart, null));\n        }\n\n        public static IBackgroundProcessDispatcherBuilder UseBackgroundPool(\n            [NotNull] this IBackgroundProcessAsync process,\n            int maxConcurrency,\n            [NotNull] Func<string, ThreadStart, IEnumerable<Thread>> threadFactory)\n        {\n            if (process == null) throw new ArgumentNullException(nameof(process));\n            if (maxConcurrency <= 0) throw new ArgumentOutOfRangeException(nameof(maxConcurrency));\n            if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory));\n\n            Func<TaskScheduler> createScheduler = () => new BackgroundTaskScheduler(\n                threadStart => threadFactory(process.GetType().Name, threadStart),\n                static exception =>\n                {\n                    LogProvider.GetLogger(typeof(BackgroundTaskScheduler)).FatalException(\n                        \"Unhandled exception occurred in scheduler. Please report it to Hangfire developers\",\n                        exception);\n                });\n\n            return new BackgroundProcessDispatcherBuilderAsync(process, createScheduler, maxConcurrency, true);\n        }\n\n        public static IBackgroundProcessDispatcherBuilder UseThreadPool(\n            [NotNull] this IBackgroundProcessAsync process)\n        {\n            return UseThreadPool(process, Environment.ProcessorCount);\n        }\n\n        public static IBackgroundProcessDispatcherBuilder UseThreadPool(\n            [NotNull] this IBackgroundProcessAsync process,\n            int maxConcurrency)\n        {\n            if (process == null) throw new ArgumentNullException(nameof(process));\n            if (maxConcurrency <= 0) throw new ArgumentOutOfRangeException(nameof(maxConcurrency));\n\n            return new BackgroundProcessDispatcherBuilderAsync(process, static () => TaskScheduler.Default, maxConcurrency, false);\n        }\n\n        internal static IEnumerable<Thread> DefaultThreadFactory(\n            int threadCount,\n            [NotNull] string threadName,\n            [NotNull] ThreadStart threadStart,\n            [CanBeNull] Action<Thread> threadConfig = null)\n        {\n            if (threadName == null) throw new ArgumentNullException(nameof(threadName));\n            if (threadStart == null) throw new ArgumentNullException(nameof(threadStart));\n            if (threadCount <= 0) throw new ArgumentOutOfRangeException(nameof(threadCount));\n\n            for (var i = 0; i < threadCount; i++)\n            {\n                var thread = new Thread(threadStart)\n                {\n                    IsBackground = true,\n                    Name = $\"{threadName} #{i + 1}\"\n                };\n\n                threadConfig?.Invoke(thread);\n                yield return thread;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/BackgroundProcessingServer.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing Hangfire.Processing;\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Responsible for running the given collection background processes.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// Immediately starts the processes in a background thread.\n    /// Responsible for announcing/removing a server, bound to a storage.\n    /// Wraps all the processes with an infinite loop and automatic retry.\n    /// Executes all the processes in a single context.\n    /// Uses timeout in dispose method, waits for all the components, cancel signals shutdown\n    /// Contains some required processes and uses storage processes.\n    /// Generates unique id.\n    /// Properties are still bad.\n    /// </remarks>\n    public sealed class BackgroundProcessingServer : IBackgroundProcessingServer\n    {\n        public static readonly TimeSpan DefaultShutdownTimeout = TimeSpan.FromSeconds(15);\n        private static int _lastThreadId;\n\n        private readonly ILog _logger = LogProvider.GetLogger(typeof(BackgroundProcessingServer));\n\n        private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();\n        private readonly CancellationTokenSource _stoppedCts = new CancellationTokenSource();\n        private readonly CancellationTokenSource _shutdownCts = new CancellationTokenSource();\n        private CancellationTokenRegistration _shutdownRegistration;\n\n        private readonly IBackgroundServerProcess _process;\n        private readonly BackgroundProcessingServerOptions _options;\n        private readonly IBackgroundDispatcher _dispatcher;\n\n        private int _disposed;\n        private bool _awaited;\n\n        public BackgroundProcessingServer([NotNull] IEnumerable<IBackgroundProcess> processes)\n            : this(JobStorage.Current, processes)\n        {\n        }\n\n        public BackgroundProcessingServer(\n            [NotNull] IEnumerable<IBackgroundProcess> processes,\n            [NotNull] IDictionary<string, object> properties)\n            : this(JobStorage.Current, processes, properties)\n        {\n        }\n\n        public BackgroundProcessingServer(\n            [NotNull] JobStorage storage,\n            [NotNull] IEnumerable<IBackgroundProcess> processes)\n            : this(storage, processes, new Dictionary<string, object>())\n        {\n        }\n\n        public BackgroundProcessingServer(\n            [NotNull] JobStorage storage,\n            [NotNull] IEnumerable<IBackgroundProcess> processes,\n            [NotNull] IDictionary<string, object> properties)\n            : this(storage, processes, properties, new BackgroundProcessingServerOptions())\n        {\n        }\n\n        public BackgroundProcessingServer(\n            [NotNull] JobStorage storage,\n            [NotNull] IEnumerable<IBackgroundProcess> processes,\n            [NotNull] IDictionary<string, object> properties,\n            [NotNull] BackgroundProcessingServerOptions options)\n            : this(storage, GetProcesses(processes), properties, options)\n        {\n        }\n\n        public BackgroundProcessingServer(\n            [NotNull] JobStorage storage,\n            [NotNull] IEnumerable<IBackgroundProcessDispatcherBuilder> dispatcherBuilders,\n            [NotNull] IDictionary<string, object> properties,\n            [NotNull] BackgroundProcessingServerOptions options)\n            : this(new BackgroundServerProcess(storage, dispatcherBuilders, options, properties), options)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundProcessingServer\"/>\n        /// class and immediately starts all the given background processes.\n        /// </summary>\n        internal BackgroundProcessingServer(\n            [NotNull] BackgroundServerProcess process,\n            [NotNull] BackgroundProcessingServerOptions options)\n        {\n            _process = process ?? throw new ArgumentNullException(nameof(process));\n            _options = options ?? throw new ArgumentNullException(nameof(options));\n\n            _dispatcher = CreateDispatcher();\n\n#if !NETSTANDARD1_3\n            AppDomain.CurrentDomain.DomainUnload += OnCurrentDomainUnload;\n            AppDomain.CurrentDomain.ProcessExit += OnCurrentDomainUnload;\n#endif\n\n            _shutdownRegistration = AspNetShutdownDetector.GetShutdownToken().Register(OnAspNetShutdown);\n        }\n\n        public void SendStop()\n        {\n            ThrowIfDisposed();\n\n            _stoppingCts.Cancel();\n            _stoppedCts.CancelAfter(_options.StopTimeout);\n            _shutdownCts.CancelAfter(_options.ShutdownTimeout);\n        }\n\n        public bool WaitForShutdown(TimeSpan timeout)\n        {\n            ThrowIfDisposed();\n\n            Volatile.Write(ref _awaited, true);\n            return _dispatcher.Wait(timeout);\n        }\n\n        public async Task WaitForShutdownAsync(CancellationToken cancellationToken)\n        {\n            ThrowIfDisposed();\n\n            Volatile.Write(ref _awaited, true);\n            await _dispatcher.WaitAsync(Timeout.InfiniteTimeSpan, cancellationToken).ConfigureAwait(false);\n        }\n\n        public void Dispose()\n        {\n            if (Volatile.Read(ref _disposed) == 1) return;\n\n            _shutdownRegistration.Dispose();\n\n            if (!_stoppingCts.IsCancellationRequested)\n            {\n                SendStop();\n            }\n\n            if (!Volatile.Read(ref _awaited))\n            {\n                WaitForShutdown(Timeout.InfiniteTimeSpan);\n            }\n\n            if (Interlocked.Exchange(ref _disposed, 1) == 1) return;\n\n#if !NETSTANDARD1_3\n            AppDomain.CurrentDomain.DomainUnload -= OnCurrentDomainUnload;\n            AppDomain.CurrentDomain.ProcessExit -= OnCurrentDomainUnload;\n#endif\n\n            _dispatcher.Dispose();\n            _stoppingCts.Dispose();\n            _stoppedCts.Dispose();\n            _shutdownCts.Dispose();\n        }\n\n        private void OnCurrentDomainUnload(object sender, EventArgs args)\n        {\n            if (Volatile.Read(ref _disposed) == 1) return;\n\n            _logger.Warn(\"Stopping the server due to DomainUnload or ProcessExit event...\");\n\n            _stoppingCts.Cancel();\n            _stoppedCts.Cancel();\n            _shutdownCts.Cancel();\n\n            if (!AspNetShutdownDetector.IsSucceeded)\n            {\n                // ASP.NET can be very sensitive to any delays during AppDomain unload.\n                WaitForShutdown(_options.LastChanceTimeout);\n            }\n        }\n\n        private void OnAspNetShutdown()\n        {\n            if (Volatile.Read(ref _disposed) == 1)\n            {\n                // Exit if our server was already disposed, there's no need to\n                // throw ObjectDisposedException when unnecessary.\n                return;\n            }\n\n            try\n            {\n                // When ASP.NET shutdown is detected, we only need to send a stop\n                // signal to our background processing servers to allow correctly\n                // await for background processing server shutdown during a direct\n                // or indirect call to IRegisteredObject.Stop method, such as\n                // OWIN's \"onAppDisposing\" event.\n                SendStop();\n            }\n            catch (ObjectDisposedException)\n            {\n                // There's a benign race condition, when SendStop is called after\n                // processing server was already disposed.\n            }\n        }\n\n        private static IBackgroundProcessDispatcherBuilder[] GetProcesses([NotNull] IEnumerable<IBackgroundProcess> processes)\n        {\n            if (processes == null) throw new ArgumentNullException(nameof(processes));\n            return processes.Select(static x => x.UseBackgroundPool(threadCount: 1)).ToArray();\n        }\n\n        private IBackgroundDispatcher CreateDispatcher()\n        {\n            var execution = new BackgroundExecution(\n                new BackgroundExecutionOptions\n                {\n                    Name = nameof(BackgroundServerProcess),\n                    ErrorThreshold = TimeSpan.Zero,\n                    StillErrorThreshold = TimeSpan.Zero,\n                    RetryDelay = retry => _options.RestartDelay\n                },\n                _stoppingCts.Token);\n\n            return new BackgroundDispatcher(\n                execution,\n                RunServer,\n                execution,\n                ThreadFactory);\n        }\n\n        private void RunServer(Guid executionId, object state)\n        {\n            _process.Execute(executionId, (BackgroundExecution)state, _stoppingCts.Token, _stoppedCts.Token, _shutdownCts.Token);\n        }\n\n        private static IEnumerable<Thread> ThreadFactory(ThreadStart threadStart)\n        {\n            yield return new Thread(threadStart)\n            {\n                IsBackground = true,\n                Name = $\"{nameof(BackgroundServerProcess)} #{Interlocked.Increment(ref _lastThreadId)}\",\n            };\n        }\n\n        private void ThrowIfDisposed()\n        {\n            if (Volatile.Read(ref _disposed) == 1)\n            {\n                throw new ObjectDisposedException(GetType().FullName);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/BackgroundProcessingServerOptions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Processing;\n\nnamespace Hangfire.Server\n{\n    public sealed class BackgroundProcessingServerOptions\n    {\n        internal static TimeSpan DefaultStopTimeout = TimeSpan.FromMilliseconds(500);\n        internal static TimeSpan DefaultLastChanceTimeout = TimeSpan.FromSeconds(1);\n        internal static TimeSpan DefaultHeartbeatInterval = TimeSpan.FromSeconds(30);\n\n        private Func<int, TimeSpan> _retryDelay;\n\n        public BackgroundProcessingServerOptions()\n        {\n            HeartbeatInterval = DefaultHeartbeatInterval;\n            ServerCheckInterval = ServerWatchdog.DefaultCheckInterval;\n            ServerTimeout = ServerWatchdog.DefaultServerTimeout;\n\n            CancellationCheckInterval = ServerJobCancellationWatcher.DefaultCheckInterval;\n\n            RetryDelay = BackgroundExecutionOptions.GetBackOffMultiplier;\n            RestartDelay = TimeSpan.FromSeconds(15);\n\n            StopTimeout = DefaultStopTimeout;\n            ShutdownTimeout = BackgroundProcessingServer.DefaultShutdownTimeout;\n            LastChanceTimeout = DefaultLastChanceTimeout;\n        }\n\n        public TimeSpan HeartbeatInterval { get; set; }\n        public TimeSpan ServerCheckInterval { get; set; }\n        public TimeSpan ServerTimeout { get; set; }\n        public TimeSpan CancellationCheckInterval { get; set; }\n        public string ServerName { get; set; }\n        public bool ExcludeStorageProcesses { get; set; }\n\n        public Func<int, TimeSpan> RetryDelay\n        {\n            get => _retryDelay;\n            set => _retryDelay = value ?? throw new ArgumentNullException(nameof(value));\n        }\n\n        public TimeSpan StopTimeout { get; set; }\n        public TimeSpan ShutdownTimeout { get; set; }\n        public TimeSpan LastChanceTimeout { get; set; }\n        public TimeSpan RestartDelay { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/BackgroundServerContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2018 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Server\n{\n    public class BackgroundServerContext\n    {\n        public BackgroundServerContext(\n            [NotNull] string serverId,\n            [NotNull] JobStorage storage,\n            [NotNull] IDictionary<string, object> properties, \n            CancellationToken stoppingToken, \n            CancellationToken stoppedToken,\n            CancellationToken shutdownToken)\n        {\n            ServerId = serverId ?? throw new ArgumentNullException(nameof(serverId));\n            Storage = storage ?? throw new ArgumentNullException(nameof(storage));\n            Properties = properties ?? throw new ArgumentNullException(nameof(properties));\n            StoppingToken = stoppingToken;\n            StoppedToken = stoppedToken;\n            ShutdownToken = shutdownToken;\n        }\n\n        public string ServerId { get; }\n        public JobStorage Storage { get; }\n        public IDictionary<string, object> Properties { get; }\n        public CancellationToken StoppingToken { get; }\n        public CancellationToken StoppedToken { get; }\n        public CancellationToken ShutdownToken { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/BackgroundServerProcess.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing Hangfire.Processing;\n\nnamespace Hangfire.Server\n{\n    internal sealed class BackgroundServerProcess : IBackgroundServerProcess\n    {\n        private static readonly char[] ColonSeparator = new [] { ':' };\n\n        private readonly ILog _logger = LogProvider.GetLogger(typeof(BackgroundServerProcess));\n        private readonly JobStorage _storage;\n        private readonly BackgroundProcessingServerOptions _options;\n        private readonly IDictionary<string, object> _properties;\n        private readonly IBackgroundProcessDispatcherBuilder[] _dispatcherBuilders;\n\n        public BackgroundServerProcess(\n            [NotNull] JobStorage storage,\n            [NotNull] IEnumerable<IBackgroundProcessDispatcherBuilder> dispatcherBuilders,\n            [NotNull] BackgroundProcessingServerOptions options,\n            [NotNull] IDictionary<string, object> properties)\n        {\n            if (dispatcherBuilders == null) throw new ArgumentNullException(nameof(dispatcherBuilders));\n\n            _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n            _options = options ?? throw new ArgumentNullException(nameof(options));\n            _properties = properties ?? throw new ArgumentNullException(nameof(properties));\n\n            var builders = new List<IBackgroundProcessDispatcherBuilder>();\n            builders.AddRange(GetRequiredProcesses());\n            if (!options.ExcludeStorageProcesses)\n            {\n                builders.AddRange(GetStorageComponents());\n            }\n            builders.AddRange(dispatcherBuilders);\n\n            _dispatcherBuilders = builders.ToArray();\n        }\n\n        public void Execute(Guid executionId, BackgroundExecution execution, CancellationToken stoppingToken,\n            CancellationToken stoppedToken, CancellationToken shutdownToken)\n        {\n            var serverId = GetServerId();\n            Stopwatch stoppedAt = null;\n\n            void HandleStopRestartSignal() => Interlocked.CompareExchange(ref stoppedAt, Stopwatch.StartNew(), null);\n            void HandleStoppingSignal() => _logger.Info($\"{GetServerTemplate(serverId)} caught stopping signal...\");\n            void HandleStoppedSignal() => _logger.Info($\"{GetServerTemplate(serverId)} caught stopped signal...\");\n            void HandleShutdownSignal() => _logger.Warn($\"{GetServerTemplate(serverId)} caught shutdown signal...\");\n\n            void HandleRestartSignal()\n            {\n                if (!stoppingToken.IsCancellationRequested)\n                {\n                    _logger.Info($\"{GetServerTemplate(serverId)} caught restart signal...\");\n                }\n            }\n\n            //using (LogProvider.OpenMappedContext(\"ServerId\", serverId.ToString()))\n            using (var restartCts = new CancellationTokenSource())\n            using (var restartStoppingCts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken, restartCts.Token))\n            using (var restartStoppedCts = CancellationTokenSource.CreateLinkedTokenSource(stoppedToken, restartCts.Token))\n            using (var restartShutdownCts = CancellationTokenSource.CreateLinkedTokenSource(shutdownToken, restartCts.Token))\n            using (restartStoppingCts.Token.Register(HandleStopRestartSignal))\n            using (stoppingToken.Register(HandleStoppingSignal))\n            using (stoppedToken.Register(HandleStoppedSignal))\n            using (shutdownToken.Register(HandleShutdownSignal))\n            using (restartCts.Token.Register(HandleRestartSignal))\n            {\n                var context = new BackgroundServerContext(\n                    serverId,\n                    _storage,\n                    _properties,\n                    restartStoppingCts.Token,\n                    restartStoppedCts.Token,\n                    restartShutdownCts.Token);\n\n                var dispatchers = new List<IBackgroundDispatcher>();\n\n                CreateServer(context);\n\n                try\n                {\n                    // ReSharper disable once AccessToDisposedClosure\n                    using (var heartbeat = CreateHeartbeatProcess(context, () => restartCts.Cancel()))\n                    {\n                        StartDispatchers(context, dispatchers);\n                        execution.NotifySucceeded();\n\n                        WaitForDispatchers(context, dispatchers);\n\n                        restartCts.Cancel();\n\n                        // TODO Either modify the IBackgroundDispatcher.Wait method to handle CancellationToken\n                        // or expose the WaitHandle property to not to perform sync-over-async and vice versa\n                        // in 2.0.\n                        heartbeat.WaitAsync(Timeout.InfiniteTimeSpan, shutdownToken).GetAwaiter().GetResult();\n                    }\n                }\n                finally\n                {\n                    DisposeDispatchers(dispatchers);\n                    ServerDelete(context, stoppedAt);\n                }\n            }\n        }\n\n        private IBackgroundDispatcher CreateHeartbeatProcess(BackgroundServerContext context, Action requestRestart)\n        {\n            // We need to ensure that heartbeats are sent until server shutdown. Otherwise our server\n            // can be considered as dead before completing all the background jobs on a graceful\n            // shutdown.\n            var heartbeatContext = new BackgroundServerContext(\n                context.ServerId,\n                context.Storage,\n                context.Properties,\n                context.ShutdownToken,\n                context.ShutdownToken,\n                context.ShutdownToken);\n\n            return new ServerHeartbeatProcess(_options.HeartbeatInterval, _options.ServerTimeout, requestRestart)\n                .UseBackgroundPool(threadCount: 1\n#if !NETSTANDARD1_3\n                    , static thread => { thread.Priority = ThreadPriority.AboveNormal; }\n#endif\n                )\n                .Create(heartbeatContext, _options);\n        }\n\n        private IEnumerable<IBackgroundProcessDispatcherBuilder> GetRequiredProcesses()\n        {\n            yield return new ServerWatchdog(_options.ServerCheckInterval, _options.ServerTimeout).UseBackgroundPool(threadCount: 1);\n            yield return new ServerJobCancellationWatcher(_options.CancellationCheckInterval).UseBackgroundPool(threadCount: 1);\n\n            if (_storage.HasFeature(Storage.JobStorageFeatures.ProcessesInsteadOfComponents))\n            {\n                foreach (var serverProcess in _storage.GetServerRequiredProcesses())\n                {\n                    yield return serverProcess.UseBackgroundPool(threadCount: 1);\n                }\n            }\n        }\n\n        private IEnumerable<IBackgroundProcessDispatcherBuilder> GetStorageComponents()\n        {\n            if (_storage.HasFeature(Storage.JobStorageFeatures.ProcessesInsteadOfComponents))\n            {\n                return _storage.GetStorageWideProcesses().Select(static process => process.UseBackgroundPool(threadCount: 1));\n            }\n\n#pragma warning disable CS0618 // Type or member is obsolete\n            return _storage.GetComponents().Select(static component => new ServerProcessDispatcherBuilder(\n#pragma warning restore CS0618 // Type or member is obsolete\n                component, \n                threadStart => BackgroundProcessExtensions.DefaultThreadFactory(1, component.GetType().Name, threadStart, null)));\n        }\n\n        private string GetServerId()\n        {\n            var serverName = _options.ServerName\n                 ?? Environment.GetEnvironmentVariable(\"COMPUTERNAME\")\n                 ?? Environment.GetEnvironmentVariable(\"HOSTNAME\")\n#if !NETSTANDARD1_3\n                 ?? Environment.MachineName\n#endif\n                ;\n\n            var guid = Guid.NewGuid().ToString();\n\n#if !NETSTANDARD1_3\n            if (!String.IsNullOrWhiteSpace(serverName))\n            {\n                serverName += \":\" + Process.GetCurrentProcess().Id;\n            }\n#endif\n\n            return !String.IsNullOrWhiteSpace(serverName) ? $\"{serverName.ToLowerInvariant()}:{guid}\" : guid;\n        }\n\n        private void CreateServer(BackgroundServerContext context)\n        {\n            _logger.Trace($\"{GetServerTemplate(context.ServerId)} is announcing itself...\");\n\n            var stopwatch = Stopwatch.StartNew();\n\n            using (var connection = _storage.GetConnection())\n            {\n                connection.AnnounceServer(context.ServerId, GetServerContext(_properties));\n            }\n\n            stopwatch.Stop();\n\n            ServerJobCancellationToken.AddServer(context.ServerId);\n            _logger.Info($\"{GetServerTemplate(context.ServerId)} successfully announced in {stopwatch.Elapsed.TotalMilliseconds} ms\");\n        }\n\n        private void ServerDelete(BackgroundServerContext context, Stopwatch stoppedAt)\n        {\n            try\n            {\n                _logger.Trace($\"{GetServerTemplate(context.ServerId)} is reporting itself as stopped...\");\n                ServerJobCancellationToken.RemoveServer(context.ServerId);\n\n                var stopwatch = Stopwatch.StartNew();\n\n                using (var connection = _storage.GetConnection())\n                {\n                    connection.RemoveServer(context.ServerId);\n                }\n\n                stopwatch.Stop();\n\n                _logger.Info($\"{GetServerTemplate(context.ServerId)} successfully reported itself as stopped in {stopwatch.Elapsed.TotalMilliseconds} ms\");\n                _logger.Info($\"{GetServerTemplate(context.ServerId)} has been stopped in total {stoppedAt?.Elapsed.TotalMilliseconds ?? 0} ms\");\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                _logger.WarnException($\"{GetServerTemplate(context.ServerId)} there was an exception, server may not be removed\", ex);\n            }\n        }\n\n        private void StartDispatchers(BackgroundServerContext context, ICollection<IBackgroundDispatcher> dispatchers)\n        {\n            if (_dispatcherBuilders.Length == 0)\n            {\n                throw new InvalidOperationException(\"No dispatchers registered for the processing server.\");\n            }\n\n            _logger.Info($\"{GetServerTemplate(context.ServerId)} is starting the registered dispatchers: {String.Join(\", \", _dispatcherBuilders.Select(static builder => $\"{builder}\"))}...\");\n\n            foreach (var dispatcherBuilder in _dispatcherBuilders)\n            {\n                dispatchers.Add(dispatcherBuilder.Create(context, _options));\n            }\n\n            _logger.Info($\"{GetServerTemplate(context.ServerId)} all the dispatchers started\");\n        }\n\n        private void WaitForDispatchers(\n            BackgroundServerContext context,\n            IReadOnlyList<IBackgroundDispatcher> dispatchers)\n        {\n            if (dispatchers.Count == 0) return;\n\n            var waitTasks = new Task[dispatchers.Count];\n\n            for (var i = 0; i < dispatchers.Count; i++)\n            {\n                waitTasks[i] = dispatchers[i].WaitAsync(Timeout.InfiniteTimeSpan, CancellationToken.None);\n            }\n\n            var nonStopped = new List<IBackgroundDispatcher>();\n\n            try\n            {\n                Task.WaitAll(waitTasks, context.ShutdownToken);\n            }\n            catch (OperationCanceledException)\n            {\n                for (var i = 0; i < dispatchers.Count; i++)\n                {\n                    if (waitTasks[i].Status != TaskStatus.RanToCompletion)\n                    {\n                        nonStopped.Add(dispatchers[i]);\n                    }\n                }\n            }\n\n            if (nonStopped.Count > 0)\n            {\n                var nonStoppedNames = nonStopped.Select(static dispatcher => $\"{dispatcher}\").ToArray();\n                _logger.Warn($\"{GetServerTemplate(context.ServerId)} stopped non-gracefully due to {String.Join(\", \", nonStoppedNames)}. Outstanding work on those dispatchers could be aborted, and there can be delays in background processing. This server instance will be incorrectly shown as active for a while. To avoid non-graceful shutdowns, investigate what prevents from stopping gracefully and add CancellationToken support for those methods.\");\n            }\n            else\n            {\n                _logger.Info($\"{GetServerTemplate(context.ServerId)} All dispatchers stopped\");\n            }\n        }\n\n        private static void DisposeDispatchers(IEnumerable<IBackgroundDispatcher> dispatchers)\n        {\n            foreach (var dispatcher in dispatchers)\n            {\n                dispatcher.Dispose();\n            }\n        }\n\n        private static ServerContext GetServerContext(IDictionary<string, object> properties)\n        {\n            var serverContext = new ServerContext();\n\n            if (properties.TryGetValue(\"Queues\", out var queues) && queues is string[] array)\n            {\n                serverContext.Queues = array;\n            }\n\n            if (properties.TryGetValue(\"WorkerCount\", out var workerCount))\n            {\n                serverContext.WorkerCount = (int)workerCount;\n            }\n\n            return serverContext;\n        }\n\n        internal static string GetServerTemplate(string serverId)\n        {\n            string name = serverId;\n\n            try\n            {\n                var split = serverId.Split(ColonSeparator, StringSplitOptions.RemoveEmptyEntries);\n                if (split.Length == 3 && split[2].Length > 8)\n                {\n                    name = $\"{split[0]}:{split[1]}:{split[2].Substring(0, 8)}\";\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                // ignored\n            }\n\n            return $\"Server {name}\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/CoreBackgroundJobPerformer.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing System.Runtime.ExceptionServices;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Processing;\n\nnamespace Hangfire.Server\n{\n    internal sealed class CoreBackgroundJobPerformer : IBackgroundJobPerformer\n    {\n        internal static readonly Dictionary<Type, Func<PerformContext, object>> Substitutions\n            = new Dictionary<Type, Func<PerformContext, object>>\n            {\n                { typeof (IJobCancellationToken), static x => x.CancellationToken },\n                { typeof (CancellationToken), static x => x.CancellationToken.ShutdownToken },\n                { typeof (PerformContext), static x => x }\n            };\n\n        private readonly JobActivator _activator;\n        private readonly TaskScheduler _taskScheduler;\n\n        public CoreBackgroundJobPerformer([NotNull] JobActivator activator, [CanBeNull] TaskScheduler taskScheduler)\n        {\n            _activator = activator ?? throw new ArgumentNullException(nameof(activator));\n            _taskScheduler = taskScheduler;\n        }\n\n        public object Perform(PerformContext context)\n        {\n            using (var scope = _activator.BeginScope(context))\n            {\n                object instance = null;\n\n                if (context.BackgroundJob.Job == null)\n                {\n                    throw new InvalidOperationException(\"Can't perform a background job with a null job.\");\n                }\n                \n                if (!context.BackgroundJob.Job.Method.IsStatic)\n                {\n                    instance = scope.Resolve(context.BackgroundJob.Job.Type);\n\n                    if (instance == null)\n                    {\n                        throw new InvalidOperationException(\n                            $\"JobActivator returned NULL instance of the '{context.BackgroundJob.Job.Type}' type.\");\n                    }\n                }\n\n                var arguments = SubstituteArguments(context);\n                var result = InvokeMethod(context, instance, arguments);\n\n                return result;\n            }\n        }\n\n        internal static void HandleJobPerformanceException(Exception exception, IJobCancellationToken cancellationToken, [CanBeNull] BackgroundJob job)\n        {\n            if (exception is JobAbortedException)\n            {\n                // JobAbortedException exception should be thrown as-is to notify\n                // a worker that background job was aborted by a state change, and\n                // should NOT be re-queued.\n                ExceptionDispatchInfo.Capture(exception).Throw();\n            }\n            \n            if (exception is OperationCanceledException && cancellationToken.IsAborted())\n            {\n                // OperationCanceledException exception is thrown because \n                // ServerJobCancellationWatcher has detected the job was aborted.\n                throw new JobAbortedException();\n            }\n\n            if (exception is OperationCanceledException && cancellationToken.ShutdownToken.IsCancellationRequested)\n            {\n                // OperationCanceledException exceptions are treated differently from\n                // others, when ShutdownToken's cancellation was requested, to notify\n                // a worker that job performance was aborted by a shutdown request,\n                // and a job identifier should BE re-queued.\n                ExceptionDispatchInfo.Capture(exception).Throw();\n                throw exception;\n            }\n\n            // Other exceptions are wrapped with JobPerformanceException to preserve a\n            // shallow stack trace without Hangfire methods.\n            throw new JobPerformanceException(\n                \"An exception occurred during performance of the job.\",\n                exception, job?.Id);\n        }\n\n        private object InvokeMethod(PerformContext context, object instance, object[] arguments)\n        {\n            if (context.BackgroundJob.Job == null) return null;\n\n            try\n            {\n                var methodInfo = context.BackgroundJob.Job.Method;\n                var method = new BackgroundJobMethod(methodInfo, instance, arguments);\n                var returnType = methodInfo.ReturnType;\n\n                if (returnType.IsTaskLike(out var getTaskFunc))\n                {\n                    if (_taskScheduler != null)\n                    {\n                        return InvokeOnTaskScheduler(context, method, getTaskFunc);\n                    }\n\n                    return InvokeOnTaskPump(context, method, getTaskFunc);\n                }\n\n                return InvokeSynchronously(method);\n            }\n            catch (ArgumentException ex)\n            {\n                HandleJobPerformanceException(ex, context.CancellationToken, context.BackgroundJob);\n                throw;\n            }\n            catch (AggregateException ex)\n            {\n                HandleJobPerformanceException(ex.InnerException, context.CancellationToken, context.BackgroundJob);\n                throw;\n            }\n            catch (TargetInvocationException ex)\n            {\n                HandleJobPerformanceException(ex.InnerException, context.CancellationToken, context.BackgroundJob);\n                throw;\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                HandleJobPerformanceException(ex, context.CancellationToken, context.BackgroundJob);\n                throw;\n            }\n        }\n\n        private object InvokeOnTaskScheduler(PerformContext context, BackgroundJobMethod method, Func<object, Task> getTaskFunc)\n        {\n            var scheduledTask = Task.Factory.StartNew(\n                InvokeOnTaskSchedulerInternal,\n                method,\n                CancellationToken.None,\n                TaskCreationOptions.None,\n                _taskScheduler);\n\n            var result = scheduledTask.GetAwaiter().GetResult();\n            if (result == null) return null;\n\n            return getTaskFunc(result).GetTaskLikeResult(result, method.ReturnType);\n        }\n\n        private static object InvokeOnTaskSchedulerInternal(object state)\n        {\n            // ExecutionContext is captured automatically when calling the Task.Factory.StartNew\n            // method, so we don't need to capture it manually. Please see the comment for\n            // synchronous method execution below for details.\n            return ((BackgroundJobMethod)state).Invoke();\n        }\n\n        private static object InvokeOnTaskPump(PerformContext context, BackgroundJobMethod method, Func<object, Task> getTaskFunc)\n        {\n            // Using SynchronizationContext here is the best default option, where workers\n            // are still running on synchronous dispatchers, and where a single job performer\n            // may be used by multiple workers. We can't create a separate TaskScheduler\n            // instance of every background job invocation, because TaskScheduler.Id may\n            // overflow relatively fast, and can't use single scheduler for multiple performers\n            // for better isolation in the default case – non-default external scheduler should\n            // be used. It's also great to preserve backward compatibility for those who are\n            // using Parallel.For(Each), since we aren't changing the TaskScheduler.Current.\n\n            var oldSyncContext = SynchronizationContext.Current;\n\n            try\n            {\n                using (var syncContext = new InlineSynchronizationContext())\n                using (var cancellationEvent = context.CancellationToken.ShutdownToken.GetCancellationEvent())\n                {\n                    SynchronizationContext.SetSynchronizationContext(syncContext);\n\n                    var result = InvokeSynchronously(method);\n                    if (result == null) return null;\n\n                    var task = getTaskFunc(result);\n                    var asyncResult = (IAsyncResult)task;\n\n                    var waitHandles = new[] { syncContext.WaitHandle, asyncResult.AsyncWaitHandle, cancellationEvent.WaitHandle };\n\n                    while (!asyncResult.IsCompleted && WaitHandle.WaitAny(waitHandles) == 0)\n                    {\n                        var workItem = syncContext.Dequeue();\n                        workItem.Item1(workItem.Item2);\n                    }\n\n                    return task.GetTaskLikeResult(result, method.ReturnType);\n                }\n            }\n            finally\n            {\n                SynchronizationContext.SetSynchronizationContext(oldSyncContext);\n            }\n        }\n\n        private static object InvokeSynchronously(object state)\n        {\n            var method = (BackgroundJobMethod) state;\n            var executionContext = ExecutionContext.Capture();\n\n            if (executionContext != null)\n            {\n                // Asynchronous methods started with the TaskScheduler.StartNew method, capture the\n                // current execution context by default and call the ExecutionContext.Run method on\n                // a thread pool thread to pass the current AsyncLocal values there.\n                //\n                // As a side effect of this call, any updates to AsyncLocal values which happen inside\n                // the background job method itself values aren't passed back to the calling thread\n                // and end their lifetime as soon as method execution is finished.\n                //\n                // Synchronous methods don't need to capture the current execution context because the\n                // thread is not changed. However, any updates to AsyncLocal values that happen inside\n                // the background job method are passed back to the calling thread, expanding their\n                // lifetime to the lifetime of the thread itself. This can result in memory leaks and\n                // possibly affect future background job method executions in unexpected ways.\n                //\n                // To avoid this and to have the same behavior of AsyncLocal between synchronous and\n                // asynchronous methods, we run synchronous ones in a captured execution context. The\n                // ExecutionContext.Run method ensures that AsyncLocal values will be modified only in\n                // the captured context and will not flow back to the parent context.\n                ExecutionContext.Run(executionContext, InvokeSynchronouslyInternal, method);\n                return method.Result;\n            }\n\n            return method.Invoke();\n        }\n\n        private static void InvokeSynchronouslyInternal(object state)\n        {\n            ((BackgroundJobMethod)state).Invoke();\n        }\n\n        private static object[] SubstituteArguments(PerformContext context)\n        {\n            if (context.BackgroundJob.Job == null)\n            {\n                return null;\n            }\n\n            var parameters = context.BackgroundJob.Job.Method.GetParameters();\n            var result = new List<object>(context.BackgroundJob.Job.Args.Count);\n\n            for (var i = 0; i < parameters.Length; i++)\n            {\n                var parameter = parameters[i];\n                var argument = context.BackgroundJob.Job.Args[i];\n\n                var value = Substitutions.TryGetValue(parameter.ParameterType, out var substitution) \n                    ? substitution(context) \n                    : argument;\n\n                result.Add(value);\n            }\n\n            return result.ToArray();\n        }\n\n        private sealed class BackgroundJobMethod(MethodInfo methodInfo, object instance, object[] parameters)\n        {\n            public Type ReturnType => methodInfo.ReturnType;\n            public object Result { get; private set; }\n\n            public object Invoke()\n            {\n                Result = methodInfo.Invoke(instance, parameters);\n                return Result;\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/DelayedJobScheduler.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Profiling;\nusing Hangfire.States;\nusing Hangfire.Storage;\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Represents a background process responsible for <i>enqueueing delayed\n    /// jobs</i>.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// <para>This background process polls the <i>delayed job schedule</i> for \n    /// delayed jobs that are ready to be enqueued. To prevent a stress load\n    /// on a job storage, the configurable delay is used between scheduler \n    /// runs. Delay is used only when there are no more background jobs to be\n    /// enqueued.</para>\n    /// \n    /// <para>When a background job is ready to be enqueued, it is simply\n    /// moved from <see cref=\"ScheduledState\"/> to the <see cref=\"EnqueuedState\"/>\n    /// by using <see cref=\"IBackgroundJobStateChanger\"/>.</para>\n    /// \n    /// <para>Delayed job schedule is based on a Set data structure of a job\n    /// storage, so you can use this background process as an example of a\n    /// custom extension.</para>\n    ///  \n    /// <para>Multiple instances of this background process can be used in\n    /// separate threads/processes without additional configuration (distributed\n    /// locks are used). However, this only adds support for fail-over, and does \n    /// not increase the performance.</para>\n    /// \n    /// <note type=\"important\">\n    /// If you are using <b>custom filter providers</b>, you need to pass a custom\n    /// <see cref=\"IBackgroundJobStateChanger\"/> instance to make this process\n    /// respect your filters when enqueueing background jobs.\n    /// </note>\n    /// </remarks>\n    /// \n    /// <threadsafety static=\"true\" instance=\"true\"/>\n    /// \n    /// <seealso cref=\"ScheduledState\"/>\n    public class DelayedJobScheduler : IBackgroundProcess\n    {\n        /// <summary>\n        /// Represents a default polling interval for delayed job scheduler. \n        /// This field is read-only.\n        /// </summary>\n        /// <remarks>\n        /// The value of this field is <c>TimeSpan.FromSeconds(15)</c>.\n        /// </remarks>\n        public static readonly TimeSpan DefaultPollingDelay = TimeSpan.FromSeconds(15);\n        private static readonly TimeSpan DefaultLockTimeout = TimeSpan.FromMinutes(1);\n        private static readonly int BatchSize = 1000;\n        private static readonly int MaxStateChangeAttempts = 5;\n\n        private readonly ILog _logger = LogProvider.For<DelayedJobScheduler>();\n        private readonly ConcurrentDictionary<Type, bool> _isBatchingAvailableCache = new ConcurrentDictionary<Type, bool>();\n\n        private readonly IBackgroundJobStateChanger _stateChanger;\n        private readonly IProfiler _profiler;\n        private readonly TimeSpan _pollingDelay;\n        private bool _parallelismIssueLogged;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DelayedJobScheduler\"/>\n        /// class with the <see cref=\"DefaultPollingDelay\"/> value as a\n        /// delay between runs.\n        /// </summary>\n        public DelayedJobScheduler() \n            : this(DefaultPollingDelay)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DelayedJobScheduler\"/>\n        /// class with a specified polling interval.\n        /// </summary>\n        /// <param name=\"pollingDelay\">Delay between scheduler runs.</param>\n        public DelayedJobScheduler(TimeSpan pollingDelay)\n            : this(pollingDelay, new BackgroundJobStateChanger())\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DelayedJobScheduler\"/>\n        /// class with a specified polling interval and given state changer.\n        /// </summary>\n        /// <param name=\"pollingDelay\">Delay between scheduler runs.</param>\n        /// <param name=\"stateChanger\">State changer to use for background jobs.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"stateChanger\"/> is null.</exception>\n        public DelayedJobScheduler(TimeSpan pollingDelay, [NotNull] IBackgroundJobStateChanger stateChanger)\n        {\n            if (stateChanger == null) throw new ArgumentNullException(nameof(stateChanger));\n\n            _stateChanger = stateChanger;\n            _pollingDelay = pollingDelay;\n            _profiler = new SlowLogProfiler(_logger);\n        }\n\n        /// <summary>\n        /// Gets or sets the maximum degree of parallelism for a scheduler instance.\n        /// When greater than <c>1</c> and batching enabling, delayed jobs will\n        /// be scheduled in parallel under separate connections, increasing the\n        /// throughput.\n        /// </summary>\n        public int MaxDegreeOfParallelism { get; set; }\n\n        /// <summary>\n        /// Gets or sets a task scheduler that will be used when parallel scheduling\n        /// is enabled via the <see cref=\"MaxDegreeOfParallelism\"/> option.\n        /// </summary>\n        public TaskScheduler TaskScheduler { get; set; }\n\n        internal Func<int, TimeSpan> RetryDelayFunc { get; set; } = attempt => TimeSpan.FromSeconds(attempt);\n\n        /// <inheritdoc />\n        public void Execute(BackgroundProcessContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            int jobsProcessed;\n\n            do\n            {\n                jobsProcessed = EnqueueNextScheduledJobs(context);\n\n                if (jobsProcessed != 0)\n                {\n                    _logger.Debug($\"{jobsProcessed} scheduled job(s) processed by scheduler.\");\n                }\n            } while (jobsProcessed > 0 && !context.IsStopping);\n\n            context.Wait(_pollingDelay);\n        }\n\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return GetType().Name;\n        }\n\n        private int EnqueueNextScheduledJobs(BackgroundProcessContext context)\n        {\n            return UseConnectionDistributedLock(context.Storage, connection =>\n            {\n                var jobsProcessed = 0;\n\n                var now = !context.Storage.HasFeature(JobStorageFeatures.Connection.GetUtcDateTime)\n                    ? DateTime.UtcNow\n                    : ((JobStorageConnection)connection).GetUtcDateTime();\n\n                if (IsBatchingAvailable(context.Storage, connection))\n                {\n                    var timestamp = JobHelper.ToTimestamp(now);\n                    var entries = ((JobStorageConnection)connection).GetFirstByLowestScoreFromSet(\"schedule\", 0, timestamp, BatchSize);\n                    var toBeTransactionallyEnqueued = new List<Tuple<string, int>>();\n                    var toBeSequentiallyEnqueued = new List<string>();\n\n                    if (entries != null)\n                    {\n                        foreach (var entry in entries)\n                        {\n                            if (context.IsStopping) break;\n\n                            var colonIndex = entry.IndexOf(':');\n\n                            if (colonIndex < 0) toBeSequentiallyEnqueued.Add(entry);\n                            else toBeTransactionallyEnqueued.Add(Tuple.Create(entry, colonIndex));\n\n                            jobsProcessed++;\n                        }\n\n#if !NETSTANDARD1_3\n                        if (MaxDegreeOfParallelism > 1)\n                        {\n                            Parallel.ForEach(\n                                toBeSequentiallyEnqueued,\n                                new ParallelOptions\n                                {\n                                    MaxDegreeOfParallelism = MaxDegreeOfParallelism,\n                                    CancellationToken = context.StoppingToken,\n                                    TaskScheduler = TaskScheduler\n                                },\n                                (jobId, state) =>\n                                {\n                                    using (var dedicated = context.Storage.GetConnection())\n                                    {\n                                        EnqueueBackgroundJob(context, dedicated, jobId);\n                                    }\n                                });\n                        }\n                        else\n#endif\n                        {\n                            foreach (var jobId in toBeSequentiallyEnqueued)\n                            {\n                                EnqueueBackgroundJob(context, connection, jobId);\n                            }\n                        }\n\n                        if (toBeTransactionallyEnqueued.Count > 0)\n                        {\n                            using (var transaction = connection.CreateWriteTransaction())\n                            {\n                                foreach (var tuple in toBeTransactionallyEnqueued)\n                                {\n                                    EnqueueEntry(tuple.Item1, tuple.Item2, transaction);\n                                }\n\n                                transaction.Commit();\n                            }\n                        }\n                    }\n                }\n                else\n                {\n                    if (MaxDegreeOfParallelism > 1 && !_parallelismIssueLogged)\n                    {\n                        _logger.Warn(\"Parallel execution is configured but can't be used, because current storage implementation doesn't support batching.\");\n                        _parallelismIssueLogged = true;\n                    }\n\n                    for (var i = 0; i < BatchSize; i++)\n                    {\n                        if (context.IsStopping) break;\n\n                        var timestamp = JobHelper.ToTimestamp(now);\n\n                        var entry = connection.GetFirstByLowestScoreFromSet(\"schedule\", 0, timestamp);\n                        if (entry == null) break;\n\n                        var colonIndex = entry.IndexOf(':');\n\n                        if (colonIndex < 0)\n                        {\n                            EnqueueBackgroundJob(context, connection, entry);\n                        }\n                        else\n                        {\n                            using (var transaction = connection.CreateWriteTransaction())\n                            {\n                                EnqueueEntry(entry, colonIndex, transaction);\n                                transaction.Commit();\n                            }\n                        }\n\n                        jobsProcessed++;\n                    }\n                }\n\n                return jobsProcessed;\n            });\n        }\n\n        private static void EnqueueEntry(string entry, int colonIndex, IWriteOnlyTransaction transaction)\n        {\n            if (colonIndex < 0) throw new ArgumentOutOfRangeException(nameof(colonIndex));\n\n            var queue = entry.Substring(0, colonIndex);\n            var jobId = entry.Substring(colonIndex + 1);\n\n            transaction.RemoveFromSet(\"schedule\", entry);\n            transaction.AddToQueue(queue, jobId);\n        }\n\n        private void EnqueueBackgroundJob(BackgroundProcessContext context, IStorageConnection connection, string jobId)\n        {\n            Exception exception = null;\n\n            // At least one retry attempt should always be performed.\n            var maxRetryAttempts = MaxStateChangeAttempts > 0 ? MaxStateChangeAttempts : 1;\n\n            for (var retryAttempt = 0; retryAttempt < maxRetryAttempts; retryAttempt++)\n            {\n                try\n                {\n                    var appliedState = _stateChanger.ChangeState(new StateChangeContext(\n                        context.Storage,\n                        connection,\n                        jobId,\n                        new EnqueuedState { Reason = $\"Triggered by {ToString()}\" },\n                        new [] { ScheduledState.StateName },\n                        disableFilters: false,\n                        context.StoppingToken,\n                        _profiler,\n                        context.ServerId));\n\n                    if (appliedState == null)\n                    {\n                        _logger.Debug($\"Failed to change state of a scheduled background job '{jobId}'\");\n\n                        // When a background job with the given id does not exist, or its state\n                        // does not equal to the Scheduled one, we should remove its id manually\n                        // to avoid poisoned schedule and be able to process other scheduled jobs.\n                        // This might happen when someone modifies the storage bypassing Hangfire API.\n                        using (connection.AcquireDistributedJobLock(jobId, TimeSpan.FromSeconds(5)))\n                        {\n                            var jobData = connection.GetJobData(jobId);\n                            if (jobData == null || !ScheduledState.StateName.Equals(jobData.State, StringComparison.OrdinalIgnoreCase))\n                            {\n                                using (var transaction = connection.CreateWriteTransaction())\n                                {\n                                    transaction.RemoveFromSet(\"schedule\", jobId);\n                                    transaction.Commit();\n                                }\n\n                                _logger.Warn($\"Background job '{jobId}' removed from the schedule, because it's expired or its state was changed\");\n                            }\n                        }\n                    }\n\n                    return;\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    _logger.DebugException(\n                        $\"State change attempt {retryAttempt + 1} of {MaxStateChangeAttempts} failed due to an error, see inner exception for details\", \n                        ex);\n                    \n                    exception = ex;\n                }\n\n                context.Wait(RetryDelayFunc(retryAttempt));\n            }\n\n            _logger.ErrorException(\n                $\"{MaxStateChangeAttempts} state change attempt(s) failed due to an exception, moving job to the FailedState\",\n                exception);\n            \n            // When exception occurs, it's essential to remove a background job identifier from the schedule,\n            // because otherwise delayed job scheduler will fetch such a failing job identifier again and again\n            // and will be unable to make any progress. Any successful state change will cause that identifier\n            // to be removed from the schedule.\n            _stateChanger.ChangeState(new StateChangeContext(\n                context.Storage,\n                connection,\n                jobId,\n                new FailedState(exception, context.ServerId)\n                {\n                    Reason = $\"Failed to change state to the '{EnqueuedState.StateName}' one due to an exception after {MaxStateChangeAttempts} retry attempts\"\n                },\n                new[] { ScheduledState.StateName },\n                disableFilters: true,\n                context.StoppingToken,\n                _profiler,\n                context.ServerId));\n        }\n\n        // TODO Use new HasFeature method if available to avoid exceptions\n        private bool IsBatchingAvailable(JobStorage storage, IStorageConnection connection)\n        {\n            if (storage.HasFeature(JobStorageFeatures.Connection.BatchedGetFirstByLowest) ||\n                storage.HasFeature(\"BatchedGetFirstByLowestScoreFromSet\")) // FROM RCs\n            {\n                return true;\n            }\n\n            return _isBatchingAvailableCache.GetOrAdd(\n                connection.GetType(),\n                type =>\n                {\n                    if (connection is JobStorageConnection storageConnection)\n                    {\n                        try\n                        {\n                            storageConnection.GetFirstByLowestScoreFromSet(null, 0, 0, 1);\n                        }\n                        catch (ArgumentNullException ex) when (ex.ParamName == \"key\")\n                        {\n                            return true;\n                        }\n                        catch (Exception ex) when (ex.IsCatchableExceptionType())\n                        {\n                            //\n                        }\n                    }\n\n                    return false;\n                });\n        }\n\n        private T UseConnectionDistributedLock<T>(JobStorage storage, Func<IStorageConnection, T> action)\n        {\n            var resource = \"locks:schedulepoller\";\n            try\n            {\n                using (var connection = storage.GetConnection())\n                using (connection.AcquireDistributedLock(resource, DefaultLockTimeout))\n                {\n                    return action(connection);\n                }\n            }\n            catch (DistributedLockTimeoutException e) when (e.Resource.EndsWith(resource, StringComparison.Ordinal))\n            {\n                // DistributedLockTimeoutException here doesn't mean that delayed jobs weren't enqueued.\n                // It just means another Hangfire server did this work.\n                _logger.DebugException(\n                    $@\"An exception was thrown during acquiring distributed lock on the {resource} resource within {DefaultLockTimeout.TotalSeconds} seconds. The scheduled jobs have not been handled this time.\nIt will be retried in {_pollingDelay.TotalSeconds} seconds\", \n                    e);\n                return default(T);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/IBackgroundJobPerformer.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Server\n{\n    public interface IBackgroundJobPerformer\n    {\n        object Perform(PerformContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/IBackgroundProcess.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\n\n#pragma warning disable 618 // Obsolete member\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Provides methods for defining processes that will be executed in a\n    /// background thread by <see cref=\"BackgroundProcessingServer\"/>.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// Needs a wait.\n    /// Cancellation token\n    /// Connection disposal\n    /// </remarks>\n    /// \n    /// <seealso cref=\"BackgroundProcessingServer\"/>\n    public interface IBackgroundProcess : IServerProcess\n    {\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"context\">Context for a background process.</param>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"context\"/> is null.</exception>\n        void Execute([NotNull] BackgroundProcessContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/IBackgroundProcessAsync.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Server\n{\n    public interface IBackgroundProcessAsync\n    {\n        Task ExecuteAsync([NotNull] BackgroundProcessContext context);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/IBackgroundProcessDispatcherBuilder.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing Hangfire.Annotations;\nusing Hangfire.Processing;\n\nnamespace Hangfire.Server\n{\n    public interface IBackgroundProcessDispatcherBuilder\n    {\n        IBackgroundDispatcher Create([NotNull] BackgroundServerContext context, [NotNull] BackgroundProcessingServerOptions options);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/IBackgroundProcessingServer.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Hangfire.Server\n{\n    public interface IBackgroundProcessingServer : IDisposable\n    {\n        void SendStop();\n\n        bool WaitForShutdown(TimeSpan timeout);\n        Task WaitForShutdownAsync(CancellationToken cancellationToken);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/IBackgroundServerProcess.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading;\nusing Hangfire.Processing;\n\nnamespace Hangfire.Server\n{\n    internal interface IBackgroundServerProcess\n    {\n        void Execute(\n            Guid executionId, \n            BackgroundExecution execution, \n            CancellationToken stoppingToken, \n            CancellationToken stoppedToken,\n            CancellationToken shutdownToken);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/IServerExceptionFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Defines methods that are required for the server exception filter.\n    /// </summary>\n    public interface IServerExceptionFilter\n    {\n        /// <summary>\n        /// Called when an exception occurred during the performance of the job.\n        /// </summary>\n        /// <param name=\"filterContext\">The filter context.</param>\n        void OnServerException(ServerExceptionContext filterContext);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/IServerFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Defines methods that are required for a server filter.\n    /// </summary>\n    public interface IServerFilter\n    {\n        /// <summary>\n        /// Called before the performance of the job.\n        /// </summary>\n        /// <param name=\"context\">The filter context.</param>\n        void OnPerforming(PerformingContext context);\n\n        /// <summary>\n        /// Called after the performance of the job.\n        /// </summary>\n        /// <param name=\"context\">The filter context.</param>\n        void OnPerformed(PerformedContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/JobAbortedException.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Runtime.Serialization;\n\nnamespace Hangfire.Server\n{\n#if !NETSTANDARD1_3\n    [Serializable]\n#endif\n    public class JobAbortedException : OperationCanceledException\n    {\n        public JobAbortedException()\n        {\n        }\n        \n#if !NETSTANDARD1_3\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"JobAbortedException\"/> class\n        /// with serialized data.\n        /// </summary>\n        /// <param name=\"info\">The <see cref=\"SerializationInfo\"/> that holds the serialized object data about the exception being thrown.</param>\n        /// <param name=\"context\">The <see cref=\"StreamingContext\"/> that contains contextual information about the source or destination.</param>\n        protected JobAbortedException(SerializationInfo info, StreamingContext context)\n            : base(info, context)\n        {\n        }\n#endif\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/JobPerformanceException.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Runtime.Serialization;\n\nnamespace Hangfire.Server\n{\n#if !NETSTANDARD1_3\n    [Serializable]\n#endif\n    public class JobPerformanceException : Exception\n    {\n        public JobPerformanceException(string message, Exception innerException)\n            : this(message, innerException, null)\n        {\n        }\n    \n        public JobPerformanceException(string message, Exception innerException, string jobId)\n            : base(message, innerException)\n        {\n            JobId = jobId;\n        }\n\n#if !NETSTANDARD1_3\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"JobPerformanceException\"/> class\n        /// with serialized data.\n        /// </summary>\n        /// <param name=\"info\">The <see cref=\"SerializationInfo\"/> that holds the serialized object data about the exception being thrown.</param>\n        /// <param name=\"context\">The <see cref=\"StreamingContext\"/> that contains contextual information about the source or destination.</param>\n        protected JobPerformanceException(SerializationInfo info, StreamingContext context)\n            : base(info, context)\n        {\n        }\n#endif\n\n        /// <summary>\n        /// The Background Job Id of the Job instance this exception has been raised for\n        /// </summary>\n        public string JobId { get; private set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/PerformContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Profiling;\nusing Hangfire.Storage;\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Provides information about the context in which the job\n    /// is performed.\n    /// </summary>\n    public class PerformContext\n    {\n        public PerformContext([NotNull] PerformContext context)\n            : this(context.Storage, context.Connection, context.BackgroundJob, context.CancellationToken, context.Profiler, context.ServerId, context.Items)\n        {\n            Performer = context.Performer;\n        }\n\n        [Obsolete(\"Please use PerformContext(JobStorage, IStorageConnection, BackgroundJob, IJobCancellationToken) overload instead. Will be removed in 2.0.0.\")]\n        public PerformContext(\n            [NotNull] IStorageConnection connection,\n            [NotNull] BackgroundJob backgroundJob,\n            [NotNull] IJobCancellationToken cancellationToken)\n            : this(null, connection, backgroundJob, cancellationToken)\n        {\n        }\n\n        public PerformContext(\n            [CanBeNull] JobStorage storage,\n            [NotNull] IStorageConnection connection, \n            [NotNull] BackgroundJob backgroundJob,\n            [NotNull] IJobCancellationToken cancellationToken)\n            : this(storage, connection, backgroundJob, cancellationToken, EmptyProfiler.Instance, null, null)\n        {\n        }\n\n        internal PerformContext(\n            [CanBeNull] JobStorage storage,\n            [NotNull] IStorageConnection connection, \n            [NotNull] BackgroundJob backgroundJob,\n            [NotNull] IJobCancellationToken cancellationToken,\n            [NotNull] IProfiler profiler,\n            [CanBeNull] string serverId,\n            [CanBeNull] IDictionary<string, object> items)\n        {\n            Storage = storage;\n            Connection = connection ?? throw new ArgumentNullException(nameof(connection));\n            BackgroundJob = backgroundJob ?? throw new ArgumentNullException(nameof(backgroundJob));\n            CancellationToken = cancellationToken ?? throw new ArgumentNullException(nameof(cancellationToken));\n            Profiler = profiler ?? throw new ArgumentNullException(nameof(profiler));\n            ServerId = serverId;\n\n            Items = items ?? new Dictionary<string, object>();\n        }\n\n        [CanBeNull]\n        public JobStorage Storage { get; }\n\n        /// <summary>\n        /// Gets an instance of the key-value storage. You can use it\n        /// to pass additional information between different client filters\n        /// or just between different methods.\n        /// </summary>\n        [NotNull]\n        public IDictionary<string, object> Items { get; }\n\n        [NotNull]\n        public BackgroundJob BackgroundJob { get; }\n\n        [Obsolete(\"Please use BackgroundJob property instead. Will be removed in 2.0.0.\")]\n        public string JobId => BackgroundJob.Id;\n\n        [Obsolete(\"Please use BackgroundJob property instead. Will be removed in 2.0.0.\")]\n        public Job Job => BackgroundJob.Job;\n\n        [Obsolete(\"Please use BackgroundJob property instead. Will be removed in 2.0.0.\")]\n        public DateTime CreatedAt => BackgroundJob.CreatedAt;\n\n        [NotNull]\n        public IJobCancellationToken CancellationToken { get; }\n\n        [NotNull]\n        public IStorageConnection Connection { get; }\n        \n        [NotNull]\n        internal IProfiler Profiler { get; }\n        \n        [CanBeNull]\n        public IBackgroundJobPerformer Performer { get; internal set; }\n\n        [CanBeNull]\n        public string ServerId { get; }\n\n        public void SetJobParameter([NotNull] string name, object value)\n        {\n            if (String.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));\n\n            Connection.SetJobParameter(BackgroundJob.Id, name, SerializationHelper.Serialize(value, SerializationOption.User));\n        }\n\n        public T GetJobParameter<T>([NotNull] string name) => GetJobParameter<T>(name, allowStale: false);\n\n        public T GetJobParameter<T>([NotNull] string name, bool allowStale)\n        {\n            if (String.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));\n\n            try\n            {\n                string value;\n\n                if (allowStale && BackgroundJob.ParametersSnapshot != null)\n                {\n                    BackgroundJob.ParametersSnapshot.TryGetValue(name, out value);\n                }\n                else\n                {\n                    value = Connection.GetJobParameter(BackgroundJob.Id, name);                \n                }\n\n                return SerializationHelper.Deserialize<T>(value, SerializationOption.User);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new InvalidOperationException(\n                    $\"Could not get a value of the job parameter `{name}`. See inner exception for details.\", ex);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/PerformedContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Provides the context for the <see cref=\"IServerFilter.OnPerformed\"/>\n    /// method of the <see cref=\"IServerFilter\"/> interface.\n    /// </summary>\n    public class PerformedContext : PerformContext\n    {\n        public PerformedContext(\n            [NotNull] PerformContext context,\n            [CanBeNull]object result,\n            bool canceled,\n            [CanBeNull] Exception exception)\n            : base(context)\n        {\n            Result = result;\n            Canceled = canceled;\n            Exception = exception;\n        }\n\n        /// <summary>\n        /// Gets a value that was returned by the job.\n        /// </summary>\n        [CanBeNull]\n        public object Result { get; }\n\n        /// <summary>\n        /// Gets a value that indicates that this <see cref=\"PerformedContext\"/>\n        /// object was canceled.\n        /// </summary>\n        public bool Canceled { get; }\n\n        /// <summary>\n        /// Gets an exception that occurred during the performance of the job.\n        /// </summary>\n        [CanBeNull]\n        public Exception Exception { get; }\n\n        /// <summary>\n        /// Gets or sets a value that indicates that this <see cref=\"PerformedContext\"/>\n        /// object handles an exception occurred during the performance of the job.\n        /// </summary>\n        public bool ExceptionHandled { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/PerformingContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Provides the context for the <see cref=\"IServerFilter.OnPerforming\"/>\n    /// method of the <see cref=\"IServerFilter\"/> interface.\n    /// </summary>\n    public class PerformingContext : PerformContext\n    {\n        public PerformingContext(PerformContext context)\n            : base(context)\n        {\n        }\n\n        /// <summary>\n        /// Gets or sets a value that indicates that this <see cref=\"PerformingContext\"/>\n        /// object was canceled.\n        /// </summary>\n        public bool Canceled { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/RecurringJobScheduler.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Profiling;\nusing Hangfire.Storage;\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Represents a background process responsible for <i>enqueueing recurring \n    /// jobs</i>.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// <para>This background process polls the <i>recurring job schedule</i>\n    /// for recurring jobs ready to be enqueued. Interval between scheduler\n    /// polls is hard-coded to <b>1 minute</b> as a compromise between\n    /// frequency and additional stress on job storage.</para>\n    /// \n    /// <note type=\"tip\">\n    /// Use custom background processes if you need to schedule recurring jobs\n    /// with frequency less than one minute. Please see the \n    /// <see cref=\"IBackgroundProcess\"/> interface for details.\n    /// </note>\n    /// \n    /// <para>Recurring job schedule is based on Set and Hash data structures\n    /// of a job storage, so you can use this background process as an example \n    /// of a custom extension.</para>\n    /// \n    /// <para>Multiple instances of this background process can be used in\n    /// separate threads/processes without additional configuration (distributed\n    /// locks are used). However, this only adds support for fail-over, and does \n    /// not increase the performance.</para>\n    /// \n    /// <note type=\"important\">\n    /// If you are using <b>custom filter providers</b>, you need to pass a \n    /// custom <see cref=\"IBackgroundJobFactory\"/> instance to make this \n    /// process respect your filters when enqueueing background jobs.\n    /// </note>\n    /// </remarks>\n    /// \n    /// <threadsafety static=\"true\" instance=\"true\"/>\n    /// \n    /// <seealso cref=\"RecurringJobManager\"/>\n    public class RecurringJobScheduler : IBackgroundProcess\n    {\n        private static readonly TimeSpan LockTimeout = TimeSpan.FromMinutes(1);\n        private static readonly int BatchSize = 1000;\n        private static readonly int MaxRetryAttemptCount = 5;\n        private static readonly int MaxSupportedVersion = 2;\n\n        private readonly ILog _logger = LogProvider.For<RecurringJobScheduler>();\n        private readonly ConcurrentDictionary<Type, bool> _isBatchingAvailableCache = new ConcurrentDictionary<Type, bool>();\n\n        private readonly IBackgroundJobFactory _factory;\n        private readonly Func<DateTime> _nowFactory;\n        private readonly ITimeZoneResolver _timeZoneResolver;\n        private readonly TimeSpan _pollingDelay;\n        private readonly IProfiler _profiler;\n        private bool _parallelismIssueLogged;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RecurringJobScheduler\"/>\n        /// class with default background job factory.\n        /// </summary>\n        public RecurringJobScheduler()\n            : this(new BackgroundJobFactory(JobFilterProviders.Providers))\n        {\n        }\n        \n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RecurringJobScheduler\"/>\n        /// class with custom background job factory and a state machine.\n        /// </summary>\n        /// <param name=\"factory\">Factory that will be used to create background jobs.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"factory\"/> is null.</exception>\n        public RecurringJobScheduler(\n            [NotNull] IBackgroundJobFactory factory)\n            : this(factory, TimeSpan.Zero)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RecurringJobScheduler\"/> class\n        /// with custom background job factory, state machine and clocks.\n        /// </summary>\n        /// <param name=\"factory\">Factory that will be used to create background jobs.</param>\n        /// <param name=\"pollingDelay\">Delay before another polling attempt, when no jobs scheduled yet.</param>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"factory\"/> is null.</exception>\n        public RecurringJobScheduler(\n            [NotNull] IBackgroundJobFactory factory,\n            TimeSpan pollingDelay)\n            : this(factory, pollingDelay, new DefaultTimeZoneResolver())\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RecurringJobScheduler\"/> class\n        /// with custom background job factory, state machine and clocks.\n        /// </summary>\n        /// <param name=\"factory\">Factory that will be used to create background jobs.</param>\n        /// <param name=\"pollingDelay\">Delay before another polling attempt, when no jobs scheduled yet.</param>\n        /// <param name=\"timeZoneResolver\">Function that returns a time zone object by its identifier.</param>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"factory\"/> is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"timeZoneResolver\"/> is null.</exception>\n        public RecurringJobScheduler(\n            [NotNull] IBackgroundJobFactory factory,\n            TimeSpan pollingDelay,\n            [NotNull] ITimeZoneResolver timeZoneResolver)\n            : this(factory, pollingDelay, timeZoneResolver, static () => DateTime.UtcNow)\n        {\n        }\n\n        public RecurringJobScheduler(\n            [NotNull] IBackgroundJobFactory factory,\n            TimeSpan pollingDelay,\n            [NotNull] ITimeZoneResolver timeZoneResolver,\n            [NotNull] Func<DateTime> nowFactory)\n        {\n            if (factory == null) throw new ArgumentNullException(nameof(factory));\n            if (nowFactory == null) throw new ArgumentNullException(nameof(nowFactory));\n            if (timeZoneResolver == null) throw new ArgumentNullException(nameof(timeZoneResolver));\n\n            _factory = factory;\n            _nowFactory = nowFactory;\n            _timeZoneResolver = timeZoneResolver;\n            _pollingDelay = pollingDelay;\n            _profiler = new SlowLogProfiler(_logger);\n        }\n\n        /// <summary>\n        /// Gets or sets the maximum degree of parallelism for a scheduler instance.\n        /// When greater than <c>1</c> and batching enabling, recurring jobs will\n        /// be scheduled in parallel under separate connections, increasing the\n        /// throughput.\n        /// </summary>\n        public int MaxDegreeOfParallelism { get; set; }\n\n        /// <summary>\n        /// Gets or sets a task scheduler that will be used when parallel scheduling\n        /// is enabled via the <see cref=\"MaxDegreeOfParallelism\"/> option.\n        /// </summary>\n        public TaskScheduler TaskScheduler { get; set; }\n\n        /// <inheritdoc />\n        public void Execute(BackgroundProcessContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            int jobsProcessed;\n\n            do\n            {\n                jobsProcessed = EnqueueNextRecurringJobs(context);\n\n                if (jobsProcessed != 0)\n                {\n                    _logger.Debug($\"{jobsProcessed} recurring job(s) processed by scheduler.\");\n                }\n            } while (jobsProcessed > 0 && !context.IsStopping);\n\n            if (_pollingDelay > TimeSpan.Zero)\n            {\n                context.Wait(_pollingDelay);\n            }\n            else\n            {\n                var now = _nowFactory();\n                context.Wait(now.AddMilliseconds(-now.Millisecond).AddSeconds(-now.Second).AddMinutes(1) - now);\n            }\n        }\n\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return GetType().Name;\n        }\n\n        private int EnqueueNextRecurringJobs(BackgroundProcessContext context)\n        {\n            return UseConnectionDistributedLock(context.Storage, connection =>\n            {\n                var jobsProcessed = 0;\n\n                var now = DateTime.SpecifyKind(\n                    !context.Storage.HasFeature(JobStorageFeatures.Connection.GetUtcDateTime)\n                        ? _nowFactory()\n                        : ((JobStorageConnection)connection).GetUtcDateTime(),\n                    DateTimeKind.Utc);\n\n                if (IsBatchingAvailable(context.Storage, connection))\n                {\n                    var timestamp = JobHelper.ToTimestamp(now);\n                    var recurringJobIds = ((JobStorageConnection)connection).GetFirstByLowestScoreFromSet(\"recurring-jobs\", 0, timestamp, BatchSize);\n\n                    if (recurringJobIds != null)\n                    {\n#if !NETSTANDARD1_3\n                        if (MaxDegreeOfParallelism > 1)\n                        {\n                            Parallel.ForEach(\n                                recurringJobIds,\n                                new ParallelOptions\n                                {\n                                    MaxDegreeOfParallelism = MaxDegreeOfParallelism,\n                                    CancellationToken = context.StoppingToken,\n                                    TaskScheduler = TaskScheduler\n                                },\n                                (recurringJobId, state) =>\n                                {\n                                    using (var dedicated = context.Storage.GetConnection())\n                                    {\n                                        TryEnqueueBackgroundJob(context, dedicated, recurringJobId, now);\n                                    }\n\n                                    Interlocked.Increment(ref jobsProcessed);\n                                });\n                        }\n                        else\n#endif\n                        {\n                            foreach (var recurringJobId in recurringJobIds)\n                            {\n                                if (context.IsStopping) break;\n\n                                TryEnqueueBackgroundJob(context, connection, recurringJobId, now);\n                                jobsProcessed++;\n                            }\n                        }\n                    }\n                }\n                else\n                {\n                    if (MaxDegreeOfParallelism > 1 && !_parallelismIssueLogged)\n                    {\n                        _logger.Warn(\"Parallel execution is configured but can't be used, because current storage implementation doesn't support batching.\");\n                        _parallelismIssueLogged = true;\n                    }\n\n                    for (var i = 0; i < BatchSize; i++)\n                    {\n                        if (context.IsStopping) break;\n\n                        var timestamp = JobHelper.ToTimestamp(now);\n\n                        var recurringJobId = connection.GetFirstByLowestScoreFromSet(\"recurring-jobs\", 0, timestamp);\n                        if (recurringJobId == null) break;\n\n                        TryEnqueueBackgroundJob(context, connection, recurringJobId, now);\n                        jobsProcessed++;\n                    }\n                }\n\n                return jobsProcessed;\n            });\n        }\n\n        private void TryEnqueueBackgroundJob(\n            BackgroundProcessContext context,\n            IStorageConnection connection,\n            string recurringJobId,\n            DateTime now)\n        {\n            using (connection.AcquireDistributedRecurringJobLock(recurringJobId, LockTimeout))\n            {\n                var recurringJob = connection.GetRecurringJob(recurringJobId);\n\n                if (recurringJob == null)\n                {\n                    RemoveRecurringJob(connection, recurringJobId);\n                    return;\n                }\n\n                ScheduleRecurringJob(context, connection, recurringJobId, recurringJob, now);\n            }\n        }\n\n        private void ScheduleRecurringJob(BackgroundProcessContext context, IStorageConnection connection,\n            string recurringJobId, RecurringJobEntity recurringJob, DateTime now)\n        {\n            // We always start a transaction, regardless our recurring job was updated or not,\n            // to prevent from infinite loop, when there's an old processing server (pre-1.7.0)\n            // in our environment that doesn't know it should modify the score for entries in\n            // the recurring jobs set.\n            using (var transaction = connection.CreateWriteTransaction())\n            {\n                Exception exception = null;\n                \n                try\n                {\n                    // We can't handle recurring job with unsupported versions - there may be additional\n                    // features. we don't know about. We also shouldn't stop the whole scheduler as\n                    // there may be jobs with lower versions. Instead, we'll re-schedule such a job and\n                    // emit a warning message to the log.\n                    if (recurringJob.Version.HasValue && recurringJob.Version > MaxSupportedVersion)\n                    {\n                        throw new NotSupportedException($\"Server '{context.ServerId}' can't process recurring job '{recurringJobId}' of version '{recurringJob.Version ?? 1}'. Max supported version of this server is '{MaxSupportedVersion}'.\");\n                    }\n                    \n                    var backgroundJobs = new List<BackgroundJob>();\n                    var precision = _pollingDelay + _pollingDelay;\n\n                    var executions = recurringJob.ScheduleNext(\n                        _timeZoneResolver,\n                        recurringJob.LastExecution ?? recurringJob.CreatedAt?.AddSeconds(-1) ?? now.AddSeconds(-1),\n                        now,\n                        precision);\n\n                    foreach (var execution in executions)\n                    {\n                        var backgroundJob = _factory.TriggerRecurringJob(\n                            context.Storage,\n                            connection,\n                            _profiler,\n                            recurringJob,\n                            execution);\n\n                        if (!String.IsNullOrEmpty(backgroundJob?.Id))\n                        {\n                            backgroundJobs.Add(backgroundJob);\n                        }\n                        else\n                        {\n                            _logger.Debug($\"Recurring job '{recurringJobId}' execution at '{execution}' has been canceled.\");\n                        }\n                    }\n\n                    foreach (var backgroundJob in backgroundJobs)\n                    {\n                        _factory.StateMachine.EnqueueBackgroundJob(\n                            context.Storage,\n                            connection,\n                            transaction,\n                            recurringJob,\n                            backgroundJob,\n                            \"Triggered by recurring job scheduler\",\n                            _profiler);\n                    }\n\n                    recurringJob.RetryAttempt = 0;\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    exception = ex;\n                }\n\n                if (exception != null)\n                {\n                    RetryRecurringJob(recurringJobId, recurringJob, now, exception);\n                }\n\n                recurringJob.IsChanged(now, out var changedFields);\n                transaction.UpdateRecurringJob(recurringJob, changedFields, _logger);\n\n                // We should commit transaction outside of the internal try/catch block, because these\n                // exceptions are always due to network issues, and in case of a timeout exception we\n                // can't determine whether it was actually succeeded or not, and can't perform an\n                // idempotent retry in this case.\n                transaction.Commit();\n            }\n        }\n\n        private void RetryRecurringJob(string recurringJobId, RecurringJobEntity recurringJob, DateTime now, Exception error)\n        {\n            var errorString = error.ToStringWithOriginalStackTrace(States.FailedState.MaxLinesInExceptionDetails, includeFileInfo: false);\n\n            if (recurringJob.RetryAttempt < MaxRetryAttemptCount)\n            {\n                var delay = _pollingDelay > TimeSpan.Zero ? _pollingDelay : TimeSpan.FromMinutes(1);\n\n                _logger.WarnException(\n                    $\"Recurring job '{recurringJobId}' can't be scheduled due to an error and will be retried in {delay}.\",\n                    error);\n                recurringJob.ScheduleRetry(now.Add(delay), errorString);\n            }\n            else\n            {\n                _logger.ErrorException(\n                    $\"Recurring job '{recurringJobId}' can't be scheduled due to an error and will be disabled.\", error);\n                recurringJob.Disable(errorString);\n            }\n        }\n\n        private void RemoveRecurringJob(IStorageConnection connection, string recurringJobId)\n        {\n            _logger.Debug($\"Recurring job '{recurringJobId}' doesn't exist and will be removed from schedule.\");\n\n            using (var transaction = connection.CreateWriteTransaction())\n            {\n                transaction.RemoveFromSet(\"recurring-jobs\", recurringJobId);\n                transaction.Commit();\n            }\n        }\n\n        private T UseConnectionDistributedLock<T>(JobStorage storage, Func<IStorageConnection, T> action)\n        {\n            var resource = \"recurring-jobs:lock\";\n            try\n            {\n                using (var connection = storage.GetConnection())\n                using (connection.AcquireDistributedLock(resource, LockTimeout))\n                {\n                    return action(connection);\n                }\n            }\n            catch (DistributedLockTimeoutException e) when (e.Resource.EndsWith(resource, StringComparison.Ordinal))\n            {\n                // DistributedLockTimeoutException here doesn't mean that recurring jobs weren't scheduled.\n                // It just means another Hangfire server did this work.\n                _logger.Log(\n                    LogLevel.Debug,\n                    () => $@\"An exception was thrown during acquiring distributed lock the {resource} resource within {LockTimeout.TotalSeconds} seconds. The recurring jobs have not been handled this time.\",\n                    e);\n            }\n\n            return default;\n        }\n\n        private bool IsBatchingAvailable(JobStorage storage, IStorageConnection connection)\n        {\n            if (storage.HasFeature(JobStorageFeatures.Connection.BatchedGetFirstByLowest) ||\n                storage.HasFeature(\"BatchedGetFirstByLowestScoreFromSet\")) // FROM RCs\n            {\n                return true;\n            }\n\n            return _isBatchingAvailableCache.GetOrAdd(\n                connection.GetType(),\n                type =>\n                {\n                    if (connection is JobStorageConnection storageConnection)\n                    {\n                        try\n                        {\n                            storageConnection.GetFirstByLowestScoreFromSet(null, 0, 0, 1);\n                        }\n                        catch (ArgumentNullException ex) when (ex.ParamName == \"key\")\n                        {\n                            return true;\n                        }\n                        catch (Exception ex) when (ex.IsCatchableExceptionType())\n                        {\n                            //\n                        }\n                    }\n\n                    return false;\n                });\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/ServerContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Server\n{\n    public class ServerContext\n    {\n        public ServerContext()\n        {\n            Queues = [];\n            WorkerCount = -1;\n        }\n\n        public int WorkerCount { get; set; }\n        public string[] Queues { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/ServerExceptionContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Provides the context for the <see cref=\"IServerExceptionFilter.OnServerException\"/>\n    /// method of the <see cref=\"IServerExceptionFilter\"/> interface.\n    /// </summary>\n    public class ServerExceptionContext : PerformContext\n    {\n        public ServerExceptionContext(\n            PerformContext context, \n            Exception exception)\n            : base(context)\n        {\n            Exception = exception;\n        }\n\n        /// <summary>\n        /// Gets an exception that occurred during the performance of the job.\n        /// </summary>\n        public Exception Exception { get; }\n\n        /// <summary>\n        /// Gets or sets a value that indicates that this <see cref=\"ServerExceptionContext\"/>\n        /// object handles an exception occurred during the performance of the job.\n        /// </summary>\n        public bool ExceptionHandled { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/ServerHeartbeatProcess.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Diagnostics;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Storage;\n\nnamespace Hangfire.Server\n{\n    internal sealed class ServerHeartbeatProcess : IBackgroundProcess\n    {\n        private readonly ILog _logger = LogProvider.GetLogger(typeof(ServerHeartbeatProcess));\n\n        private readonly TimeSpan _interval;\n        private readonly TimeSpan _serverTimeout;\n        private readonly Action _requestRestart;\n        private Stopwatch _faultedSince;\n\n        public ServerHeartbeatProcess(TimeSpan interval, TimeSpan serverTimeout, Action requestRestart)\n        {\n            _interval = interval;\n            _serverTimeout = serverTimeout;\n            _requestRestart = requestRestart;\n        }\n\n        public void Execute(BackgroundProcessContext context)\n        {\n            _logger.Trace($\"{BackgroundServerProcess.GetServerTemplate(context.ServerId)} waiting for {_interval} delay before sending a heartbeat\");\n\n            context.ShutdownToken.WaitOrThrow(_interval);\n\n            try\n            {\n                using (var connection = context.Storage.GetConnection())\n                {\n                    connection.Heartbeat(context.ServerId);\n                }\n\n                if (_faultedSince == null)\n                {\n                    _logger.Debug($\"{BackgroundServerProcess.GetServerTemplate(context.ServerId)} heartbeat successfully sent\");\n                }\n                else\n                {\n                    _logger.Info($\"{BackgroundServerProcess.GetServerTemplate(context.ServerId)} is now able to continue sending heartbeats\");\n                    _faultedSince = null;\n                }\n            }\n            catch (BackgroundServerGoneException)\n            {\n                if (!context.ShutdownToken.IsCancellationRequested)\n                {\n                    _logger.Warn($\"{BackgroundServerProcess.GetServerTemplate(context.ServerId)} was considered dead by other servers, restarting...\");\n                    _requestRestart();\n                }\n\n                return;\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                _logger.WarnException($\"{BackgroundServerProcess.GetServerTemplate(context.ServerId)} encountered an exception while sending heartbeat\", ex);\n\n                if (_faultedSince == null) _faultedSince = Stopwatch.StartNew();\n                if (_faultedSince.Elapsed >= _serverTimeout)\n                {\n                    _logger.Error($\"{BackgroundServerProcess.GetServerTemplate(context.ServerId)} will be restarted due to server time out\");\n\n                    _requestRestart();\n                    return;\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/ServerJobCancellationToken.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Hangfire.Server\n{\n    internal sealed class ServerJobCancellationToken : IJobCancellationToken, IDisposable\n    {\n        private static readonly ConcurrentDictionary<string, ConcurrentDictionary<ServerJobCancellationToken, object>> WatchedServers\n            = new ConcurrentDictionary<string, ConcurrentDictionary<ServerJobCancellationToken, object>>();\n\n        private readonly object _syncRoot = new object();\n        private readonly string _jobId;\n        private readonly string _serverId;\n        private readonly string _workerId;\n        private readonly IStorageConnection _connection;\n        private readonly CancellationToken _shutdownToken;\n        private readonly Lazy<CancellationTokenHolder> _cancellationTokenHolder;\n        private readonly ConcurrentDictionary<ServerJobCancellationToken, object> _watchedTokens;\n        private bool _disposed;\n\n        public ServerJobCancellationToken(\n            [NotNull] IStorageConnection connection,\n            [NotNull] string jobId, \n            [NotNull] string serverId,\n            [NotNull] string workerId,\n            CancellationToken shutdownToken)\n        {\n            _jobId = jobId ?? throw new ArgumentNullException(nameof(jobId));\n            _serverId = serverId ?? throw new ArgumentNullException(nameof(serverId));\n            _workerId = workerId ?? throw new ArgumentNullException(nameof(workerId));\n            _connection = connection ?? throw new ArgumentNullException(nameof(connection));\n\n            _shutdownToken = shutdownToken;\n\n            _cancellationTokenHolder = new Lazy<CancellationTokenHolder>(\n                () => new CancellationTokenHolder(_shutdownToken),\n                LazyThreadSafetyMode.None);\n\n            if (WatchedServers.TryGetValue(_serverId, out _watchedTokens))\n            {\n                _watchedTokens.TryAdd(this, null);\n            }\n        }\n\n        public void Dispose()\n        {\n            lock (_syncRoot)\n            {\n                if (_disposed) return;\n                _disposed = true;\n\n                _watchedTokens?.TryRemove(this, out _);\n\n                if (_cancellationTokenHolder.IsValueCreated)\n                {\n                    _cancellationTokenHolder.Value.Dispose();\n                }\n            }\n        }\n\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"SonarLint\", \"S4275:GettersAndSettersShouldAccessTheExpectedFields\", Justification = \"Bad property naming for backwards compatibility.\")]\n        public CancellationToken ShutdownToken\n        {\n            get\n            {\n                lock (_syncRoot)\n                {\n                    CheckDisposed();\n                    return _cancellationTokenHolder.Value.CancellationToken;\n                }\n            }\n        }\n\n        public bool IsAborted\n        {\n            get\n            {\n                lock (_syncRoot)\n                {\n                    CheckDisposed();\n                    return _cancellationTokenHolder.IsValueCreated && _cancellationTokenHolder.Value.IsAborted;\n                }\n            }\n        }\n\n        public void ThrowIfCancellationRequested()\n        {\n            lock (_syncRoot)\n            {\n                CheckDisposed();\n\n                _shutdownToken.ThrowIfCancellationRequested();\n\n                if (_cancellationTokenHolder.IsValueCreated && _cancellationTokenHolder.Value.IsAborted)\n                {\n                    throw new JobAbortedException();\n                }\n\n                // TODO: Create a new connection instead to avoid possible race conditions due to user code\n                if (CheckJobStateChanged(_connection))\n                {\n                    throw new JobAbortedException();\n                }\n            }\n        }\n\n        public static void AddServer(string serverId)\n        {\n            WatchedServers.TryAdd(serverId, new ConcurrentDictionary<ServerJobCancellationToken, object>());\n        }\n\n        public static void RemoveServer(string serverId)\n        {\n            WatchedServers.TryRemove(serverId, out _);\n        }\n\n        public static IEnumerable<Tuple<string, string>> CheckAllCancellationTokens(\n            string serverId,\n            IStorageConnection connection,\n            CancellationToken cancellationToken)\n        {\n            if (WatchedServers.TryGetValue(serverId, out var watchedTokens))\n            {\n                var result = new List<Tuple<string, string>>();\n\n                foreach (var token in watchedTokens)\n                {\n                    cancellationToken.ThrowIfCancellationRequested();\n\n                    if (token.Key.TryCheckJobIsAborted(connection))\n                    {\n                        result.Add(Tuple.Create(token.Key._jobId, token.Key._workerId));\n                    }\n                }\n\n                return result;\n            }\n\n            return Enumerable.Empty<Tuple<string, string>>();\n        }\n\n        public bool TryCheckJobIsAborted(IStorageConnection connection)\n        {\n            // Returns `true` only when check was performed AND the job is\n            // aborted AND it was not already aborted by calling this method.\n            // When return value is `false`, this means either the check\n            // wasn't performed (because object is disposed, or there's no\n            // associated token holder) or job is still running.\n\n            lock (_syncRoot)\n            {\n                if (_disposed || !_cancellationTokenHolder.IsValueCreated || _cancellationTokenHolder.Value.IsAborted)\n                {\n                    return false;\n                }\n\n                return CheckJobStateChanged(connection);\n            }\n        }\n\n        private bool CheckJobStateChanged(IStorageConnection connection)\n        {\n            if (IsJobStateChanged(connection))\n            {\n                _cancellationTokenHolder.Value.Abort();\n                return true;\n            }\n\n            return false;\n        }\n\n        private bool IsJobStateChanged(IStorageConnection connection)\n        {\n            var state = connection.GetStateData(_jobId);\n\n            if (state == null || !state.Name.Equals(ProcessingState.StateName, StringComparison.OrdinalIgnoreCase))\n            {\n                return true;\n            }\n\n            if (!state.Data.TryGetValue(\"ServerId\", out var serverId) || !serverId.Equals(_serverId, StringComparison.OrdinalIgnoreCase))\n            {\n                return true;\n            }\n\n            if (!state.Data.TryGetValue(\"WorkerId\", out var workerId) || !workerId.Equals(_workerId, StringComparison.OrdinalIgnoreCase))\n            {\n                return true;\n            }\n\n            return false;\n        }\n\n        private void CheckDisposed()\n        {\n            if (_disposed)\n            {\n                throw new ObjectDisposedException(GetType().FullName);\n            }\n        }\n\n        private sealed class CancellationTokenHolder : IDisposable\n        {\n            private readonly CancellationTokenSource _abortedTokenSource;\n            private readonly CancellationTokenSource _linkedTokenSource;\n\n            public CancellationTokenHolder(CancellationToken shutdownToken)\n            {\n                _abortedTokenSource = new CancellationTokenSource();\n                _linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(shutdownToken, _abortedTokenSource.Token);\n            }\n\n            public CancellationToken CancellationToken => _linkedTokenSource.Token;\n\n            public bool IsAborted => _abortedTokenSource.IsCancellationRequested;\n\n            public void Abort()\n            {\n                _abortedTokenSource.Cancel();\n            }\n\n            public void Dispose()\n            {\n                _linkedTokenSource.Dispose();\n                _abortedTokenSource.Dispose();\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/ServerJobCancellationWatcher.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Logging;\n\nnamespace Hangfire.Server\n{\n    internal sealed class ServerJobCancellationWatcher : IBackgroundProcess\n    {\n        public static readonly TimeSpan DefaultCheckInterval = TimeSpan.FromSeconds(5);\n        \n        private readonly ILog _logger = LogProvider.GetLogger(typeof(ServerJobCancellationWatcher));\n        private readonly TimeSpan _checkInterval;\n\n        public ServerJobCancellationWatcher(TimeSpan checkInterval)\n        {\n            _checkInterval = checkInterval;\n        }\n\n        public void Execute(BackgroundProcessContext context)\n        {\n            _logger.Trace(\"Checking for aborted jobs...\");\n\n            using (var connection = context.Storage.GetConnection())\n            {\n                var abortedJobIds = ServerJobCancellationToken.CheckAllCancellationTokens(\n                    context.ServerId,\n                    connection,\n                    context.StoppedToken);\n\n                var aborted = false;\n\n                foreach (var abortedJobId in abortedJobIds)\n                {\n                    _logger.Debug($\"Job {abortedJobId.Item1} was aborted on worker {abortedJobId.Item2}.\");\n                    aborted = true;\n                }\n\n                if (!aborted)\n                {\n                    _logger.Trace(\"No newly aborted jobs found.\");\n                }\n            }\n\n            context.Wait(_checkInterval);\n        }\n\n        public override string ToString()\n        {\n            return GetType().Name;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/ServerProcessDispatcherBuilder.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Processing;\n\n#pragma warning disable 618\n\nnamespace Hangfire.Server\n{\n    internal sealed class ServerProcessDispatcherBuilder : IBackgroundProcessDispatcherBuilder\n    {\n        private readonly IServerComponent _component;\n        private readonly Func<ThreadStart, IEnumerable<Thread>> _threadFactory;\n\n        public ServerProcessDispatcherBuilder(\n            [NotNull] IServerComponent component,\n            [NotNull] Func<ThreadStart, IEnumerable<Thread>> threadFactory)\n        {\n            if (component == null) throw new ArgumentNullException(nameof(component));\n            if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory));\n            _component = component;\n            _threadFactory = threadFactory;\n        }\n\n        public IBackgroundDispatcher Create(BackgroundServerContext context, BackgroundProcessingServerOptions options)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            return new BackgroundDispatcher(\n                new BackgroundExecution(new BackgroundExecutionOptions\n                {\n                    Name = _component.GetType().Name,\n                    RetryDelay = options.RetryDelay\n                }, context.StoppingToken),\n                ExecuteComponent,\n                Tuple.Create(_component, context),\n                _threadFactory);\n        }\n\n        public override string ToString()\n        {\n            return _component.GetType().Name;\n        }\n\n        private static void ExecuteComponent(Guid executionId, object state)\n        {\n            var tuple = (Tuple<IServerComponent, BackgroundServerContext>)state;\n            tuple.Item1.Execute(tuple.Item2.StoppingToken);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Server/ServerWatchdog.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Logging;\n\nnamespace Hangfire.Server\n{\n    internal sealed class ServerWatchdog : IBackgroundProcess\n    {\n        public static readonly TimeSpan DefaultCheckInterval = TimeSpan.FromMinutes(5);\n        public static readonly TimeSpan DefaultServerTimeout = TimeSpan.FromMinutes(5);\n        public static readonly TimeSpan MaxServerTimeout = TimeSpan.FromHours(24);\n        public static readonly TimeSpan MaxServerCheckInterval = TimeSpan.FromHours(24);\n        public static readonly TimeSpan MaxHeartbeatInterval = TimeSpan.FromHours(24);\n\n        private readonly ILog _logger = LogProvider.For<ServerWatchdog>();\n\n        private readonly TimeSpan _checkInterval;\n        private readonly TimeSpan _serverTimeout;\n\n        public ServerWatchdog(TimeSpan checkInterval, TimeSpan serverTimeout)\n        {\n            _checkInterval = checkInterval;\n            _serverTimeout = serverTimeout;\n        }\n\n        public void Execute(BackgroundProcessContext context)\n        {\n            using (var connection = context.Storage.GetConnection())\n            {\n                var serversRemoved = connection.RemoveTimedOutServers(_serverTimeout);\n                if (serversRemoved != 0)\n                {\n                    _logger.Info($\"{serversRemoved} servers were removed due to timeout\");\n                }\n            }\n\n            context.Wait(_checkInterval);\n        }\n\n        public override string ToString()\n        {\n            return GetType().Name;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Server/Worker.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Profiling;\nusing Hangfire.States;\nusing Hangfire.Storage;\n\nnamespace Hangfire.Server\n{\n    /// <summary>\n    /// Represents a background process responsible for <i>processing \n    /// fire-and-forget jobs</i>.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// <para>This is the heart of background processing in Hangfire</para>\n    /// </remarks>\n    /// \n    /// <threadsafety static=\"true\" instance=\"true\"/>\n    /// \n    /// <seealso cref=\"EnqueuedState\"/>\n    public class Worker : IBackgroundProcess\n    {\n        [Obsolete(\"Please use JobStorageFeatures.StorageTransactionalAcknowledge instead.\")]\n        public static readonly string TransactionalAcknowledgePrefix = JobStorageFeatures.TransactionalAcknowledgePrefix;\n\n        private static readonly ConcurrentDictionary<Guid, string> WorkerGuidCache = new();\n        private static readonly string[] EligibleWorkerStates = new[] { EnqueuedState.StateName, ScheduledState.StateName, ProcessingState.StateName };\n        private static readonly string[] ProcessingStateArray = new[] { ProcessingState.StateName };\n\n        private readonly TimeSpan _jobInitializationWaitTimeout;\n        private readonly int _maxStateChangeAttempts;\n\n        private readonly ILog _logger = LogProvider.For<Worker>();\n\n        private readonly IEnumerable<string> _queues;\n\n        private readonly IBackgroundJobPerformer _performer;\n        private readonly IBackgroundJobStateChanger _stateChanger;\n        private readonly IProfiler _profiler;\n        \n        public Worker() : this(EnqueuedState.DefaultQueue)\n        {\n        }\n\n        public Worker([NotNull] params string[] queues)\n            : this(queues, new BackgroundJobPerformer(), new BackgroundJobStateChanger())\n        {\n        }\n\n        public Worker(\n            [NotNull] IEnumerable<string> queues,\n            [NotNull] IBackgroundJobPerformer performer,\n            [NotNull] IBackgroundJobStateChanger stateChanger)\n            : this(queues, performer, stateChanger, jobInitializationTimeout: TimeSpan.FromMinutes(1), maxStateChangeAttempts: 10)\n        {\n        }\n\n        internal Worker(\n            [NotNull] IEnumerable<string> queues,\n            [NotNull] IBackgroundJobPerformer performer, \n            [NotNull] IBackgroundJobStateChanger stateChanger,\n            TimeSpan jobInitializationTimeout,\n            int maxStateChangeAttempts)\n        {\n            if (queues == null) throw new ArgumentNullException(nameof(queues));\n            if (performer == null) throw new ArgumentNullException(nameof(performer));\n            if (stateChanger == null) throw new ArgumentNullException(nameof(stateChanger));\n            \n            _queues = queues;\n            _performer = performer;\n            _stateChanger = stateChanger;\n\n            _jobInitializationWaitTimeout = jobInitializationTimeout;\n            _maxStateChangeAttempts = maxStateChangeAttempts;\n\n            _profiler = new SlowLogProfiler(_logger);\n        }\n\n        /// <inheritdoc />\n        public void Execute(BackgroundProcessContext context)\n        {\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            using (var connection = context.Storage.GetConnection())\n            using (var fetchedJob = connection.FetchNextJob(_queues.ToArray(), context.StoppingToken))\n            {\n                var requeueOnException = true;\n\n                try\n                {\n                    BackgroundJob backgroundJob = null;\n\n                    using (var timeoutCts = new CancellationTokenSource(_jobInitializationWaitTimeout))\n                    using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(\n                        context.StoppingToken,\n                        timeoutCts.Token))\n                    {\n                        var processingState = new ProcessingState(context.ServerId, WorkerGuidCache.GetOrAdd(context.ExecutionId, static guid => guid.ToString()));\n\n                        var appliedState = TryChangeState(\n                            context, \n                            connection, \n                            fetchedJob.JobId, \n                            processingState, \n                            null,\n                            EligibleWorkerStates,\n                            null,\n                            out backgroundJob,\n                            linkedCts.Token,\n                            context.StoppingToken);\n\n                        // Cancel job processing if the job could not be loaded, was not in the initial state expected\n                        // or if a job filter changed the state to something other than processing state\n                        if (appliedState == null || !appliedState.Name.Equals(ProcessingState.StateName, StringComparison.OrdinalIgnoreCase))\n                        {\n                            // We should forget a job in a wrong state, or when timeout exceeded.\n                            requeueOnException = false;\n                            fetchedJob.RemoveFromQueue();\n                            return;\n                        }\n                    }\n\n                    // Checkpoint #3. Job is in the Processing state. However, there are\n                    // no guarantees that it was performed. We need to re-queue it even\n                    // it was performed to guarantee that it was performed AT LEAST once.\n                    // It will be re-queued after the JobTimeout was expired.\n\n                    var state = PerformJob(context, connection, fetchedJob.JobId, backgroundJob, out var customData);\n                    var transactionalAck = context.Storage.HasFeature(JobStorageFeatures.Transaction.RemoveFromQueue(fetchedJob.GetType()));\n\n                    if (state != null)\n                    {\n                        // Ignore return value, because we should not do anything when current state is not Processing.\n                        TryChangeState(\n                            context,\n                            connection,\n                            fetchedJob.JobId,\n                            state,\n                            customData,\n                            ProcessingStateArray,\n                            transactionalAck ? fetchedJob : null,\n                            out _,\n                            CancellationToken.None,\n                            context.ShutdownToken);\n                    }\n\n                    // Checkpoint #4. The job was performed, and it is in the one\n                    // of the explicit states (Succeeded, Scheduled and so on).\n                    // It should not be re-queued, but we still need to remove its\n                    // processing information.\n\n                    requeueOnException = false;\n                    fetchedJob.RemoveFromQueue();\n\n                    // Success point. No things must be done after previous command\n                    // was succeeded.\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    if (context.IsStopping)\n                    {\n                        var action = requeueOnException ? \"It will be re-queued\" : \"It will be removed from queue later\";\n                        _logger.Warn($\"Worker stop requested while processing background job '{fetchedJob.JobId}'. {action}.\");\n                    }\n\n                    if (requeueOnException)\n                    {\n                        Requeue(fetchedJob);\n                    }\n\n                    throw;\n                }\n            }\n        }\n\n        private IState TryChangeState(\n            BackgroundProcessContext context, \n            IStorageConnection connection, \n            string jobId,\n            IState state,\n            IReadOnlyDictionary<string, object> customData,\n            string[] expectedStates,\n            IFetchedJob completeJob,\n            out BackgroundJob backgroundJob,\n            CancellationToken initializeToken,\n            CancellationToken abortToken)\n        {\n            Exception exception = null;\n\n            abortToken.ThrowIfCancellationRequested();\n\n            // At least one retry attempt should always be performed.\n            var maxRetryAttempts = _maxStateChangeAttempts > 0 ? _maxStateChangeAttempts : 1;\n\n            for (var retryAttempt = 0; retryAttempt < maxRetryAttempts; retryAttempt++)\n            {\n                try\n                {\n                    var stateChangeContext = new StateChangeContext(\n                        context.Storage,\n                        connection,\n                        null,\n                        jobId,\n                        state,\n                        expectedStates,\n                        disableFilters: false,\n                        completeJob,\n                        initializeToken,\n                        _profiler,\n                        context.ServerId,\n                        customData);\n\n                    var resultingState = _stateChanger.ChangeState(stateChangeContext);\n\n                    backgroundJob = stateChangeContext.ProcessedJob;\n                    return resultingState;\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    _logger.DebugException(\n                        $\"State change attempt {retryAttempt + 1} of {_maxStateChangeAttempts} failed due to an error, see inner exception for details\", \n                        ex);\n\n                    exception = ex;\n                }\n\n                abortToken.WaitOrThrow(TimeSpan.FromSeconds(retryAttempt));\n            }\n\n            _logger.ErrorException(\n                $\"{_maxStateChangeAttempts} state change attempt(s) failed due to an exception, moving job to the FailedState\",\n                exception);\n\n            var failedStateContext = new StateChangeContext(\n                context.Storage,\n                connection,\n                null,\n                jobId,\n                new FailedState(exception, context.ServerId) { Reason = $\"Failed to change state to a '{state.Name}' one due to an exception after {_maxStateChangeAttempts} retry attempts\" },\n                expectedStates,\n                disableFilters: true,\n                completeJob,\n                initializeToken,\n                _profiler,\n                context.ServerId);\n\n            var failedResult = _stateChanger.ChangeState(failedStateContext);\n            backgroundJob = failedStateContext.ProcessedJob;\n            return failedResult;\n        }\n\n        private void Requeue(IFetchedJob fetchedJob)\n        {\n            try\n            {\n                fetchedJob.Requeue();\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                _logger.WarnException($\"Failed to immediately re-queue the background job '{fetchedJob.JobId}'. Next invocation may be delayed, if invisibility timeout is used\", ex);\n            }\n        }\n\n        private IState PerformJob(\n            BackgroundProcessContext context,\n            IStorageConnection connection,\n            string jobId,\n            BackgroundJob backgroundJob,\n            out IReadOnlyDictionary<string, object> customData)\n        {\n            customData = null;\n\n            try\n            {\n                if (backgroundJob == null)\n                {\n                    var jobData = connection.GetJobData(jobId);\n                    if (jobData == null)\n                    {\n                        // Job expired just after moving to a processing state. This is an\n                        // unreal scenario, but shit happens. Returning null instead of throwing\n                        // an exception and rescuing from en-queueing a poisoned jobId back\n                        // to a queue.\n                        return null;\n                    }\n\n                    jobData.EnsureLoaded();\n                    backgroundJob = new BackgroundJob(jobId, jobData.Job, jobData.CreatedAt, jobData.ParametersSnapshot);\n                }\n\n                using (var jobToken = new ServerJobCancellationToken(connection, backgroundJob.Id, context.ServerId, WorkerGuidCache.GetOrAdd(context.ExecutionId, static guid => guid.ToString()), context.StoppedToken))\n                {\n                    var performContext = new PerformContext(context.Storage, connection, backgroundJob, jobToken, _profiler, context.ServerId, null);\n\n                    var latency = (DateTime.UtcNow - backgroundJob.CreatedAt).TotalMilliseconds;\n                    var duration = Stopwatch.StartNew();\n\n                    var result = _performer.Perform(performContext);\n                    duration.Stop();\n\n                    customData = new Dictionary<string, object>(performContext.Items);\n                    return !performContext.Items.TryGetValue(BackgroundJobPerformer.ContextCanceledKey, out var filter) \n                        ? (IState)new SucceededState(result, (long) latency, duration.ElapsedMilliseconds)\n                        : new DeletedState { Reason = $\"Canceled by filter '{filter}'\" };\n                }\n            }\n            catch (JobAbortedException)\n            {\n                // Background job performance was aborted due to a\n                // state change, so its identifier should be removed\n                // from a queue.\n                return null;\n            }\n            catch (JobPerformanceException ex)\n            {\n                return new FailedState(ex.InnerException, context.ServerId)\n                {\n                    Reason = ex.Message\n                };\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                if (ex is OperationCanceledException && context.IsStopped)\n                {\n                    throw;\n                }\n\n                return new FailedState(ex, context.ServerId)\n                {\n                    Reason = \"An exception occurred during processing of a background job.\"\n                };\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/ApplyStateContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Profiling;\nusing Hangfire.Storage;\n\nnamespace Hangfire.States\n{\n#pragma warning disable 618\n    public class ApplyStateContext : StateContext\n#pragma warning restore 618\n    {\n        public ApplyStateContext(\n            [NotNull] IWriteOnlyTransaction transaction, \n            [NotNull] ElectStateContext context)\n            : this(context.Storage, context.Connection, transaction, context.BackgroundJob, context.CandidateState, context.CurrentState, context.Profiler, context.StateMachine, context.CustomData != null ? new Dictionary<string, object>(context.CustomData) : null)\n        {\n            // TODO: Add explicit JobExpirationTimeout parameter in 2.0, because it's unclear it isn't preserved\n        }\n\n        public ApplyStateContext(\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [NotNull] IWriteOnlyTransaction transaction,\n            [NotNull] BackgroundJob backgroundJob,\n            [NotNull] IState newState,\n            [CanBeNull] string oldStateName)\n            : this(storage, connection, transaction, backgroundJob, newState, oldStateName, EmptyProfiler.Instance, null)\n        {\n        }\n\n        internal ApplyStateContext(\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [NotNull] IWriteOnlyTransaction transaction,\n            [NotNull] BackgroundJob backgroundJob,\n            [NotNull] IState newState, \n            [CanBeNull] string oldStateName,\n            [NotNull] IProfiler profiler,\n            [CanBeNull] IStateMachine stateMachine,\n            [CanBeNull] IReadOnlyDictionary<string, object> customData = null)\n        {\n            BackgroundJob = backgroundJob ?? throw new ArgumentNullException(nameof(backgroundJob));\n            Storage = storage ?? throw new ArgumentNullException(nameof(storage));\n            Connection = connection ?? throw new ArgumentNullException(nameof(connection));\n            Transaction = transaction ?? throw new ArgumentNullException(nameof(transaction));\n            NewState = newState ?? throw new ArgumentNullException(nameof(newState));\n            OldStateName = oldStateName;\n            Profiler = profiler ?? throw new ArgumentNullException(nameof(profiler));\n            StateMachine = stateMachine;\n            CustomData = customData;\n            JobExpirationTimeout = storage.JobExpirationTimeout;\n        }\n\n        [NotNull]\n        public JobStorage Storage { get; }\n\n        [NotNull]\n        public IStorageConnection Connection { get; }\n\n        [NotNull]\n        public IWriteOnlyTransaction Transaction { get; }\n        \n        public override BackgroundJob BackgroundJob { get; }\n\n        [CanBeNull]\n        public string OldStateName { get; }\n\n        [NotNull]\n        public IState NewState { get; }\n        \n        public TimeSpan JobExpirationTimeout { get; set; }\n\n        [NotNull]\n        internal IProfiler Profiler { get; }\n\n        [CanBeNull]\n        public IReadOnlyDictionary<string, object> CustomData { get; }\n\n        [CanBeNull]\n        public IStateMachine StateMachine { get; }\n\n        public T GetJobParameter<T>([NotNull] string name) => GetJobParameter<T>(name, allowStale: false);\n\n        public T GetJobParameter<T>([NotNull] string name, bool allowStale)\n        {\n            if (String.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));\n\n            try\n            {\n                string value;\n\n                if (allowStale && BackgroundJob.ParametersSnapshot != null)\n                {\n                    BackgroundJob.ParametersSnapshot.TryGetValue(name, out value);\n                }\n                else\n                {\n                    value = Connection.GetJobParameter(BackgroundJob.Id, name);                \n                }\n\n                return SerializationHelper.Deserialize<T>(value, SerializationOption.User);\n            }\n            catch (Exception ex)\n            {\n                throw new InvalidOperationException(\n                    $\"Could not get a value of the job parameter `{name}`. See inner exception for details.\", ex);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/AwaitingState.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Storage;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Defines the <i>intermediate</i> state of a background job when it is waiting\n    /// for a parent background job to be finished before it is moved to the\n    /// <see cref=\"EnqueuedState\"/> by the <see cref=\"ContinuationsSupportAttribute\"/>\n    /// filter.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// <para>Background job in <see cref=\"AwaitingState\"/> is referred as a\n    /// <b>continuation</b> of a background job with <see cref=\"ParentId\"/>.</para>\n    /// </remarks>\n    /// \n    /// <threadsafety static=\"true\" instance=\"false\"/>\n    public class AwaitingState : IState\n    {\n        private static readonly TimeSpan DefaultExpiration = TimeSpan.FromDays(365);\n\n        /// <summary>\n        /// Represents the name of the <i>Awaiting</i> state. This field is read-only.\n        /// </summary>\n        /// <remarks>\n        /// The value of this field is <c>\"Awaiting\"</c>.\n        /// </remarks>\n        public static readonly string StateName = \"Awaiting\";\n        \n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"AwaitingState\"/> class with\n        /// the specified parent background job id and with an instance of the \n        /// <see cref=\"EnqueuedState\"/> class as a next state.\n        /// </summary>\n        /// <param name=\"parentId\">The identifier of a background job to wait for.</param>\n        public AwaitingState([NotNull] string parentId)\n            : this(parentId, new EnqueuedState())\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"AwaitingState\"/> class with\n        /// the specified parent job id and next state.\n        /// </summary>\n        /// <param name=\"parentId\">The identifier of a background job to wait for.</param>\n        /// <param name=\"nextState\">The next state for the continuation.</param>\n        // TODO: Warning inconsistency - everywhere else is OnlyOnSucceededState\n        public AwaitingState([NotNull] string parentId, [NotNull] IState nextState)\n            : this(parentId, nextState, JobContinuationOptions.OnAnyFinishedState)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"AwaitingState\"/> class with\n        /// the given <i>options</i> along with other parameters.\n        /// </summary>\n        /// <param name=\"parentId\">The identifier of a background job to wait for.</param>\n        /// <param name=\"nextState\">The next state for the continuation.</param>\n        /// <param name=\"options\">Options to configure a continuation.</param>\n        [JsonConstructor]\n        public AwaitingState([NotNull] string parentId, [NotNull] IState nextState, JobContinuationOptions options)\n            : this(parentId, nextState, options, DefaultExpiration)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"AwaitingState\"/> class with\n        /// the specified <i>expiration time</i> along with other parameters.\n        /// </summary>\n        /// <param name=\"parentId\">The identifier of a background job to wait for.</param>\n        /// <param name=\"nextState\">The next state for the continuation.</param>\n        /// <param name=\"options\">Options to configure the continuation.</param>\n        /// <param name=\"expiration\">The expiration time for the continuation.</param>\n        public AwaitingState(\n            [NotNull] string parentId,\n            [NotNull] IState nextState,\n            JobContinuationOptions options,\n            TimeSpan expiration)\n        {\n            if (parentId == null) throw new ArgumentNullException(nameof(parentId));\n            if (nextState == null) throw new ArgumentNullException(nameof(nextState));\n\n            ParentId = parentId;\n            NextState = nextState;\n\n            Options = options;\n            Expiration = expiration;\n        }\n\n        /// <summary>\n        /// Gets the identifier of a parent background job.\n        /// </summary>\n        [NotNull]\n        public string ParentId { get; }\n\n        /// <summary>\n        /// Gets the next state, to which a background job will be moved.\n        /// </summary>\n        [NotNull]\n        public IState NextState { get; }\n\n        /// <summary>\n        /// Gets the continuation options associated with the current state.\n        /// </summary>\n        public JobContinuationOptions Options { get; }\n\n        /// <summary>\n        /// Gets the expiration time of a background job continuation.\n        /// </summary>\n        [JsonIgnore]\n        public TimeSpan Expiration { get; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always equals to <see cref=\"StateName\"/> for the <see cref=\"AwaitingState\"/>.\n        /// Please see the remarks section of the <see cref=\"IState.Name\">IState.Name</see>\n        /// article for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public string Name => StateName;\n\n        /// <inheritdoc />\n        public string Reason { get; set; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"false\"/> for the <see cref=\"AwaitingState\"/>.\n        /// Please refer to the <see cref=\"IState.IsFinal\">IState.IsFinal</see> documentation\n        /// for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IsFinal => false;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"false\" /> for the <see cref=\"AwaitingState\"/>.\n        /// Please see the description of this property in the\n        /// <see cref=\"IState.IgnoreJobLoadException\">IState.IgnoreJobLoadException</see>\n        /// article.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IgnoreJobLoadException => false;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// <para>Returning dictionary contains the following keys. You can obtain \n        /// the state data by using the <see cref=\"IStorageConnection.GetStateData\"/>\n        /// method.</para>\n        /// <list type=\"table\">\n        ///     <listheader>\n        ///         <term>Key</term>\n        ///         <term>Type</term>\n        ///         <term>Deserialize Method</term>\n        ///         <description>Notes</description>\n        ///     </listheader>\n        ///     <item>\n        ///         <term><c>ParentId</c></term>\n        ///         <term><see cref=\"string\"/></term>\n        ///         <term><i>Not required</i></term>\n        ///         <description>Please see the <see cref=\"ParentId\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>NextState</c></term>\n        ///         <term><see cref=\"IState\"/></term>\n        ///         <term>\n        ///             <see cref=\"SerializationHelper.Deserialize{T}(string, SerializationOption)\"/> with \n        ///             <see cref=\"SerializationOption.TypedInternal\"/>\n        ///         </term>\n        ///         <description>Please see the <see cref=\"NextState\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>Options</c></term>\n        ///         <term><see cref=\"JobContinuationOptions\"/></term>\n        ///         <term>\n        ///             <see cref=\"Enum.Parse(Type, string)\"/> with <see cref=\"JobContinuationOptions\"/>\n        ///         </term>\n        ///         <description>Please see the <see cref=\"Options\"/> property.</description>\n        ///     </item>\n        /// </list>\n        /// </remarks>\n        public Dictionary<string, string> SerializeData()\n        {\n            var result = new Dictionary<string, string>\n            {\n                { \"ParentId\", ParentId },\n                { \"NextState\", SerializationHelper.Serialize(NextState, SerializationOption.TypedInternal) }\n            };\n\n            if (GlobalConfiguration.HasCompatibilityLevel(CompatibilityLevel.Version_170))\n            {\n                result.Add(\"Options\", Options.ToString(\"D\"));\n            }\n            else\n            {\n                result.Add(\"Options\", Options.ToString(\"G\"));\n                result.Add(\"Expiration\", Expiration.ToString());\n            }\n\n            return result;\n        }\n\n        internal sealed class Handler : IStateHandler\n        {\n            public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)\n            {\n                if (!GlobalConfiguration.HasCompatibilityLevel(CompatibilityLevel.Version_180) ||\n                    !context.Storage.HasFeature(JobStorageFeatures.Monitoring.AwaitingJobs))\n                {\n                    transaction.AddToSet(\"awaiting\", context.BackgroundJob.Id, JobHelper.ToTimestamp(DateTime.UtcNow));\n                }\n            }\n\n            public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)\n            {\n                transaction.RemoveFromSet(\"awaiting\", context.BackgroundJob.Id);\n            }\n\n            // ReSharper disable once MemberHidesStaticFromOuterClass\n            public string StateName => AwaitingState.StateName;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/BackgroundJobStateChanger.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Storage;\n\nnamespace Hangfire.States\n{\n    public class BackgroundJobStateChanger : IBackgroundJobStateChanger\n    {\n        private static readonly TimeSpan JobLockTimeout = TimeSpan.FromMinutes(15);\n        private readonly StateMachine _stateMachine;\n\n        public BackgroundJobStateChanger()\n            : this(JobFilterProviders.Providers)\n        {\n        }\n\n        public BackgroundJobStateChanger([NotNull] IJobFilterProvider filterProvider)\n            : this(new StateMachine(filterProvider, new CoreStateMachine()))\n        {\n        }\n\n        public BackgroundJobStateChanger([NotNull] StateMachine stateMachine)\n        {\n            _stateMachine = stateMachine ?? throw new ArgumentNullException(nameof(stateMachine));\n        }\n\n        internal BackgroundJobStateChanger([NotNull] IJobFilterProvider filterProvider, [NotNull] IStateMachine stateMachine)\n        {\n            if (filterProvider == null) throw new ArgumentNullException(nameof(filterProvider));\n            if (stateMachine == null) throw new ArgumentNullException(nameof(stateMachine));\n\n            _stateMachine = new StateMachine(filterProvider, stateMachine);\n        }\n        \n        public IState ChangeState(StateChangeContext context)\n        {\n            // To ensure that job state will be changed only from one of the\n            // specified states, we need to ensure that other users/workers\n            // are not able to change the state of the job during the\n            // execution of this method. To guarantee this behavior, we are\n            // using distributed application locks and rely on fact, that\n            // any state transitions will be made only within a such lock.\n            IDisposable distributedLock = null;\n\n            if (context.Transaction != null)\n            {\n                context.Transaction.AcquireDistributedJobLock(context.BackgroundJobId, JobLockTimeout);\n            }\n            else\n            {\n                distributedLock = context.Connection.AcquireDistributedJobLock(context.BackgroundJobId, JobLockTimeout);\n            }\n\n            using (distributedLock)\n            {\n                var jobData = GetJobData(context);\n\n                if (jobData == null)\n                {\n                    return null;\n                }\n\n                if (context.ExpectedStates != null && !context.ExpectedStates.Contains(jobData.State, StringComparer.OrdinalIgnoreCase))\n                {\n                    return null;\n                }\n                \n                var stateToApply = context.NewState;\n\n                try\n                {\n                    jobData.EnsureLoaded();\n                }\n                catch (JobLoadException ex)\n                {\n                    // This happens when Hangfire couldn't find the target method,\n                    // serialized within a background job. There are many reasons\n                    // for this case, including refactored code, or a missing\n                    // assembly reference due to a mistake or erroneous deployment.\n                    // \n                    // The problem is that in this case we can't get any filters,\n                    // applied at a method or a class level, and we can't proceed\n                    // with the state change without breaking a consistent behavior:\n                    // in some cases our filters will be applied, and in other ones\n                    // will not.\n                    if (!stateToApply.IgnoreJobLoadException)\n                    {\n                        stateToApply = new FailedState(ex.InnerException, context.ServerId)\n                        {\n                            Reason = $\"Can not change the state to '{stateToApply.Name}': target method was not found.\"\n                        };\n                    }\n                }\n\n                IWriteOnlyTransaction transaction;\n                IDisposable disposableTransaction;\n\n                if (context.Transaction == null)\n                {\n                    disposableTransaction = transaction = context.Connection.CreateWriteTransaction();\n                }\n                else\n                {\n                    transaction = context.Transaction;\n                    disposableTransaction = null;\n                }\n                \n                var backgroundJob = new BackgroundJob(context.BackgroundJobId, jobData.Job, jobData.CreatedAt, jobData.ParametersSnapshot);\n\n                using (disposableTransaction)\n                {\n                    var applyContext = new ApplyStateContext(\n                        context.Storage,\n                        context.Connection,\n                        transaction,\n                        backgroundJob,\n                        stateToApply,\n                        jobData.State,\n                        context.Profiler,\n                        _stateMachine,\n                        context.CustomData);\n\n                    // State changing process can fail due to an exception in state filters themselves,\n                    // and DisableFilters property will cause state machine to perform a state transition\n                    // without calling any filters. This is required when all the other state change\n                    // attempts failed and we need to remove such a job from the processing pipeline.\n                    // In this case all the filters are ignored, which may lead to confusion, so it's\n                    // highly recommended to use the DisableFilters property only when changing state\n                    // to the FailedState.\n                    var stateMachine = context.DisableFilters ? _stateMachine.InnerStateMachine : _stateMachine;\n                    var appliedState = stateMachine.ApplyState(applyContext);\n\n                    if (context.CompleteJob != null)\n                    {\n                        if (transaction is JobStorageTransaction jobStorageTransaction)\n                        {\n                            jobStorageTransaction.RemoveFromQueue(context.CompleteJob);\n                        }\n                        else\n                        {\n                            throw new InvalidOperationException(\"Storage transaction class must inherit the \" + nameof(JobStorageTransaction) + \" class to use transactional acknowledge\");\n                        }\n                    }\n\n                    if (context.Transaction == null)\n                    {\n                        transaction.Commit();\n                    }\n\n                    context.ProcessedJob = backgroundJob;\n                    return appliedState;\n                }\n            }\n        }\n\n        private static JobData GetJobData(StateChangeContext context)\n        {\n            // This code was introduced as a fix for an issue, which appeared when an\n            // external queue implementation was used together with a non-linearizable\n            // storage. The problem was likely related to the SQL Azure + Azure ServiceBus\n            // (or RabbitMQ or so) bundle, because the READCOMMITTED_SNAPSHOT_ON setting\n            // is enabled by default there.\n            //\n            // Since external queueing doesn't share the linearization point with the\n            // storage, it is possible that a worker will pick up a background job before\n            // its transaction was committed. Non-linearizable read will simply return\n            // the NULL value instead of waiting for a transaction to be committed. With\n            // this code, we will make several retry attempts to handle this case to wait\n            // on the client side.\n            // \n            // On the other hand, we need to give up after some retry attempt, because\n            // we should also handle the case, when our queue and job storage became\n            // unsynchronized with each other due to failures, manual intervention or so.\n            // Otherwise we will wait forever in this cases, since And there's no way to\n            // make a distinction between a non-linearizable read and the storages, non-\n            // synchronized with each other.\n            // \n            // In recent versions, Hangfire.SqlServer uses query hints to make all the\n            // reads linearizable no matter what, but there may be other storages that\n            // still require this workaround. So we leave this part implemented, but\n            // decrease the number of attempt from infinite to only a few, because most\n            // storages will provide linearizable reads anyway. It's better to have\n            // \"hanging\" job in the Processing jobs page in the Dashboard UI than use\n            // infinite loop under the hoods - the former case is better discoverable,\n            // and it's not hard to improve the storage implementation.\n\n            // TODO 2.0:\n            // Eliminate the need of this timeout by placing an explicit requirement to\n            // storage implementations to either have a single linearization point for all\n            // the operations inside a transaction; or make all the reads linearizable and\n            // execute queueing operations after all the other ones in a transaction.\n\n            for (var retryAttempt = 0; retryAttempt < 5; retryAttempt++)\n            {\n                var jobData = context.Connection.GetJobData(context.BackgroundJobId);\n\n                // Empty state means our job wasn't moved to any state after its creation.\n                // Such a jobs may be created by internal logic, and those jobs have very\n                // special meaning, thus we shouldn't allow state changer to alter them,\n                // using this class (which can be used by users), leaving this logic to\n                // low level API only, i.e. state machine.\n\n                // TODO 1.X:\n                // However, we shouldn't wait for the initial state change, because in some\n                // cases (like in batches) it may take days. We should throw an exception\n                // instead, clearly indicating that such a state change is prohibited. There\n                // may be some issues on GitHub, related to the hanging dashboard requests\n                // in this case.\n\n                if (!String.IsNullOrEmpty(jobData?.State) || context.Storage.LinearizableReads)\n                {\n                    return jobData;\n                }\n\n                // State change can also be requested from user's request processing logic.\n                // There is always a chance it will be issued against a non-existing or an\n                // already expired background job, and a minute wait (or whatever timeout is\n                // used) is completely unnecessary in this case.\n                // \n                // Since waiting is only required when a worker picks up a job, and \n                // cancellation tokens are used only by the Worker class, we can avoid the\n                // unnecessary waiting logic when no cancellation token is passed.\n\n                if (context.CancellationToken.IsCancellationRequested ||\n                    context.CancellationToken == CancellationToken.None)\n                {\n                    return null;\n                }\n\n                context.CancellationToken.Wait(TimeSpan.FromSeconds(retryAttempt));\n            }\n\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/States/CoreStateMachine.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.States\n{\n    internal sealed class CoreStateMachine : IStateMachine\n    {\n        private readonly Func<JobStorage, string, StateHandlersCollection> _stateHandlersThunk;\n\n        public CoreStateMachine()\n            : this(GetStateHandlers)\n        {\n        }\n\n        internal CoreStateMachine([NotNull] Func<JobStorage, string, StateHandlersCollection> stateHandlersThunk)\n        {\n            if (stateHandlersThunk == null) throw new ArgumentNullException(nameof(stateHandlersThunk));\n            _stateHandlersThunk = stateHandlersThunk;\n        }\n\n        public IState ApplyState(ApplyStateContext context)\n        {\n            foreach (var handler in _stateHandlersThunk(context.Storage, context.OldStateName))\n            {\n                handler.Unapply(context, context.Transaction);\n            }\n\n            context.Transaction.SetJobState(context.BackgroundJob.Id, context.NewState);\n\n            foreach (var handler in _stateHandlersThunk(context.Storage, context.NewState.Name))\n            {\n                handler.Apply(context, context.Transaction);\n            }\n\n            if (context.NewState.IsFinal)\n            {\n                context.Transaction.ExpireJob(context.BackgroundJob.Id, context.JobExpirationTimeout);\n            }\n            else\n            {\n                context.Transaction.PersistJob(context.BackgroundJob.Id);\n            }\n\n            return context.NewState;\n        }\n\n        private static StateHandlersCollection GetStateHandlers(JobStorage storage, string stateName)\n        {\n            var globalHandlers = GlobalStateHandlers.Handlers;\n\n            return new StateHandlersCollection(\n                globalHandlers as List<IStateHandler> ?? globalHandlers.ToList(),\n                storage.GetStateHandlers(),\n                stateName);\n        }\n\n        internal readonly struct StateHandlersCollection(\n            List<IStateHandler> globalHandlers,\n            IEnumerable<IStateHandler> storageHandlers,\n            string stateName)\n        {\n            public Enumerator GetEnumerator() => new Enumerator(globalHandlers, storageHandlers, stateName);\n\n            public ref struct Enumerator\n            {\n                private List<IStateHandler>.Enumerator _globalEnumerator;\n                private readonly IEnumerator<IStateHandler> _storageEnumerator;\n                private readonly string _stateName;\n                private IStateHandler _current;\n\n                public Enumerator(List<IStateHandler> globalHandlers, IEnumerable<IStateHandler> storageHandlers, string stateName)\n                {\n                    _globalEnumerator = globalHandlers.GetEnumerator();\n                    _storageEnumerator = storageHandlers.GetEnumerator();\n                    _stateName = stateName;\n                    _current = default;\n                }\n\n                public bool MoveNext()\n                {\n                    while (_globalEnumerator.MoveNext())\n                    {\n                        var current = _globalEnumerator.Current!;\n                        if (current.StateName.Equals(_stateName, StringComparison.OrdinalIgnoreCase))\n                        {\n                            _current = current;\n                            return true;\n                        }\n                    }\n\n                    while (_storageEnumerator.MoveNext())\n                    {\n                        var current = _storageEnumerator.Current!;\n                        if (current.StateName.Equals(_stateName, StringComparison.OrdinalIgnoreCase))\n                        {\n                            _current = current;\n                            return true;\n                        }\n                    }\n\n                    _current = default;\n                    return false;\n                }\n\n                public IStateHandler Current => _current;\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/DeletedState.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Storage;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Defines the <i>final</i> state of a background job when nobody\n    /// is interested whether it was performed or not.\n    /// </summary>\n    /// <remarks>\n    /// <para>Deleted state is used when you are not interested in a processing\n    /// of a background job. This state isn't backed by any background process,\n    /// so when you change a state of the job to the <i>Deleted</i>, only\n    /// expiration time will be set on a job without any additional processing.</para>\n    /// </remarks>\n    /// \n    /// <example>\n    /// <para>The following example demonstrates how to cancel an <i>enqueued</i> background\n    /// job. Please note that this job may be processed before you change its state.</para>\n    /// <para>This example shows how to create an instance of the <see cref=\"DeletedState\"/>\n    /// class and use the <see cref=\"IBackgroundJobClient.ChangeState\"/> method. Please see\n    /// <see cref=\"O:Hangfire.BackgroundJob.Delete\">BackgroundJob.Delete</see>\n    /// and <see cref=\"O:Hangfire.BackgroundJobClientExtensions.Delete\">BackgroundJobClientExtensions.Delete</see>\n    /// method overloads for simpler API.</para> \n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\States.cs\" region=\"DeletedState\" />\n    /// \n    /// </example>\n    /// \n    /// <seealso cref=\"O:Hangfire.BackgroundJob.Delete\">BackgroundJob.Delete Overload</seealso>\n    /// <seealso cref=\"O:Hangfire.BackgroundJobClientExtensions.Delete\">BackgroundJobClientExtensions.Delete Overload</seealso>\n    /// <seealso cref=\"IBackgroundJobClient.ChangeState\" />\n    /// \n    /// <threadsafety static=\"true\" instance=\"false\" />\n    public class DeletedState : IState\n    {\n        public static readonly Exception DefaultException = new OperationCanceledException();\n\n        /// <summary>\n        /// Represents the name of the <i>Deleted</i> state. This field is read-only.\n        /// </summary>\n        /// <remarks>\n        /// The value of this field is <c>\"Deleted\"</c>.\n        /// </remarks>\n        public static readonly string StateName = \"Deleted\";\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DeletedState\"/> class.\n        /// </summary>\n        public DeletedState() : this(null)\n        {\n        }\n\n        public DeletedState([CanBeNull] ExceptionInfo exceptionInfo)\n        {\n            ExceptionInfo = exceptionInfo;\n            DeletedAt = DateTime.UtcNow;\n        }\n\n        [JsonProperty(\"Ex\", NullValueHandling = NullValueHandling.Ignore)]\n        public ExceptionInfo ExceptionInfo { get; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always equals to <see cref=\"StateName\"/> for the <see cref=\"DeletedState\"/>.\n        /// Please see the remarks section of the <see cref=\"IState.Name\">IState.Name</see>\n        /// article for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public string Name => StateName;\n\n        /// <inheritdoc />\n        public string Reason { get; set; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"true\"/> for the <see cref=\"DeletedState\"/>.\n        /// Please refer to the <see cref=\"IState.IsFinal\">IState.IsFinal</see> documentation\n        /// for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IsFinal => true;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"true\" /> for the <see cref=\"DeletedState\"/>.\n        /// Please see the description of this property in the\n        /// <see cref=\"IState.IgnoreJobLoadException\">IState.IgnoreJobLoadException</see>\n        /// article.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IgnoreJobLoadException => true;\n\n        /// <summary>\n        /// Gets a date/time when the current state instance was created.\n        /// </summary>\n        [JsonIgnore]\n        public DateTime DeletedAt { get; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// <para>Returning dictionary contains the following keys. You can obtain \n        /// the state data by using the <see cref=\"IStorageConnection.GetStateData\"/>\n        /// method.</para>\n        /// <list type=\"table\">\n        ///     <listheader>\n        ///         <term>Key</term>\n        ///         <term>Type</term>\n        ///         <term>Deserialize Method</term>\n        ///         <description>Notes</description>\n        ///     </listheader>\n        ///     <item>\n        ///         <term><c>DeletedAt</c></term>\n        ///         <term><see cref=\"DateTime\"/></term>\n        ///         <term><see cref=\"JobHelper.DeserializeDateTime\"/></term>\n        ///         <description>Please see the <see cref=\"DeletedAt\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>Exception</c></term>\n        ///         <term><see cref=\"ExceptionInfo\"/></term>\n        ///         <term><see cref=\"SerializationHelper.Deserialize{T}(string, SerializationOption)\"/> with <see cref=\"SerializationOption.Internal\"/> option.</term>\n        ///         <description>Can be absent or null. Please see the <see cref=\"Exception\"/> property.</description>\n        ///     </item>\n        /// </list>\n        /// </remarks>\n        public Dictionary<string, string> SerializeData()\n        {\n            var result = new Dictionary<string, string>\n            {\n                { \"DeletedAt\", JobHelper.SerializeDateTime(DeletedAt) },\n            };\n\n            if (ExceptionInfo != null)\n            {\n                result.Add(\"Exception\", SerializationHelper.Serialize(ExceptionInfo, SerializationOption.User));\n            }\n\n            return result;\n        }\n\n        internal sealed class Handler : IStateHandler\n        {\n            public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)\n            {\n                transaction.IncrementCounter(\"stats:deleted\");\n            }\n\n            public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)\n            {\n                transaction.DecrementCounter(\"stats:deleted\");\n            }\n\n            // ReSharper disable once MemberHidesStaticFromOuterClass\n            public string StateName => DeletedState.StateName;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/States/ElectStateContext.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Profiling;\nusing Hangfire.Storage;\n\nnamespace Hangfire.States\n{\n#pragma warning disable 618\n    public class ElectStateContext : StateContext\n#pragma warning restore 618\n    {\n        private readonly IList<IState> _traversedStates = new List<IState>();\n        private IState _candidateState;\n\n        public ElectStateContext([NotNull] ApplyStateContext applyContext)\n            : this(applyContext, null)\n        {\n        }\n\n        public ElectStateContext([NotNull] ApplyStateContext applyContext, [CanBeNull] StateMachine stateMachine)\n        {\n            if (applyContext == null) throw new ArgumentNullException(nameof(applyContext));\n            \n            BackgroundJob = applyContext.BackgroundJob;\n            _candidateState = applyContext.NewState;\n\n            Storage = applyContext.Storage;\n            Connection = applyContext.Connection;\n            Transaction = applyContext.Transaction;\n            CurrentState = applyContext.OldStateName;\n            Profiler = applyContext.Profiler;\n            CustomData = applyContext.CustomData?.ToDictionary(static x => x.Key, static x => x.Value);\n            StateMachine = stateMachine;\n        }\n        \n        public override BackgroundJob BackgroundJob { get; }\n\n        [NotNull]\n        public JobStorage Storage { get; }\n\n        [NotNull]\n        public IStorageConnection Connection { get; }\n\n        [NotNull]\n        public IWriteOnlyTransaction Transaction { get; }\n\n        [NotNull]\n        public IState CandidateState\n        {\n            get => _candidateState;\n            set\n            {\n                if (value == null)\n                {\n                    throw new ArgumentNullException(nameof(value), \"The CandidateState property can not be set to null.\");\n                }\n\n                if (_candidateState != value)\n                {\n                    _traversedStates.Add(_candidateState);\n                    _candidateState = value;\n                }\n            }\n        }\n\n        [CanBeNull]\n        public string CurrentState { get; }\n\n        [NotNull]\n        public IState[] TraversedStates => _traversedStates.ToArray();\n\n        [NotNull]\n        internal IProfiler Profiler { get; }\n\n        [CanBeNull]\n        public IDictionary<string, object> CustomData { get; }\n        \n        [CanBeNull]\n        public StateMachine StateMachine { get; }\n\n        public void SetJobParameter<T>([NotNull] string name, T value)\n        {\n            if (String.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));\n            Connection.SetJobParameter(BackgroundJob.Id, name, SerializationHelper.Serialize(value, SerializationOption.User));\n        }\n\n        public T GetJobParameter<T>([NotNull] string name) => GetJobParameter<T>(name, allowStale: false);\n\n        public T GetJobParameter<T>([NotNull] string name, bool allowStale)\n        {\n            if (String.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));\n\n            try\n            {\n                string value;\n\n                if (allowStale && BackgroundJob.ParametersSnapshot != null)\n                {\n                    BackgroundJob.ParametersSnapshot.TryGetValue(name, out value);\n                }\n                else\n                {\n                    value = Connection.GetJobParameter(BackgroundJob.Id, name);                \n                }\n\n                return SerializationHelper.Deserialize<T>(value, SerializationOption.User);\n            }\n            catch (Exception ex)\n            {\n                throw new InvalidOperationException(\n                    $\"Could not get a value of the job parameter `{name}`. See inner exception for details.\", ex);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/EnqueuedState.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text.RegularExpressions;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Storage;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Defines the <i>intermediate</i> state of a background job when it is placed \n    /// on a message queue to be processed by the <see cref=\"Server.Worker\"/> \n    /// background process <i>as soon as possible</i>.\n    /// </summary>\n    /// <remarks>\n    /// <para>Background job in <see cref=\"EnqueuedState\"/> is referred as\n    /// <b>fire-and-forget job</b>.</para>\n    /// <para>Background job identifier is placed on a queue with the given name. When\n    /// a queue name wasn't specified, the <see cref=\"DefaultQueue\"/> name will\n    /// be used. Message queue implementation depends on a current <see cref=\"JobStorage\"/>\n    /// instance.</para>\n    /// </remarks> \n    /// <example>\n    /// The following example demonstrates the creation of a background job in\n    /// <see cref=\"EnqueuedState\"/>. Please see \n    /// <see cref=\"O:Hangfire.BackgroundJob.Enqueue\">BackgroundJob.Enqueue</see>\n    /// and <see cref=\"O:Hangfire.BackgroundJobClientExtensions.Enqueue\">BackgroundJobClientExtensions.Enqueue</see>\n    /// method overloads for simpler API.\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\States.cs\" region=\"EnqueuedState #1\" />\n    /// <code lang=\"vb\" source=\"..\\VBSamples\\States.vb\" region=\"EnqueuedState #1\" />\n    /// \n    /// The code below implements the retry action for a failed background job.\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\States.cs\" region=\"EnqueuedState #2\" />\n    /// <code lang=\"vb\" source=\"..\\VBSamples\\States.vb\" region=\"EnqueuedState #2\" />\n    ///  \n    /// </example>\n    /// \n    /// <seealso cref=\"O:Hangfire.BackgroundJob.Enqueue\">BackgroundJob.Enqueue Overload</seealso>\n    /// <seealso cref=\"O:Hangfire.BackgroundJobClientExtensions.Enqueue\">BackgroundJobClientExtensions.Enqueue Overload</seealso>\n    /// <seealso cref=\"O:Hangfire.BackgroundJobClientExtensions.Create\">BackgroundJobClientExtensions.Create Overload</seealso>\n    /// <seealso cref=\"IBackgroundJobClient.Create\" />\n    /// <seealso cref=\"IBackgroundJobClient.ChangeState\" />\n    /// <seealso cref=\"Server.Worker\"/>\n    /// \n    /// <threadsafety static=\"true\" instance=\"false\" />\n    public class EnqueuedState : IState\n    {\n        /// <summary>\n        /// Represents the default queue name. This field is constant.\n        /// </summary>\n        /// <remarks>\n        /// The value of this field is <c>\"default\"</c>.\n        /// </remarks>\n        public const string DefaultQueue = \"default\";\n\n        /// <summary>\n        /// Represents the name of the <i>Enqueued</i> state. This field is read-only.\n        /// </summary>\n        /// <remarks>\n        /// The value of this field is <c>\"Enqueued\"</c>.\n        /// </remarks>\n        public static readonly string StateName = \"Enqueued\";\n\n        private string _queue;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"EnqueuedState\"/> class \n        /// with the <see cref=\"DefaultQueue\">default</see> queue name.\n        /// </summary>\n        public EnqueuedState()\n            : this(DefaultQueue)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"EnqueuedState\"/> class\n        /// with the specified queue name.\n        /// </summary>\n        /// <param name=\"queue\">The queue name to which a background job identifier will be added.</param>\n        /// \n        /// <seealso cref=\"Queue\"/>\n        /// \n        /// <exception cref=\"ArgumentNullException\">\n        /// The <paramref name=\"queue\"/> argument is <see langword=\"null\"/>, empty or consists only of \n        /// white-space characters.\n        /// </exception>\n        /// <exception cref=\"ArgumentException\">\n        /// The <paramref name=\"queue\"/> argument is not a valid queue name.\n        /// </exception>\n        [JsonConstructor]\n        public EnqueuedState([CanBeNull] string queue)\n        {\n            queue = queue ?? DefaultQueue;\n\n            ValidateQueueName(nameof(queue), queue);\n\n            _queue = queue;\n            EnqueuedAt = DateTime.UtcNow;\n        }\n\n        /// <summary>\n        /// Gets or sets a queue name to which a background job identifier\n        /// will be added.\n        /// </summary>\n        /// <value>A queue name that consists only of lowercase letters, digits and\n        /// underscores.</value>\n        /// <remarks>\n        /// <para>Queue name must consist only of lowercase letters, digits and\n        /// underscores, other characters aren't permitted. Some examples:</para>\n        /// <list type=\"bullet\">\n        ///     <item><c>\"critical\"</c> (good)</item>\n        ///     <item><c>\"worker_1\"</c> (good)</item>\n        ///     <item><c>\"documents queue\"</c> (bad, whitespace)</item>\n        ///     <item><c>\"MyQueue\"</c> (bad, capital letters)</item>\n        /// </list>\n        /// </remarks>\n        /// \n        /// <exception cref=\"ArgumentNullException\">\n        /// The value specified for a set operation is <see langword=\"null\"/>, \n        /// empty or consists only of white-space characters.\n        /// </exception>\n        /// <exception cref=\"ArgumentException\">\n        /// The value specified for a set operation is not a valid queue name.\n        /// </exception>\n        [NotNull]\n        public string Queue\n        {\n            get { return _queue; }\n            set\n            {\n                ValidateQueueName(nameof(value), value);\n                _queue = value;\n            }\n        }\n\n        /// <summary>\n        /// Gets a date/time when the current state instance was created.\n        /// </summary>\n        [JsonIgnore]\n        public DateTime EnqueuedAt { get; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always equals to <see cref=\"StateName\"/> for the <see cref=\"EnqueuedState\"/>.\n        /// Please see the remarks section of the <see cref=\"IState.Name\">IState.Name</see>\n        /// article for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public string Name => StateName;\n\n        /// <inheritdoc />\n        public string Reason { get; set; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"false\" /> for the <see cref=\"EnqueuedState\"/>.\n        /// Please refer to the <see cref=\"IState.IsFinal\">IState.IsFinal</see> documentation\n        /// for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IsFinal => false;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"false\"/> for the <see cref=\"EnqueuedState\"/>.\n        /// Please see the description of this property in the\n        /// <see cref=\"IState.IgnoreJobLoadException\">IState.IgnoreJobLoadException</see>\n        /// article.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IgnoreJobLoadException => false;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// <para>Returning dictionary contains the following keys. You can obtain \n        /// the state data by using the <see cref=\"IStorageConnection.GetStateData\"/>\n        /// method.</para>\n        /// <list type=\"table\">\n        ///     <listheader>\n        ///         <term>Key</term>\n        ///         <term>Type</term>\n        ///         <term>Deserialize Method</term>\n        ///         <description>Notes</description>\n        ///     </listheader>\n        ///     <item>\n        ///         <term><c>EnqueuedAt</c></term>\n        ///         <term><see cref=\"DateTime\"/></term>\n        ///         <term><see cref=\"JobHelper.DeserializeDateTime\"/></term>\n        ///         <description>Please see the <see cref=\"EnqueuedAt\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>Queue</c></term>\n        ///         <term><see cref=\"string\"/></term>\n        ///         <term><i>Not required</i></term>\n        ///         <description>Please see the <see cref=\"Queue\"/> property.</description>\n        ///     </item>\n        /// </list>\n        /// </remarks>\n        public Dictionary<string, string> SerializeData()\n        {\n            return new Dictionary<string, string>\n            {\n                { \"EnqueuedAt\", JobHelper.SerializeDateTime(EnqueuedAt) },\n                { \"Queue\", Queue }\n            };\n        }\n\n        internal static bool TryValidateQueueName([NotNull] string value)\n        {\n            if (String.IsNullOrWhiteSpace(value))\n            {\n                throw new ArgumentNullException(nameof(value));\n            }\n\n            return ValidateQueueNameInner(value);\n        }\n\n        internal static void ValidateQueueName([InvokerParameterName] string parameterName, [NotNull] string value)\n        {\n            if (String.IsNullOrWhiteSpace(value))\n            {\n                throw new ArgumentNullException(parameterName);\n            }\n\n            if (!ValidateQueueNameInner(value))\n            {\n                throw new ArgumentException(\n                    $\"The queue name must consist of lowercase letters, digits, underscore, and dash characters only. Given: '{value}'.\",\n                    parameterName);\n            }\n        }\n\n        private static bool ValidateQueueNameInner(string value)\n        {\n            foreach (var ch in value)\n            {\n                // ^[a-z0-9_-]+$\n                if (!((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '_'))\n                {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        internal sealed class Handler : IStateHandler\n        {\n            public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)\n            {\n                var enqueuedState = context.NewState as EnqueuedState;\n                if (enqueuedState == null)\n                {\n                    throw new InvalidOperationException(\n                        $\"`{typeof (Handler).FullName}` state handler can be registered only for the Enqueued state.\");\n                }\n\n                if (context.BackgroundJob.Job?.Queue != null && !context.Storage.HasFeature(JobStorageFeatures.JobQueueProperty))\n                {\n                    throw new NotSupportedException(\"Current storage doesn't support specifying queues directly for a specific job. Please use the QueueAttribute instead.\");\n                }\n\n                transaction.AddToQueue(\n                    context.BackgroundJob.Job?.Queue == null || !DefaultQueue.Equals(enqueuedState.Queue, StringComparison.OrdinalIgnoreCase)\n                        ? enqueuedState.Queue\n                        : context.BackgroundJob.Job.Queue,\n                    context.BackgroundJob.Id);\n            }\n\n            public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)\n            {\n            }\n\n            // ReSharper disable once MemberHidesStaticFromOuterClass\n            public string StateName => EnqueuedState.StateName;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/States/FailedState.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Defines the <i>intermediate</i> state of a background job when its processing \n    /// was interrupted by an exception and it is a developer's responsibility\n    /// to decide what to do with it next.\n    /// </summary>\n    /// <remarks>\n    /// <para>Failed state is used in Hangfire when something went wrong and an exception\n    /// occurred during the background job processing. The primary reason for this state\n    /// is to notify the developers that something went wrong. By default background job \n    /// is moved to the <i>Failed</i> state only after some automatic retries, because the \n    /// <see cref=\"AutomaticRetryAttribute\"/> filter is enabled by default.</para>\n    /// <note type=\"important\">\n    /// Failed jobs are <b>not expiring</b> and will stay in your current job storage \n    /// forever, increasing its size until you retry or delete them manually. If you \n    /// expect some exceptions, please use the following rules.\n    /// <list type=\"bullet\">\n    ///     <item>Ignore, move to <i>Succeeded</i> state – use the <c>catch</c>\n    ///     statement in your code without re-throwing the exception.</item>\n    ///     <item>Ignore, move to <i>Deleted</i> state – use the <see cref=\"AutomaticRetryAttribute\"/>\n    ///     with <see cref=\"AttemptsExceededAction.Delete\"/> option.</item>\n    ///     <item>Re-queue a job – use the <see cref=\"AutomaticRetryAttribute\"/> with\n    ///     <see cref=\"AttemptsExceededAction.Fail\"/> option.</item>\n    /// </list>\n    /// </note>\n    /// <para>It is not supposed to use the <see cref=\"FailedState\"/> class in a user\n    /// code unless you are writing state changing filters or new background processing\n    /// rules.</para>\n    /// </remarks>\n    /// \n    /// <seealso cref=\"AutomaticRetryAttribute\"/>\n    /// <seealso cref=\"IBackgroundJobStateChanger\"/>\n    /// <seealso cref=\"Server.Worker\"/>\n    /// \n    /// <threadsafety static=\"true\" instance=\"false\" />\n    public class FailedState : IState\n    {\n        internal static int? MaxLinesInExceptionDetails = 100;\n\n        /// <summary>\n        /// Represents the name of the <i>Failed</i> state. This field is read-only.\n        /// </summary>\n        /// <remarks>\n        /// The value of this field is <c>\"Failed\"</c>.\n        /// </remarks>\n        public static readonly string StateName = \"Failed\";\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"FailedState\"/> class\n        /// with the given exception.\n        /// </summary>\n        /// <param name=\"exception\">Exception that occurred during the background \n        /// job processing.</param>\n        /// \n        /// <exception cref=\"ArgumentNullException\">The <paramref name=\"exception\"/> \n        /// argument is <see langword=\"null\" /></exception>\n        public FailedState([NotNull] Exception exception)\n            : this(exception, null)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"FailedState\"/> class\n        /// with the given exception and specified server id.\n        /// </summary>\n        /// <param name=\"exception\">Exception that occurred during the background job processing.</param>\n        /// <param name=\"serverId\">Server Id on which the exception occurred.</param>\n        ///\n        /// <exception cref=\"ArgumentNullException\">The <paramref name=\"exception\"/>\n        /// argument is <see langword=\"null\" /></exception>\n        public FailedState([NotNull] Exception exception, [CanBeNull] string serverId)\n        {\n            if (exception == null) throw new ArgumentNullException(nameof(exception));\n\n            FailedAt = DateTime.UtcNow;\n            Exception = exception;\n            ServerId = serverId;\n            MaxLinesInStackTrace = MaxLinesInExceptionDetails;\n        }\n\n        /// <summary>\n        /// Gets a date/time when the current state instance was created.\n        /// </summary>\n        [JsonIgnore]\n        public DateTime FailedAt { get; }\n\n        /// <summary>\n        /// Gets the exception that occurred during the background job processing.\n        /// </summary>\n        public Exception Exception { get; }\n\n        [JsonIgnore]\n        public bool IncludeFileInfo { get; set; } = true;\n\n        /// <summary>\n        /// Gets the server identifier on which the exception occurred.\n        /// </summary>\n        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]\n        public string ServerId { get; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always equals to <see cref=\"StateName\"/> for the <see cref=\"FailedState\"/>.\n        /// Please see the remarks section of the <see cref=\"IState.Name\">IState.Name</see>\n        /// article for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public string Name => StateName;\n\n        /// <inheritdoc />\n        public string Reason { get; set; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"false\" /> for the <see cref=\"FailedState\"/>.\n        /// Please refer to the <see cref=\"IState.IsFinal\">IState.IsFinal</see> documentation\n        /// for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IsFinal => false;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"false\"/> for the <see cref=\"FailedState\"/>.\n        /// Please see the description of this property in the\n        /// <see cref=\"IState.IgnoreJobLoadException\">IState.IgnoreJobLoadException</see>\n        /// article.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IgnoreJobLoadException => false;\n\n        [JsonIgnore]\n        public int? MaxLinesInStackTrace { get; set; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// <para>Returning dictionary contains the following keys. You can obtain \n        /// the state data by using the <see cref=\"Storage.IStorageConnection.GetStateData\"/>\n        /// method.</para>\n        /// <list type=\"table\">\n        ///     <listheader>\n        ///         <term>Key</term>\n        ///         <term>Type</term>\n        ///         <term>Deserialize Method</term>\n        ///         <description>Notes</description>\n        ///     </listheader>\n        ///     <item>\n        ///         <term><c>FailedAt</c></term>\n        ///         <term><see cref=\"DateTime\"/></term>\n        ///         <term><see cref=\"JobHelper.DeserializeDateTime\"/></term>\n        ///         <description>Please see the <see cref=\"FailedAt\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>ExceptionType</c></term>\n        ///         <term><see cref=\"string\"/></term>\n        ///         <term><i>Not required</i></term>\n        ///         <description>The full name of the current exception type.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>ExceptionMessage</c></term>\n        ///         <term><see cref=\"string\"/></term>\n        ///         <term><i>Not required</i></term>\n        ///         <description>Message that describes the current exception.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>ExceptionDetails</c></term>\n        ///         <term><see cref=\"string\"/></term>\n        ///         <term><i>Not required</i></term>\n        ///         <description>String representation of the current exception.</description>\n        ///     </item>\n        /// </list>\n        /// </remarks>\n        public Dictionary<string, string> SerializeData()\n        {\n            var result = new Dictionary<string, string>\n            {\n                { \"FailedAt\", JobHelper.SerializeDateTime(FailedAt) },\n                { \"ExceptionType\", Exception.GetType().FullName },\n                { \"ExceptionMessage\", Exception.Message },\n                { \"ExceptionDetails\", Exception.ToStringWithOriginalStackTrace(MaxLinesInStackTrace, IncludeFileInfo) }\n            };\n\n            if (ServerId != null)\n            {\n                result.Add(\"ServerId\", ServerId);\n            }\n\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/States/IApplyStateFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing Hangfire.Storage;\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Provides methods that are required for a state changed filter.\n    /// </summary>\n    public interface IApplyStateFilter\n    {\n        /// <summary>\n        /// Called after the specified state was applied\n        /// to the job within the given transaction.\n        /// </summary>\n        void OnStateApplied(\n            ApplyStateContext context, IWriteOnlyTransaction transaction);\n\n        /// <summary>\n        /// Called when the state with specified state was \n        /// unapplied from the job within the given transaction.\n        /// </summary>\n        void OnStateUnapplied(\n            ApplyStateContext context, IWriteOnlyTransaction transaction);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/IBackgroundJobStateChanger.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.States\n{\n    public interface IBackgroundJobStateChanger\n    {\n        /// <summary>\n        /// Attempts to change the state of a job, respecting any applicable job filters and state handlers.\n        /// </summary>\n        /// <returns><c>Null</c> if a constraint has failed, otherwise the final applied state</returns>\n        /// <remarks>Also ensures that the job data can be loaded for this job</remarks>\n        IState ChangeState(StateChangeContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/IElectStateFilter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Defines methods that are required for a state changing filter.\n    /// </summary>\n    public interface IElectStateFilter\n    {\n        /// <summary>\n        /// Called when the current state of the job is being changed to the\n        /// specified candidate state.\n        /// This state change could be intercepted and the final state could\n        /// be changed through setting the different state in the context \n        /// in an implementation of this method.\n        /// </summary>\n        void OnStateElection(ElectStateContext context);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/IState.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Provides the essential members for describing a background job state.\n    /// </summary>\n    /// <remarks>\n    /// <para>Background job processing in Hangfire is all about moving a background job\n    /// from one state to another. States are used to clearly decide what to do\n    /// with a background job. For example, <see cref=\"EnqueuedState\"/> tells\n    /// Hangfire that a job should be processed by a <see cref=\"Hangfire.Server.Worker\"/>,\n    /// and <see cref=\"FailedState\"/> tells Hangfire that a job should be investigated \n    /// by a developer.</para>\n    /// \n    /// <para>Each state has some essential properties like <see cref=\"Name\"/>,\n    /// <see cref=\"IsFinal\"/> and custom ones that are exposed through\n    /// the <see cref=\"SerializeData\"/> method. Serialized data may be used during\n    /// the processing stage.</para>\n    /// \n    /// <para>Hangfire allows you to define custom states to extend the processing\n    /// pipeline. <see cref=\"IStateHandler\"/> interface implementation can be used\n    /// to define additional work for a state transition, and \n    /// <see cref=\"Server.IBackgroundProcess\"/> interface implementation can be\n    /// used to process background jobs in a new state. For example, delayed jobs\n    /// and their <see cref=\"ScheduledState\"/>, continuations and their \n    /// <see cref=\"AwaitingState\"/> can be simply moved to an extension package.</para>\n    /// </remarks>\n    /// \n    /// <example>\n    /// <para>Let's create a new state. Consider you have background jobs that\n    /// throw a transient exception from time to time, and you want to simply\n    /// ignore those exceptions. By default, Hangfire will move a job that threw\n    /// an exception to the <see cref=\"FailedState\"/>, however a job in the <i>failed</i>\n    /// state will live in a Failed jobs page forever, unless we use <see cref=\"AutomaticRetryAttribute\"/>,\n    /// delete or retry it manually, because the <see cref=\"FailedState\"/> is not\n    /// a <i>final</i> state.</para>\n    /// \n    /// <para>Our new state will look like a <see cref=\"FailedState\"/>, but we\n    /// define the state as a <i>final</i> one, letting Hangfire to expire faulted\n    /// jobs. Please refer to the <see cref=\"IState\"/> interface properties to learn\n    /// about their details.</para>\n    /// \n    /// <para>In articles related to <see cref=\"IStateHandler\"/> and <see cref=\"IElectStateFilter\"/>\n    /// interfaces we'll discuss how to use this new state.</para>\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\States.cs\" region=\"FaultedState\" />\n    /// </example>\n    /// \n    /// <seealso cref=\"IBackgroundJobStateChanger\" />\n    /// <seealso cref=\"IStateHandler\" />\n    /// <seealso cref=\"IElectStateFilter\" />\n    /// <seealso cref=\"IApplyStateFilter\" />\n    public interface IState\n    {\n        /// <summary>\n        /// Gets the unique name of the state.\n        /// </summary>\n        /// \n        /// <value>Unique among other states string, that is ready for \n        /// ordinal comparisons.</value>\n        /// \n        /// <remarks>\n        /// <para>The state name is used to differentiate one state from another\n        /// during the state change process. So all the implemented states\n        /// should have a <b>unique</b> state name. Please use one-word names \n        /// that start with a capital letter, in a past tense in English for \n        /// your state names, for example:</para>\n        /// <list type=\"bullet\">\n        ///     <item><c>Succeeded</c></item>\n        ///     <item><c>Enqueued</c></item>\n        ///     <item><c>Deleted</c></item>\n        ///     <item><c>Failed</c></item>\n        /// </list>\n        /// \n        /// <note type=\"implement\">\n        /// The returning value should be hard-coded, no modifications of\n        /// this property should be allowed to a user. Implementors should\n        /// not add a public setter on this property.\n        /// </note>\n        /// </remarks>\n        [NotNull] string Name { get; }\n\n        /// <summary>\n        /// Gets the human-readable reason of a state transition.\n        /// </summary>\n        /// \n        /// <value>Any string with a reasonable length to fit dashboard elements.</value>\n        /// \n        /// <remarks>\n        /// <para>The reason is usually displayed in the Dashboard UI to simplify \n        /// the understanding of a background job lifecycle by providing a \n        /// human-readable text that explains why a background job is moved\n        /// to the corresponding state. Here are some examples:</para>\n        /// <list type=\"bullet\">\n        ///     <item>\n        ///         <i>Can not change the state to 'Enqueued': target \n        ///         method was not found</i>\n        ///     </item>\n        ///     <item><i>Exceeded the maximum number of retry attempts</i></item>\n        /// </list>\n        /// <note type=\"implement\">\n        /// The reason value is usually not hard-coded in a state implementation,\n        /// allowing users to change it when creating an instance of a state \n        /// through the public setter.\n        /// </note>\n        /// </remarks>\n        [CanBeNull] string Reason { get; }\n\n        /// <summary>\n        /// Gets if the current state is a <i>final</i> one.\n        /// </summary>\n        /// \n        /// <value><see langword=\"false\" /> for <i>intermediate states</i>,\n        /// and <see langword=\"true\" /> for the <i>final</i> ones.</value>\n        /// \n        /// <remarks>\n        /// <para>Final states define a termination stage of a background job \n        /// processing pipeline. Background jobs in a final state is considered \n        /// as finished with no further processing required.</para>\n        /// \n        /// <para>The <see cref=\"IBackgroundJobStateChanger\">state machine</see> marks\n        /// finished background jobs to be expired within an interval that\n        /// is defined in the <see cref=\"ApplyStateContext.JobExpirationTimeout\"/>\n        /// property that is available from a state changing filter that \n        /// implements the <see cref=\"IApplyStateFilter\"/> interface.</para>\n        /// \n        /// <note type=\"implement\">\n        /// When implementing this property, always hard-code this property to\n        /// <see langword=\"true\"/> or <see langword=\"false\" />. Hangfire does\n        /// not work with states that can be both <i>intermediate</i> and\n        /// <i>final</i> yet. Don't define a public setter for this property.\n        /// </note>\n        /// </remarks>\n        /// \n        /// <seealso cref=\"SucceededState\" />\n        /// <seealso cref=\"FailedState\" />\n        /// <seealso cref=\"DeletedState\" />\n        bool IsFinal { get; }\n\n        /// <summary>\n        /// Gets whether transition to this state should ignore job de-serialization \n        /// exceptions.\n        /// </summary>\n        /// \n        /// <value><see langword=\"false\"/> to move to the <see cref=\"FailedState\"/> on \n        /// deserialization exceptions, <see langword=\"true\" /> to ignore them.</value>\n        /// \n        /// <remarks>\n        /// <para>During a state transition, an instance of the <see cref=\"Common.Job\"/> class\n        /// is deserialized to get state changing filters, and to allow <see cref=\"IStateHandler\">\n        /// state handlers</see> to perform additional work related to the state.</para>\n        /// \n        /// <para>However we cannot always deserialize a job, for example, when job method was\n        /// removed from the code base or its assembly reference is missing. Since background\n        /// processing is impossible anyway, the <see cref=\"IBackgroundJobStateChanger\">state machine</see>\n        /// moves such a background job to the <see cref=\"FailedState\"/> in this case to\n        /// highlight a problem to the developers (because deserialization exception may\n        /// occur due to bad refactorings or other programming mistakes).</para>\n        /// \n        /// <para>However, in some exceptional cases we can ignore deserialization exceptions,\n        /// and allow a state transition for some states that does not require a <see cref=\"Common.Job\"/>\n        /// instance. <see cref=\"FailedState\"/> itself and <see cref=\"DeletedState\"/> are\n        /// examples of such a behavior.</para>\n        /// \n        /// <note type=\"implement\">\n        /// In general, implementers should return <see langword=\"false\"/> when implementing \n        /// this property.\n        /// </note>\n        /// </remarks>\n        /// \n        /// <seealso cref=\"FailedState\"/>\n        /// <seealso cref=\"DeletedState\"/>\n        bool IgnoreJobLoadException { get; }\n\n        /// <summary>\n        /// Gets a serialized representation of the current state. \n        /// </summary>\n        /// <remarks>\n        /// Returning dictionary contains the serialized properties of a state. You can obtain \n        /// the state data by using the <see cref=\"Storage.IStorageConnection.GetStateData\"/>\n        /// method. Please refer to documentation for this method in implementors to learn\n        /// which key/value pairs are available.\n        /// </remarks>\n        /// <returns>A dictionary with serialized properties of the current state.</returns>\n        [NotNull] Dictionary<string, string> SerializeData();\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/States/IStateHandler.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing Hangfire.Storage;\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Provides a mechanism for performing custom actions when applying or\n    /// unapplying the state of a background job by <see cref=\"StateMachine\"/>.\n    /// </summary>\n    public interface IStateHandler\n    {\n        /// <summary>\n        /// Gets the name of a state, for which custom actions will be\n        /// performed.\n        /// </summary>\n        string StateName { get; }\n\n        /// <summary>\n        /// Performs additional actions when applying a state whose name is\n        /// equal to the <see cref=\"StateName\"/> property.\n        /// </summary>\n        /// <param name=\"context\">The context of a state applying process.</param>\n        /// <param name=\"transaction\">The current transaction of a state applying process.</param>\n        void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction);\n\n        /// <summary>\n        /// Performs additional actions when unapplying a state whose name\n        /// is equal to the <see cref=\"StateName\"/> property.\n        /// </summary>\n        /// <param name=\"context\">The context of a state applying process.</param>\n        /// <param name=\"transaction\">The current transaction of a state applying process.</param>\n        void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/IStateMachine.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Provides a mechanism for running state election and state applying processes.\n    /// </summary>\n    /// \n    /// <seealso cref=\"StateMachine\"/>\n    public interface IStateMachine\n    {\n        /// <summary>\n        /// Performs the state applying process, where a current background job\n        /// will be moved to the elected state.\n        /// </summary>\n        /// <param name=\"context\">The context of a state applying process.</param>\n        IState ApplyState(ApplyStateContext context);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/States/ProcessingState.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Defines the <i>intermediate</i> state of a background job when a \n    /// <see cref=\"Hangfire.Server.Worker\"/> has started to process it.\n    /// </summary>\n    /// \n    /// <threadsafety static=\"true\" instance=\"false\"/>\n    public class ProcessingState : IState\n    {\n        /// <summary>\n        /// Represents the name of the <i>Processing</i> state. This field is read-only.\n        /// </summary>\n        /// <remarks>\n        /// The value of this field is <c>\"Processing\"</c>.\n        /// </remarks>\n        public static readonly string StateName = \"Processing\";\n\n        internal ProcessingState(string serverId, string workerId)\n        {\n            if (String.IsNullOrWhiteSpace(serverId)) throw new ArgumentNullException(nameof(serverId));\n\n            ServerId = serverId;\n            StartedAt = DateTime.UtcNow;\n            WorkerId = workerId;\n        }\n\n        /// <summary>\n        /// Gets a date/time when the current state instance was created.\n        /// </summary>\n        [JsonIgnore]\n        public DateTime StartedAt { get; }\n\n        /// <summary>\n        /// Gets the <i>instance id</i> of an instance of the <see cref=\"BackgroundProcessingServer\"/>\n        /// class, whose <see cref=\"Server.Worker\"/> background process started to process an \n        /// <i>enqueued</i> background job.\n        /// </summary>\n        /// <value>Usually the string representation of a GUID value, may vary in future versions.</value>\n        public string ServerId { get; }\n\n        /// <summary>\n        /// Gets the identifier of a <see cref=\"Server.Worker\"/> that started to\n        /// process an <i>enqueued</i> background job.\n        /// </summary>\n        public string WorkerId { get; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always equals to <see cref=\"StateName\"/> for the <see cref=\"ProcessingState\"/>.\n        /// Please see the remarks section of the <see cref=\"IState.Name\">IState.Name</see>\n        /// article for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public string Name => StateName;\n\n        /// <inheritdoc />\n        public string Reason { get; set; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"true\"/> for the <see cref=\"ProcessingState\"/>.\n        /// Please refer to the <see cref=\"IState.IsFinal\">IState.IsFinal</see> documentation\n        /// for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IsFinal => false;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"false\" /> for the <see cref=\"ProcessingState\"/>.\n        /// Please see the description of this property in the\n        /// <see cref=\"IState.IgnoreJobLoadException\">IState.IgnoreJobLoadException</see>\n        /// article.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IgnoreJobLoadException => false;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// <para>Returning dictionary contains the following keys. You can obtain \n        /// the state data by using the <see cref=\"Storage.IStorageConnection.GetStateData\"/>\n        /// method.</para>\n        /// <list type=\"table\">\n        ///     <listheader>\n        ///         <term>Key</term>\n        ///         <term>Type</term>\n        ///         <term>Deserialize Method</term>\n        ///         <description>Notes</description>\n        ///     </listheader>\n        ///     <item>\n        ///         <term><c>StartedAt</c></term>\n        ///         <term><see cref=\"DateTime\"/></term>\n        ///         <term><see cref=\"JobHelper.DeserializeDateTime\"/></term>\n        ///         <description>Please see the <see cref=\"StartedAt\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>ServerId</c></term>\n        ///         <term><see cref=\"string\"/></term>\n        ///         <term><i>Not required</i></term>\n        ///         <description>Please see the <see cref=\"ServerId\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>WorkerId</c></term>\n        ///         <term><see cref=\"string\"/></term>\n        ///         <term><i>Not required</i></term>\n        ///         <description>Please see the <see cref=\"WorkerId\"/> property.</description>\n        ///     </item>\n        /// </list>\n        /// </remarks>\n        public Dictionary<string, string> SerializeData()\n        {\n            return new Dictionary<string, string>\n            {\n                { \"StartedAt\", JobHelper.SerializeDateTime(StartedAt) },\n                { \"ServerId\", ServerId },\n                { \"WorkerId\", WorkerId }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/States/ScheduledState.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Defines the <i>intermediate</i> state of a background job when it is placed \n    /// on a schedule to be moved to the <see cref=\"EnqueuedState\"/> in the future \n    /// by <see cref=\"DelayedJobScheduler\"/> background process.\n    /// </summary>\n    /// \n    /// <remarks>\n    /// <para>Background job in <see cref=\"ScheduledState\"/> is referred as\n    /// <b>delayed job</b>.</para>\n    /// </remarks>\n    /// \n    /// <example>\n    /// The following example demonstrates the creation of a background job that will\n    /// be processed after two hours. Please see <see cref=\"O:Hangfire.BackgroundJob.Schedule\">BackgroundJob.Schedule</see>\n    /// and <see cref=\"O:Hangfire.BackgroundJobClientExtensions.Schedule\">BackgroundJobClientExtensions.Schedule</see>\n    /// method overloads for simpler API.\n    /// \n    /// <code lang=\"cs\" source=\"..\\Samples\\States.cs\" region=\"ScheduledState\" />\n    /// </example>\n    /// \n    /// <seealso cref=\"O:Hangfire.BackgroundJob.Schedule\">BackgroundJob.Schedule Overload</seealso>\n    /// <seealso cref=\"O:Hangfire.BackgroundJobClientExtensions.Schedule\">BackgroundJobClientExtensions.Schedule Overload</seealso>\n    /// <seealso cref=\"DelayedJobScheduler\"/>\n    /// <seealso cref=\"EnqueuedState\"/>\n    /// \n    /// <threadsafety static=\"true\" instance=\"false\"/>\n    public class ScheduledState : IState\n    {\n        /// <summary>\n        /// Represents the name of the <i>Scheduled</i> state. This field is read-only.\n        /// </summary>\n        /// <remarks>\n        /// The value of this field is <c>\"Scheduled\"</c>.\n        /// </remarks>\n        public static readonly string StateName = \"Scheduled\";\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ScheduledState\"/> class\n        /// with the specified <i>time interval</i> after which a job should be moved to\n        /// the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// <param name=\"enqueueIn\">The time interval after which a job will be\n        /// moved to the <see cref=\"EnqueuedState\"/>.</param>\n        public ScheduledState(TimeSpan enqueueIn)\n            : this(DateTime.UtcNow.Add(enqueueIn))\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ScheduledState\"/>\n        /// class with the specified <i>date/time in UTC format</i> when a job should \n        /// be moved to the <see cref=\"EnqueuedState\"/>.\n        /// </summary>\n        /// <param name=\"enqueueAt\">The date/time when a job will be moved to the \n        /// <see cref=\"EnqueuedState\"/>.</param>\n        [JsonConstructor]\n        public ScheduledState(DateTime enqueueAt)\n        {\n            EnqueueAt = enqueueAt;\n            ScheduledAt = DateTime.UtcNow;\n        }\n\n        /// <summary>\n        /// Gets a date/time when a background job should be <i>enqueued</i>.\n        /// </summary>\n        /// <value>Any date/time in <see cref=\"DateTimeKind.Utc\"/> format.</value>\n        public DateTime EnqueueAt { get; }\n\n        /// <summary>\n        /// Gets a date/time when the current state instance was created.\n        /// </summary>\n        [JsonIgnore]\n        public DateTime ScheduledAt { get; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always equals to <see cref=\"StateName\"/> for the <see cref=\"ScheduledState\"/>.\n        /// Please see the remarks section of the <see cref=\"IState.Name\">IState.Name</see>\n        /// article for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public string Name => StateName;\n\n        /// <inheritdoc />\n        public string Reason { get; set; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"false\" /> for the <see cref=\"ScheduledState\"/>.\n        /// Please refer to the <see cref=\"IState.IsFinal\">IState.IsFinal</see> documentation\n        /// for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IsFinal => false;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"false\"/> for the <see cref=\"ScheduledState\"/>.\n        /// Please see the description of this property in the\n        /// <see cref=\"IState.IgnoreJobLoadException\">IState.IgnoreJobLoadException</see>\n        /// article.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IgnoreJobLoadException => false;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// <para>Returning dictionary contains the following keys. You can obtain \n        /// the state data by using the <see cref=\"Storage.IStorageConnection.GetStateData\"/>\n        /// method.</para>\n        /// <list type=\"table\">\n        ///     <listheader>\n        ///         <term>Key</term>\n        ///         <term>Type</term>\n        ///         <term>Deserialize Method</term>\n        ///         <description>Notes</description>\n        ///     </listheader>\n        ///     <item>\n        ///         <term><c>EnqueueAt</c></term>\n        ///         <term><see cref=\"DateTime\"/></term>\n        ///         <term><see cref=\"JobHelper.DeserializeDateTime\"/></term>\n        ///         <description>Please see the <see cref=\"EnqueueAt\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>ScheduledAt</c></term>\n        ///         <term><see cref=\"DateTime\"/></term>\n        ///         <term><see cref=\"JobHelper.DeserializeDateTime\"/></term>\n        ///         <description>Please see the <see cref=\"ScheduledAt\"/> property.</description>\n        ///     </item>\n        /// </list>\n        /// </remarks>\n        public Dictionary<string, string> SerializeData()\n        {\n            return new Dictionary<string, string>\n            {\n                { \"EnqueueAt\", JobHelper.SerializeDateTime(EnqueueAt) },\n                { \"ScheduledAt\", JobHelper.SerializeDateTime(ScheduledAt) }\n            };\n        }\n\n        internal sealed class Handler : IStateHandler\n        {\n            public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)\n            {\n                if (!(context.NewState is ScheduledState scheduledState))\n                {\n                    throw new InvalidOperationException(\n                        $\"`{typeof (Handler).FullName}` state handler can be registered only for the Scheduled state.\");\n                }\n\n                CheckJobQueueFeatureIsSupported(context);\n\n                transaction.AddToSet(\n                    \"schedule\",\n                    context.BackgroundJob.Job?.Queue == null\n                        ? context.BackgroundJob.Id\n                        : context.BackgroundJob.Job.Queue + ':' + context.BackgroundJob.Id,\n                    JobHelper.ToTimestamp(scheduledState.EnqueueAt));\n            }\n\n            public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)\n            {\n                CheckJobQueueFeatureIsSupported(context);\n\n                transaction.RemoveFromSet(\n                    \"schedule\",\n                    context.BackgroundJob.Job?.Queue == null\n                        ? context.BackgroundJob.Id\n                        : context.BackgroundJob.Job.Queue + ':' + context.BackgroundJob.Id);\n            }\n\n            // ReSharper disable once MemberHidesStaticFromOuterClass\n            public string StateName => ScheduledState.StateName;\n\n            private static void CheckJobQueueFeatureIsSupported(ApplyStateContext context)\n            {\n                if (context.BackgroundJob.Job?.Queue != null && !context.Storage.HasFeature(JobStorageFeatures.JobQueueProperty))\n                {\n                    throw new NotSupportedException(\"Current storage doesn't support specifying queues directly for a specific job. Please use the QueueAttribute instead.\");\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/States/StateChangeContext.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Profiling;\nusing Hangfire.Storage;\n\nnamespace Hangfire.States\n{\n    [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Design\", \"CA1068:CancellationToken parameters must come last\", Justification = \"Cancellation tokens in this class are used only as a part of a general context and don't have usual meaning.\")]\n    public class StateChangeContext\n    {\n        public StateChangeContext(\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [NotNull] string backgroundJobId, \n            [NotNull] IState newState)\n            : this(storage, connection, backgroundJobId, newState, null)\n        {\n        }\n\n        public StateChangeContext(\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [NotNull] string backgroundJobId, \n            [NotNull] IState newState, \n            [CanBeNull] params string[] expectedStates)\n            : this(storage, connection, backgroundJobId, newState, expectedStates, CancellationToken.None)\n        {\n        }\n\n        public StateChangeContext(\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [NotNull] string backgroundJobId,\n            [NotNull] IState newState,\n            [CanBeNull] IEnumerable<string> expectedStates,\n            CancellationToken cancellationToken)\n        : this(storage, connection, null, backgroundJobId, newState, expectedStates, false, null, cancellationToken, EmptyProfiler.Instance, null)\n        {\n        }\n\n        public StateChangeContext(\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [CanBeNull] JobStorageTransaction transaction,\n            [NotNull] string backgroundJobId,\n            [NotNull] IState newState,\n            [CanBeNull] IEnumerable<string> expectedStates,\n            CancellationToken cancellationToken)\n            : this(storage, connection, transaction, backgroundJobId, newState, expectedStates, false, null, cancellationToken, EmptyProfiler.Instance, null)\n        {\n        }\n\n        internal StateChangeContext(\n            [NotNull] JobStorage storage,\n            [NotNull] IStorageConnection connection,\n            [NotNull] string backgroundJobId,\n            [NotNull] IState newState,\n            [CanBeNull] IEnumerable<string> expectedStates,\n            bool disableFilters,\n            CancellationToken cancellationToken,\n            [NotNull] IProfiler profiler,\n            [CanBeNull] string serverId,\n            [CanBeNull] IReadOnlyDictionary<string, object> customData = null) \n            : this(storage, connection, null, backgroundJobId, newState, expectedStates, disableFilters, null, cancellationToken, profiler, serverId, customData)\n        {\n        }\n\n        internal StateChangeContext(\n            [NotNull] JobStorage storage, \n            [NotNull] IStorageConnection connection,\n            [CanBeNull] JobStorageTransaction transaction,\n            [NotNull] string backgroundJobId, \n            [NotNull] IState newState, \n            [CanBeNull] IEnumerable<string> expectedStates,\n            bool disableFilters,\n            [CanBeNull] IFetchedJob completeJob,\n            CancellationToken cancellationToken,\n            [NotNull] IProfiler profiler,\n            [CanBeNull] string serverId,\n            [CanBeNull] IReadOnlyDictionary<string, object> customData = null)\n        {\n            Storage = storage ?? throw new ArgumentNullException(nameof(storage));\n            Connection = connection ?? throw new ArgumentNullException(nameof(connection));\n            Transaction = transaction;\n            BackgroundJobId = backgroundJobId ?? throw new ArgumentNullException(nameof(backgroundJobId));\n            NewState = newState ?? throw new ArgumentNullException(nameof(newState));\n            ExpectedStates = expectedStates;\n            DisableFilters = disableFilters;\n            CancellationToken = cancellationToken;\n            Profiler = profiler ?? throw new ArgumentNullException(nameof(profiler));\n            ServerId = serverId;\n            CustomData = customData;\n            CompleteJob = completeJob;\n        }\n\n        [NotNull]\n        public JobStorage Storage { get; }\n\n        [NotNull]\n        public IStorageConnection Connection { get; }\n\n        [CanBeNull]\n        public JobStorageTransaction Transaction { get; }\n\n        [NotNull]\n        public string BackgroundJobId { get; }\n\n        [NotNull]\n        public IState NewState { get; }\n\n        [CanBeNull]\n        public IEnumerable<string> ExpectedStates { get; }\n        \n        public bool DisableFilters { get; }\n        public CancellationToken CancellationToken { get; }\n\n        [NotNull]\n        internal IProfiler Profiler { get; }\n\n        [CanBeNull]\n        public IReadOnlyDictionary<string, object> CustomData { get; }\n\n        [CanBeNull]\n        public IFetchedJob CompleteJob { get; }\n\n        [CanBeNull]\n        public BackgroundJob ProcessedJob { get; set; }\n\n        [CanBeNull]\n        public string ServerId { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/StateHandlerCollection.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\n\nnamespace Hangfire.States\n{\n    [SuppressMessage(\"Naming\", \"CA1711:Identifiers should not have incorrect suffix\", Justification = \"Public API, can not change in minor versions.\")]\n    [Obsolete(\"This was a helper class, and it's not used anymore. Will be removed in 2.0.0.\")]\n    public class StateHandlerCollection\n    {\n        private readonly Dictionary<string, List<IStateHandler>> _handlers = \n            new Dictionary<string, List<IStateHandler>>();\n\n        public void AddRange(IEnumerable<IStateHandler> handlers)\n        {\n            if (handlers == null) throw new ArgumentNullException(nameof(handlers));\n\n            foreach (var handler in handlers)\n            {\n                AddHandler(handler);\n            }\n        }\n\n        public void AddHandler(IStateHandler handler)\n        {\n            if (handler == null) throw new ArgumentNullException(nameof(handler));\n            if (handler.StateName == null) throw new ArgumentException(\"The StateName property of the given state handler must be non null.\", nameof(handler));\n\n            if (!_handlers.TryGetValue(handler.StateName, out var handlers))\n            {\n                _handlers.Add(handler.StateName, handlers = new List<IStateHandler>());    \n            }\n\n            handlers.Add(handler);\n        }\n\n        public IEnumerable<IStateHandler> GetHandlers(string stateName)\n        {\n            if (stateName == null || !_handlers.TryGetValue(stateName, out var handlers))\n            {\n                return Enumerable.Empty<IStateHandler>();\n            }\n\n            return handlers.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/States/StateMachine.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Profiling;\n\nnamespace Hangfire.States\n{\n    // TODO: Merge this class with BackgroundJobStateChanger in 2.0.0\n    public class StateMachine : IStateMachine\n    {\n        private readonly IJobFilterProvider _filterProvider;\n        private readonly IStateMachine _innerStateMachine;\n\n        public StateMachine([NotNull] IJobFilterProvider filterProvider)\n            : this(filterProvider, new CoreStateMachine())\n        {\n        }\n\n        internal StateMachine(\n            [NotNull] IJobFilterProvider filterProvider,\n            [NotNull] IStateMachine innerStateMachine)\n        {\n            if (filterProvider == null) throw new ArgumentNullException(nameof(filterProvider));\n            if (innerStateMachine == null) throw new ArgumentNullException(nameof(innerStateMachine));\n\n            _filterProvider = filterProvider;\n            _innerStateMachine = innerStateMachine;\n        }\n\n        public IStateMachine InnerStateMachine => _innerStateMachine;\n\n        [System.Diagnostics.CodeAnalysis.SuppressMessage(\"Naming\", \"CA1725:Parameter names should match base declaration\", Justification = \"Parameter name changed to avoid ambiguity and errors in the method's implementation.\")]\n        public IState ApplyState(ApplyStateContext initialContext)\n        {\n            if (initialContext == null) throw new ArgumentNullException(nameof(initialContext));\n\n            var filterInfo = GetFilters(initialContext.BackgroundJob.Job);\n            var electFilters = filterInfo.ElectStateFilters;\n            var applyFilters = filterInfo.ApplyStateFilters;\n\n            // Electing a a state\n            var electContext = new ElectStateContext(initialContext, this);\n\n            foreach (var filter in electFilters)\n            {\n                electContext.Profiler.InvokeMeasured(\n                    new KeyValuePair<IElectStateFilter, ElectStateContext>(filter, electContext),\n                    InvokeOnStateElection,\n                    static ctx => $\"OnStateElection for {ctx.Value.BackgroundJob.Id}\");\n            }\n\n            foreach (var state in electContext.TraversedStates)\n            {\n                initialContext.Transaction.AddJobState(electContext.BackgroundJob.Id, state);\n            }\n\n            // Applying the elected state\n            var context = new ApplyStateContext(initialContext.Transaction, electContext)\n            {\n                JobExpirationTimeout = initialContext.JobExpirationTimeout\n            };\n\n            foreach (var filter in applyFilters)\n            {\n                context.Profiler.InvokeMeasured(\n                    new KeyValuePair<IApplyStateFilter, ApplyStateContext>(filter, context),\n                    InvokeOnStateUnapplied,\n                    static ctx => $\"OnStateUnapplied for {ctx.Value.BackgroundJob.Id}\");\n            }\n\n            foreach (var filter in applyFilters)\n            {\n                context.Profiler.InvokeMeasured(\n                    new KeyValuePair<IApplyStateFilter, ApplyStateContext>(filter, context),\n                    InvokeOnStateApplied,\n                    static ctx => $\"OnStateApplied for {ctx.Value.BackgroundJob.Id}\");\n            }\n\n            return _innerStateMachine.ApplyState(context);\n        }\n\n        private static void InvokeOnStateElection(KeyValuePair<IElectStateFilter, ElectStateContext> x)\n        {\n            try\n            {\n                x.Key.OnStateElection(x.Value);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                ex.PreserveOriginalStackTrace();\n                throw;\n            }\n        }\n\n        private static void InvokeOnStateApplied(KeyValuePair<IApplyStateFilter, ApplyStateContext> x)\n        {\n            try\n            {\n                x.Key.OnStateApplied(x.Value, x.Value.Transaction);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                ex.PreserveOriginalStackTrace();\n                throw;\n            }\n        }\n\n        private static void InvokeOnStateUnapplied(KeyValuePair<IApplyStateFilter, ApplyStateContext> x)\n        {\n            try\n            {\n                x.Key.OnStateUnapplied(x.Value, x.Value.Transaction);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                ex.PreserveOriginalStackTrace();\n                throw;\n            }\n        }\n\n        private JobFilterInfo GetFilters(Job job)\n        {\n            return new JobFilterInfo(_filterProvider.GetFilters(job));\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/States/SucceededState.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing Hangfire.Common;\nusing Hangfire.Storage;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.States\n{\n    /// <summary>\n    /// Defines the <i>final</i> state of a background job when a <see cref=\"Server.Worker\"/>\n    /// performed an <i>enqueued</i> job without any exception thrown during the performance.\n    /// </summary>\n    /// <remarks>\n    /// <para>All the transitions to the <i>Succeeded</i> state are internal for the <see cref=\"Server.Worker\"/>\n    /// background process. You can't create background jobs using this state, and can't change state\n    /// to <i>Succeeded</i>.</para>\n    /// <para>This state is used in a user code primarily in state change filters (TODO: add a link)\n    /// to add custom logic during state transitions.</para> \n    /// </remarks> \n    /// \n    /// <seealso cref=\"EnqueuedState\"/>\n    /// <seealso cref=\"Server.Worker\"/>\n    /// <seealso cref=\"IState\"/>\n    /// \n    /// <threadsafety static=\"true\" instance=\"false\" />\n    public class SucceededState : IState\n    {\n        /// <summary>\n        /// Represents the name of the <i>Succeeded</i> state. This field is read-only.\n        /// </summary>\n        /// <remarks>\n        /// The value of this field is <c>\"Succeeded\"</c>.\n        /// </remarks>\n        public static readonly string StateName = \"Succeeded\";\n\n        [JsonConstructor]\n        public SucceededState(object result, long latency, long performanceDuration)\n        {\n            SucceededAt = DateTime.UtcNow;\n            Result = result;\n            Latency = latency;\n            PerformanceDuration = performanceDuration;\n        }\n\n        /// <summary>\n        /// Gets a date/time when the current state instance was created.\n        /// </summary>\n        [JsonIgnore]\n        public DateTime SucceededAt { get; }\n\n        /// <summary>\n        /// Gets the value returned by a job method.\n        /// </summary>\n        public object Result { get; }\n        \n        /// <summary>\n        /// Gets the total number of milliseconds passed from a job\n        /// creation time till the start of the performance.\n        /// </summary>\n        public long Latency { get; }\n\n        /// <summary>\n        /// Gets the total milliseconds elapsed from a processing start.\n        /// </summary>\n        public long PerformanceDuration { get; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always equals to <see cref=\"StateName\"/> for the <see cref=\"SucceededState\"/>.\n        /// Please see the remarks section of the <see cref=\"IState.Name\">IState.Name</see>\n        /// article for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public string Name => StateName;\n\n        /// <inheritdoc />\n        public string Reason { get; set; }\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"true\"/> for the <see cref=\"SucceededState\"/>.\n        /// Please refer to the <see cref=\"IState.IsFinal\">IState.IsFinal</see> documentation\n        /// for the details.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IsFinal => true;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// Always returns <see langword=\"false\" /> for the <see cref=\"SucceededState\"/>.\n        /// Please see the description of this property in the\n        /// <see cref=\"IState.IgnoreJobLoadException\">IState.IgnoreJobLoadException</see>\n        /// article.\n        /// </remarks>\n        [JsonIgnore]\n        public bool IgnoreJobLoadException => false;\n\n        /// <inheritdoc />\n        /// <remarks>\n        /// <para>Returning dictionary contains the following keys. You can obtain \n        /// the state data by using the <see cref=\"IStorageConnection.GetStateData\"/>\n        /// method.</para>\n        /// <list type=\"table\">\n        ///     <listheader>\n        ///         <term>Key</term>\n        ///         <term>Type</term>\n        ///         <term>Deserialize Method</term>\n        ///         <description>Notes</description>\n        ///     </listheader>\n        ///     <item>\n        ///         <term><c>SucceededAt</c></term>\n        ///         <term><see cref=\"DateTime\"/></term>\n        ///         <term><see cref=\"JobHelper.DeserializeDateTime\"/></term>\n        ///         <description>Please see the <see cref=\"SucceededAt\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>PerformanceDuration</c></term>\n        ///         <term><see cref=\"long\"/></term>\n        ///         <term>\n        ///             <see cref=\"Int64.Parse(string, IFormatProvider)\"/> with \n        ///             <see cref=\"CultureInfo.InvariantCulture\"/>\n        ///         </term>\n        ///         <description>Please see the <see cref=\"PerformanceDuration\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>Latency</c></term>\n        ///         <term><see cref=\"long\"/></term>\n        ///         <term>\n        ///             <see cref=\"Int64.Parse(string, IFormatProvider)\"/> with \n        ///             <see cref=\"CultureInfo.InvariantCulture\"/>\n        ///         </term>\n        ///         <description>Please see the <see cref=\"Latency\"/> property.</description>\n        ///     </item>\n        ///     <item>\n        ///         <term><c>Result</c></term>\n        ///         <term><see cref=\"object\"/></term>\n        ///         <term><see cref=\"SerializationHelper.Serialize{T}(T, SerializationOption)\"/> with <see cref=\"SerializationOption.User\"/> argument</term>\n        ///         <description>\n        ///             <para>Please see the <see cref=\"Result\"/> property.</para>\n        ///             <para>This key may be missing from the dictionary, when the return \n        ///             value was <see langword=\"null\" />. Always check for its existence \n        ///             before using it.</para>\n        ///         </description>\n        ///     </item>\n        /// </list>\n        /// </remarks>\n        public Dictionary<string, string> SerializeData()\n        {\n            var data = new Dictionary<string, string>\n            {\n                { \"SucceededAt\",  JobHelper.SerializeDateTime(SucceededAt) },\n                { \"PerformanceDuration\", PerformanceDuration.ToString(CultureInfo.InvariantCulture) },\n                { \"Latency\", Latency.ToString(CultureInfo.InvariantCulture) }\n            };\n\n            if (Result != null)\n            {\n                string serializedResult;\n\n                try\n                {\n                    serializedResult = SerializationHelper.Serialize(Result, SerializationOption.User);\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    serializedResult = \"Can not serialize the return value\";\n                }\n\n                if (serializedResult != null)\n                {\n                    data.Add(\"Result\", serializedResult);\n                }\n            }\n\n            return data;\n        }\n\n        internal sealed class Handler : IStateHandler\n        {\n            public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)\n            {\n                transaction.IncrementCounter(\"stats:succeeded\");\n            }\n\n            public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)\n            {\n                transaction.DecrementCounter(\"stats:succeeded\");\n            }\n\n            // ReSharper disable once MemberHidesStaticFromOuterClass\n            public string StateName => SucceededState.StateName;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/StatisticsHistoryAttribute.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Globalization;\nusing Hangfire.Common;\nusing Hangfire.States;\n\nnamespace Hangfire\n{\n    public sealed class StatisticsHistoryAttribute : JobFilterAttribute, IElectStateFilter\n    {\n        public StatisticsHistoryAttribute()\n        {\n            Order = 30;\n        }\n\n        public void OnStateElection(ElectStateContext context)\n        {\n            if (context.CandidateState.Name == SucceededState.StateName)\n            {\n                context.Transaction.IncrementCounter(\n                    $\"stats:succeeded:{DateTime.UtcNow.ToString(\"yyyy-MM-dd\", CultureInfo.InvariantCulture)}\",\n                    DateTime.UtcNow.AddMonths(1) - DateTime.UtcNow);\n\n                context.Transaction.IncrementCounter(\n                    $\"stats:succeeded:{DateTime.UtcNow.ToString(\"yyyy-MM-dd-HH\", CultureInfo.InvariantCulture)}\",\n                    TimeSpan.FromDays(1));\n            }\n            else if (context.CandidateState.Name == FailedState.StateName)\n            {\n                context.Transaction.IncrementCounter(\n                    $\"stats:failed:{DateTime.UtcNow.ToString(\"yyyy-MM-dd\", CultureInfo.InvariantCulture)}\",\n                    DateTime.UtcNow.AddMonths(1) - DateTime.UtcNow);\n\n                context.Transaction.IncrementCounter(\n                    $\"stats:failed:{DateTime.UtcNow.ToString(\"yyyy-MM-dd-HH\", CultureInfo.InvariantCulture)}\",\n                    TimeSpan.FromDays(1));\n            }\n            else if (context.CandidateState is DeletedState)\n            {\n                context.Transaction.IncrementCounter(\n                    $\"stats:deleted:{DateTime.UtcNow.ToString(\"yyyy-MM-dd\", CultureInfo.InvariantCulture)}\",\n                    DateTime.UtcNow.AddMonths(1) - DateTime.UtcNow);\n\n                context.Transaction.IncrementCounter(\n                    $\"stats:deleted:{DateTime.UtcNow.ToString(\"yyyy-MM-dd-HH\", CultureInfo.InvariantCulture)}\",\n                    TimeSpan.FromDays(1));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Storage/BackgroundServerGoneException.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2018 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Runtime.Serialization;\n\nnamespace Hangfire.Storage\n{\n#if !NETSTANDARD1_3\n    [Serializable]\n#endif\n    public class BackgroundServerGoneException : Exception\n    {\n        public BackgroundServerGoneException()\n        {\n        }\n\n#if !NETSTANDARD1_3\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BackgroundServerGoneException\"/> class\n        /// with serialized data.\n        /// </summary>\n        /// <param name=\"info\">The <see cref=\"SerializationInfo\"/> that holds the serialized object data about the exception being thrown.</param>\n        /// <param name=\"context\">The <see cref=\"StreamingContext\"/> that contains contextual information about the source or destination.</param>\n        protected BackgroundServerGoneException(SerializationInfo info, StreamingContext context)\n            : base(info, context)\n        {\n        }\n#endif\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Storage/DistributedLockTimeoutException.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Runtime.Serialization;\n\nnamespace Hangfire.Storage\n{\n#if !NETSTANDARD1_3\n    [Serializable]\n#endif\n    public class DistributedLockTimeoutException : TimeoutException\n    {\n        public DistributedLockTimeoutException(string resource)\n            : base(\n                $\"Timeout expired. The timeout elapsed prior to obtaining a distributed lock on the '{resource}' resource.\"\n                )\n        {\n            Resource = resource;\n        }\n\n#if !NETSTANDARD1_3\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DistributedLockTimeoutException\"/> class\n        /// with serialized data.\n        /// </summary>\n        /// <param name=\"info\">The <see cref=\"SerializationInfo\"/> that holds the serialized object data about the exception being thrown.</param>\n        /// <param name=\"context\">The <see cref=\"StreamingContext\"/> that contains contextual information about the source or destination.</param>\n        protected DistributedLockTimeoutException(SerializationInfo info, StreamingContext context)\n            : base(info, context)\n        {\n        }\n#endif\n\n        public string Resource { get; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Storage/IFetchedJob.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.Storage\n{\n    public interface IFetchedJob : IDisposable\n    {\n        string JobId { get; }\n\n        void RemoveFromQueue();\n        void Requeue();\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/IMonitoringApi.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Storage.Monitoring;\n\nnamespace Hangfire.Storage\n{\n    public interface IMonitoringApi\n    {\n        IList<QueueWithTopEnqueuedJobsDto> Queues();\n        IList<ServerDto> Servers();\n        JobDetailsDto JobDetails(string jobId);\n        StatisticsDto GetStatistics();\n\n        JobList<EnqueuedJobDto> EnqueuedJobs(string queue, int from, int perPage);\n        JobList<FetchedJobDto> FetchedJobs(string queue, int from, int perPage);\n\n        JobList<ProcessingJobDto> ProcessingJobs(int from, int count);\n        JobList<ScheduledJobDto> ScheduledJobs(int from, int count);\n        JobList<SucceededJobDto> SucceededJobs(int from, int count);\n        JobList<FailedJobDto> FailedJobs(int from, int count);\n        JobList<DeletedJobDto> DeletedJobs(int from, int count);\n\n        long ScheduledCount();\n        long EnqueuedCount(string queue);\n        long FetchedCount(string queue);\n        long FailedCount();\n        long ProcessingCount();\n\n        long SucceededListCount();\n        long DeletedListCount();\n        \n        IDictionary<DateTime, long> SucceededByDatesCount();\n        IDictionary<DateTime, long> FailedByDatesCount();\n        IDictionary<DateTime, long> HourlySucceededJobs();\n        IDictionary<DateTime, long> HourlyFailedJobs();\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/IStorageConnection.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Server;\n\nnamespace Hangfire.Storage\n{\n    public interface IStorageConnection : IDisposable\n    {\n        IWriteOnlyTransaction CreateWriteTransaction();\n        IDisposable AcquireDistributedLock(string resource, TimeSpan timeout);\n\n        string CreateExpiredJob(\n            Job job, \n            IDictionary<string, string> parameters, \n            DateTime createdAt,\n            TimeSpan expireIn);\n\n        IFetchedJob FetchNextJob(string[] queues, CancellationToken cancellationToken);\n\n        void SetJobParameter(string id, string name, string value);\n        string GetJobParameter(string id, string name);\n\n        [CanBeNull]\n        JobData GetJobData([NotNull] string jobId);\n\n        [CanBeNull]\n        StateData GetStateData([NotNull] string jobId);\n\n        void AnnounceServer(string serverId, ServerContext context);\n        void RemoveServer(string serverId);\n        void Heartbeat(string serverId);\n        int RemoveTimedOutServers(TimeSpan timeOut);\n\n        // Set operations\n\n        [NotNull]\n        HashSet<string> GetAllItemsFromSet([NotNull] string key);\n\n        string GetFirstByLowestScoreFromSet(string key, double fromScore, double toScore);\n\n        // Hash operations\n\n        void SetRangeInHash([NotNull] string key, [NotNull] IEnumerable<KeyValuePair<string, string>> keyValuePairs);\n\n        [CanBeNull]\n        Dictionary<string, string> GetAllEntriesFromHash([NotNull] string key);\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/IWriteOnlyTransaction.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.States;\n\nnamespace Hangfire.Storage\n{\n    public interface IWriteOnlyTransaction : IDisposable\n    {\n        // Job operations\n        void ExpireJob([NotNull] string jobId, TimeSpan expireIn);\n        void PersistJob([NotNull] string jobId);\n        void SetJobState([NotNull] string jobId, [NotNull] IState state);\n        void AddJobState([NotNull] string jobId, [NotNull] IState state);\n\n        // Queue operations\n        void AddToQueue([NotNull] string queue, [NotNull] string jobId);\n\n        // Counter operations\n        void IncrementCounter([NotNull] string key);\n        void IncrementCounter([NotNull] string key, TimeSpan expireIn);\n        void DecrementCounter([NotNull] string key);\n        void DecrementCounter([NotNull] string key, TimeSpan expireIn);\n\n        // Set operations\n        void AddToSet([NotNull] string key, [NotNull] string value);\n        void AddToSet([NotNull] string key, [NotNull] string value, double score);\n        void RemoveFromSet([NotNull] string key, [NotNull] string value);\n\n        // List operations\n        void InsertToList([NotNull] string key, [NotNull] string value);\n        void RemoveFromList([NotNull] string key, [NotNull] string value);\n        void TrimList([NotNull] string key, int keepStartingFrom, int keepEndingAt);\n\n        // Hash operations\n        void SetRangeInHash([NotNull] string key, [NotNull] IEnumerable<KeyValuePair<string, string>> keyValuePairs);\n        void RemoveHash([NotNull] string key);\n\n        void Commit();\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/InvocationData.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\n#if !NETSTANDARD1_3\nusing System.ComponentModel;\n#endif\nusing System.Globalization;\nusing System.Linq;\nusing System.Reflection;\nusing System.Runtime.ExceptionServices;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.Storage\n{\n    public class InvocationData\n    {\n        private static readonly\n            ConcurrentDictionary<MethodDeserializerCacheKey, MethodDeserializerCacheValue>\n            MethodDeserializerCache = new();\n\n        private static readonly\n            ConcurrentDictionary<MethodSerializerCacheKey, MethodSerializerCacheValue>\n            MethodSerializerCache = new();\n\n        private static readonly ConcurrentDictionary<Tuple<Func<Type, string>, string>, string[]>\n            ParameterTypesDeserializerCache = new();\n\n        [Obsolete(\"Please use IGlobalConfiguration.UseTypeResolver instead. Will be removed in 2.0.0.\")]\n        public static void SetTypeResolver([CanBeNull] Func<string, Type> typeResolver)\n        {\n            TypeHelper.CurrentTypeResolver = typeResolver;\n        }\n\n        [Obsolete(\"Please use IGlobalConfiguration.UseTypeSerializer instead. Will be removed in 2.0.0.\")]\n        public static void SetTypeSerializer([CanBeNull] Func<Type, string> typeSerializer)\n        {\n            TypeHelper.CurrentTypeSerializer = typeSerializer;\n        }\n\n        public InvocationData(string type, string method, string parameterTypes, string arguments)\n            : this(type, method, parameterTypes, arguments, null)\n        {\n        }\n\n        [JsonConstructor]\n        public InvocationData(string type, string method, string parameterTypes, string arguments, string queue)\n        {\n            Type = type;\n            Method = method;\n            ParameterTypes = parameterTypes;\n            Arguments = arguments;\n            Queue = queue;\n        }\n\n        public string Type { get; }\n        public string Method { get; }\n        public string ParameterTypes { get; }\n        public string Arguments { get; set; }\n\n        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]\n        public string Queue { get; }\n\n        [Obsolete(\"Please use DeserializeJob() method instead. Will be removed in 2.0.0 for clarity.\")]\n        public Job Deserialize()\n        {\n            return DeserializeJob();\n        }\n\n        [Obsolete(\"Please use SerializeJob(Job) method instead. Will be removed in 2.0.0 for clarity.\")]\n        public static InvocationData Serialize(Job job)\n        {\n            return SerializeJob(job);\n        }\n\n        public Job DeserializeJob()\n        {\n            var typeResolver = TypeHelper.CurrentTypeResolver;\n\n            try\n            {\n                CachedDeserializeMethod(typeResolver, Type, Method, ParameterTypes, out var type, out var method);\n\n                object[] arguments;\n\n                if (Arguments != null && !Arguments.Equals(\"[]\", StringComparison.Ordinal))\n                {\n                    var argumentsArray = SerializationHelper.Deserialize<string[]>(Arguments);\n                    arguments = DeserializeArguments(method, argumentsArray);\n                }\n                else\n                {\n                    arguments = [];\n                }\n\n                return new Job(type, method, arguments, Queue);\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                throw new JobLoadException(\"Could not load the job. See inner exception for the details.\", ex);\n            }\n        }\n\n        public static InvocationData SerializeJob(Job job)\n        {\n            CachedSerializeMethod(\n                TypeHelper.CurrentTypeSerializer,\n                job.Type,\n                job.Method,\n                out var typeName,\n                out var methodName,\n                out var parameterTypes);\n\n            var arguments = job.Args.Count == 0\n                ? \"[]\"\n                : SerializationHelper.Serialize(SerializeArguments(job.Method, job.Args));\n\n            return new InvocationData(typeName, methodName, parameterTypes, arguments, job.Queue);\n        }\n\n        public static InvocationData DeserializePayload(string payload)\n        {\n            if (payload == null) throw new ArgumentNullException(nameof(payload));\n\n            JobPayload jobPayload = null;\n            Exception exception = null;\n\n            try\n            {\n                jobPayload = SerializationHelper.Deserialize<JobPayload>(payload);\n                if (jobPayload == null)\n                {\n                    throw new InvalidOperationException(\"Deserialize<JobPayload> returned `null` for a non-null payload.\");\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                exception = ex;\n            }\n\n            if (exception == null && jobPayload.TypeName != null && jobPayload.MethodName != null)\n            {\n                return new InvocationData(\n                    jobPayload.TypeName,\n                    jobPayload.MethodName,\n                    SerializationHelper.Serialize(jobPayload.ParameterTypes),\n                    SerializationHelper.Serialize(jobPayload.Arguments),\n                    jobPayload.Queue);\n            }\n\n            var data = SerializationHelper.Deserialize<InvocationData>(payload);\n            if (data == null)\n            {\n                throw new InvalidOperationException(\"Deserialize<InvocationData> returned `null` for a non-null payload.\");\n            }\n\n            if (data.Type == null || data.Method == null)\n            {\n                data = SerializationHelper.Deserialize<InvocationData>(payload, SerializationOption.User);\n            }\n\n            return data;\n        }\n\n        public string SerializePayload(bool excludeArguments = false)\n        {\n            if (GlobalConfiguration.HasCompatibilityLevel(CompatibilityLevel.Version_170))\n            {\n                var parameterTypes = DeserializeParameterTypesArray(TypeHelper.CurrentTypeSerializer, ParameterTypes);\n                var arguments = excludeArguments ? null : SerializationHelper.Deserialize<string[]>(Arguments);\n\n                return SerializationHelper.Serialize(new JobPayload\n                {\n                    TypeName = Type,\n                    MethodName = Method,\n                    ParameterTypes = parameterTypes?.Length > 0 ? parameterTypes : null,\n                    Arguments = arguments?.Length > 0 ? arguments : null,\n                    Queue = Queue\n                });\n            }\n\n            return SerializationHelper.Serialize(excludeArguments\n                ? new InvocationData(Type, Method, ParameterTypes, null, Queue)\n                : this);\n        }\n\n        private static string[] DeserializeParameterTypesArray(Func<Type, string> typeSerializer, string parameterTypes)\n        {\n            return ParameterTypesDeserializerCache.GetOrAdd(Tuple.Create(typeSerializer, parameterTypes), static tuple =>\n            {\n                try\n                {\n                    return SerializationHelper.Deserialize<string[]>(tuple.Item2);\n                }\n                catch (Exception outerException) when (outerException.IsCatchableExceptionType())\n                {\n                    try\n                    {\n                        var types = SerializationHelper.Deserialize<Type[]>(tuple.Item2);\n                        return types.Select(tuple.Item1).ToArray();\n                    }\n                    catch (Exception ex) when (ex.IsCatchableExceptionType())\n                    {\n                        ExceptionDispatchInfo.Capture(outerException).Throw();\n                        throw;\n                    }\n                }\n            });\n        }\n\n        internal static string[] SerializeArguments(MethodInfo methodInfo, IReadOnlyList<object> arguments)\n        {\n            var serializedArguments = new List<string>(arguments.Count);\n            var parameters = methodInfo.GetParameters();\n\n            for (var i = 0; i < parameters.Length; i++)\n            {\n                var parameter = parameters[i];\n                var argument = arguments[i];\n\n                string value;\n\n                if (argument != null)\n                {\n                    if (!GlobalConfiguration.HasCompatibilityLevel(CompatibilityLevel.Version_170) &&\n                        argument is DateTime dateTime)\n                    {\n                        value = dateTime.ToString(\"o\", CultureInfo.InvariantCulture);\n                    }\n                    else if (argument is CancellationToken)\n                    {\n                        // CancellationToken type instances are substituted with ShutdownToken \n                        // during the background job performance, so we don't need to store \n                        // their values.\n                        value = null;\n                    }\n                    else\n                    {\n                        value = SerializationHelper.Serialize(\n                            argument,\n                            parameter.ParameterType,\n                            SerializationOption.User);\n                    }\n                }\n                else\n                {\n                    value = null;\n                }\n\n                // Logic, related to optional parameters and their default values, \n                // can be skipped, because it is impossible to omit them in \n                // lambda-expressions (leads to a compile-time error).\n\n                serializedArguments.Add(value);\n            }\n\n            return serializedArguments.ToArray();\n        }\n\n        internal static object[] DeserializeArguments(MethodInfo methodInfo, string[] arguments)\n        {\n            if (arguments == null) return [];\n\n            var parameters = methodInfo.GetParameters();\n            var result = new List<object>(arguments.Length);\n\n            for (var i = 0; i < parameters.Length; i++)\n            {\n                var parameter = parameters[i];\n                var argument = arguments[i];\n\n                object value;\n\n                if (CoreBackgroundJobPerformer.Substitutions.ContainsKey(parameter.ParameterType))\n                {\n                    value = parameter.ParameterType.GetTypeInfo().IsValueType\n                        ? Activator.CreateInstance(parameter.ParameterType)\n                        : null;\n                }\n                else\n                {\n                    value = DeserializeArgument(argument, parameter.ParameterType);\n                }\n\n                result.Add(value);\n            }\n\n            return result.ToArray();\n        }\n\n        private static void CachedSerializeMethod(\n            Func<Type, string> typeSerializer, Type type, MethodInfo methodInfo,\n            out string typeName, out string methodName, out string parameterTypes)\n        {\n            var entry = MethodSerializerCache.GetOrAdd(\n                new MethodSerializerCacheKey { TypeSerializer = typeSerializer, Type = type, Method = methodInfo },\n                static key =>\n                {\n                    return new MethodSerializerCacheValue\n                    {\n                        TypeName = key.TypeSerializer(key.Type),\n                        MethodName = key.Method.Name,\n                        ParameterTypes = SerializationHelper.Serialize(\n                            key.Method.GetParameters().Select(x => key.TypeSerializer(x.ParameterType)).ToArray())\n                    };\n                });\n\n            typeName = entry.TypeName;\n            methodName = entry.MethodName;\n            parameterTypes = entry.ParameterTypes;\n        }\n\n        private static void CachedDeserializeMethod(\n            Func<string, Type> typeResolver, string typeName, string methodName, string parameterTypes,\n            out Type type, out MethodInfo methodInfo)\n        {\n            var entry = MethodDeserializerCache.GetOrAdd(\n                new MethodDeserializerCacheKey { TypeResolver = typeResolver, TypeName = typeName, MethodName = methodName, ParameterTypes = parameterTypes },\n                static key =>\n                {\n                    var type = key.TypeResolver(key.TypeName);\n                    var parameterTypesArray = DeserializeParameterTypesArray(TypeHelper.CurrentTypeSerializer, key.ParameterTypes);\n                    var parameterTypes = parameterTypesArray?.Select(key.TypeResolver).ToArray();\n                    var method = type.GetNonOpenMatchingMethod(key.MethodName, parameterTypes);\n\n                    if (method == null)\n                    {\n                        var parametersString = parameterTypes != null\n                            ? String.Join(\", \", parameterTypes.Select(static x => x.Name))\n                            : key.ParameterTypes ?? String.Empty;\n                    \n                        throw new InvalidOperationException(\n                            $\"The type `{type.FullName}` does not contain a method with signature `{key.MethodName}({parametersString})`\");\n                    }\n\n                    return new MethodDeserializerCacheValue { Type = type, Method = method };\n                });\n\n            type = entry.Type;\n            methodInfo = entry.Method;\n        }\n\n        private static object DeserializeArgument(string argument, Type type)\n        {\n            object value;\n            try\n            {\n                value = SerializationHelper.Deserialize(argument, type, SerializationOption.User);\n            }\n            catch (Exception jsonException) when (jsonException.IsCatchableExceptionType())\n            {\n                if (type == typeof(object))\n                {\n                    // Special case for handling object types, because string can not\n                    // be converted to object type.\n                    value = argument;\n                }\n                else if ((type == typeof(DateTime) || type == typeof(DateTime?)) && ParseDateTimeArgument(argument, out var dateTime))\n                {\n                    value = dateTime;\n                }\n                else\n                {\n#if !NETSTANDARD1_3\n                    try\n                    {\n                        var converter = TypeDescriptor.GetConverter(type);\n\n                        // ReferenceConverter can't correctly convert the serialized\n                        // data. This may happen when FromJson method threw an exception,\n                        // we should rethrow it instead of trying to deserialize.\n                        if (converter.GetType() == typeof(ReferenceConverter))\n                        {\n                            ExceptionDispatchInfo.Capture(jsonException).Throw();\n                            throw;\n                        }\n\n                        value = converter.ConvertFromInvariantString(argument);\n                    }\n                    catch (Exception ex) when (ex.IsCatchableExceptionType())\n                    {\n                        ExceptionDispatchInfo.Capture(jsonException).Throw();\n                        throw;\n                    }\n#else\n                    throw;\n#endif\n                }\n            }\n            return value;\n        }\n\n        internal static bool ParseDateTimeArgument(string argument, out DateTime value)\n        {\n            var result = DateTime.TryParseExact(\n                argument,\n                \"MM/dd/yyyy HH:mm:ss.ffff\",\n                CultureInfo.CurrentCulture,\n                DateTimeStyles.None,\n                out var dateTime);\n\n            if (!result)\n            {\n                result = DateTime.TryParse(\n                    argument,\n                    CultureInfo.InvariantCulture,\n                    DateTimeStyles.RoundtripKind,\n                    out dateTime);\n            }\n\n            value = dateTime;\n            return result;\n        }\n\n        private sealed class JobPayload\n        {\n            [JsonProperty(\"t\")]\n            public string TypeName { get; set; }\n\n            [JsonProperty(\"m\")]\n            public string MethodName { get; set; }\n\n            [JsonProperty(\"p\", NullValueHandling = NullValueHandling.Ignore)]\n            public string[] ParameterTypes { get; set; }\n\n            [JsonProperty(\"a\", NullValueHandling = NullValueHandling.Ignore)]\n            public string[] Arguments { get; set; }\n\n            [JsonProperty(\"q\", NullValueHandling = NullValueHandling.Ignore)]\n            public string Queue { get; set; }\n        }\n\n        private readonly record struct MethodDeserializerCacheKey\n        {\n            public Func<string, Type> TypeResolver { get; init; }\n            public string TypeName { get; init; }\n            public string MethodName { get; init; }\n            public string ParameterTypes { get; init; }\n        }\n\n        private readonly record struct MethodDeserializerCacheValue\n        {\n            public Type Type { get; init; }\n            public MethodInfo Method { get; init; }\n        }\n\n        private readonly record struct MethodSerializerCacheKey\n        {\n            public Func<Type, string> TypeSerializer { get; init; }\n            public Type Type { get; init; }\n            public MethodInfo Method { get; init; }\n        }\n        \n        private readonly record struct MethodSerializerCacheValue\n        {\n            public string TypeName { get; init; }\n            public string MethodName { get; init; }\n            public string ParameterTypes { get; init; }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/JobData.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage\n{\n    public class JobData\n    {\n        public string State { get; set; }\n        public Job Job { get; set; }\n        public InvocationData InvocationData { get; set; }\n        public DateTime CreatedAt { get; set; }\n        public IReadOnlyDictionary<string, string> ParametersSnapshot { get; set; }\n\n        public JobLoadException LoadException { get; set; }\n\n        public void EnsureLoaded()\n        {\n            if (LoadException != null)\n            {\n                throw LoadException;\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/JobStorageConnection.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Server;\n\nnamespace Hangfire.Storage\n{\n    public abstract class JobStorageConnection : IStorageConnection\n    {\n        public virtual void Dispose()\n        {\n            GC.SuppressFinalize(this);\n        }\n\n        // Common\n        public abstract IWriteOnlyTransaction CreateWriteTransaction();\n        public abstract IDisposable AcquireDistributedLock(string resource, TimeSpan timeout);\n\n        // Background jobs\n        public abstract string CreateExpiredJob(Job job, IDictionary<string, string> parameters, DateTime createdAt, TimeSpan expireIn);\n        public abstract IFetchedJob FetchNextJob(string[] queues, CancellationToken cancellationToken);\n        public abstract void SetJobParameter(string id, string name, string value);\n        public abstract string GetJobParameter(string id, string name);\n        public abstract JobData GetJobData(string jobId);\n        public abstract StateData GetStateData(string jobId);\n\n        // Servers\n        public abstract void AnnounceServer(string serverId, ServerContext context);\n        public abstract void RemoveServer(string serverId);\n        public abstract void Heartbeat(string serverId);\n        public abstract int RemoveTimedOutServers(TimeSpan timeOut);\n\n        // Sets\n        public abstract HashSet<string> GetAllItemsFromSet(string key);\n        public abstract string GetFirstByLowestScoreFromSet(string key, double fromScore, double toScore);\n\n        public virtual List<string> GetFirstByLowestScoreFromSet(string key, double fromScore, double toScore, int count)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Connection.BatchedGetFirstByLowest);\n        }\n\n        public virtual long GetSetCount([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual long GetSetCount([NotNull] IEnumerable<string> keys, int limit)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Connection.LimitedGetSetCount);\n        }\n\n        public virtual bool GetSetContains([NotNull] string key, [NotNull] string value)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Connection.GetSetContains);\n        }\n\n        public virtual List<string> GetRangeFromSet([NotNull] string key, int startingFrom, int endingAt)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual TimeSpan GetSetTtl([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        // Hashes\n        public abstract void SetRangeInHash(string key, IEnumerable<KeyValuePair<string, string>> keyValuePairs);\n        public abstract Dictionary<string, string> GetAllEntriesFromHash(string key);\n\n        public virtual string GetValueFromHash([NotNull] string key, [NotNull] string name)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual long GetHashCount([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual TimeSpan GetHashTtl([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        // Lists\n        public virtual long GetListCount([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual List<string> GetAllItemsFromList([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual List<string> GetRangeFromList([NotNull] string key, int startingFrom, int endingAt)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual TimeSpan GetListTtl([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        // Counters\n        public virtual long GetCounter([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual DateTime GetUtcDateTime()\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Connection.GetUtcDateTime);\n        } \n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/JobStorageFeatures.cs",
    "content": "// This file is part of Hangfire.\n// Copyright © 2023 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.Storage\n{\n    public static class JobStorageFeatures\n    {\n        internal static readonly string TransactionalAcknowledgePrefix = \"TransactionalAcknowledge:\";\n\n        public static readonly string JobQueueProperty = \"Job.Queue\";\n        public static readonly string ExtendedApi = \"Storage.ExtendedApi\";\n        public static readonly string ProcessesInsteadOfComponents = \"Storage.ProcessesInsteadOfComponents\";\n\n        public static class Connection\n        {\n            public static readonly string GetUtcDateTime = \"Connection.GetUtcDateTime\";\n            public static readonly string GetSetContains = \"Connection.GetSetContains\";\n            public static readonly string LimitedGetSetCount = \"Connection.GetSetCount.Limited\";\n\n            public static readonly string BatchedGetFirstByLowest = \"Connection.BatchedGetFirstByLowestScoreFromSet\";\n        }\n\n        public static class Transaction\n        {\n            public static readonly string AcquireDistributedLock = \"Transaction.AcquireDistributedLock\";\n\n            public static readonly string CreateJob = \"Transaction.CreateJob\";\n            public static readonly string SetJobParameter = \"Transaction.SetJobParameter\";\n\n            private static readonly ConcurrentDictionary<Type, string> RemoveFromQueueFeatureCache = new(); \n\n            public static string RemoveFromQueue(Type fetchedJobType)\n            {\n                return RemoveFromQueueFeatureCache.GetOrAdd(\n                    fetchedJobType,\n                    static type => TransactionalAcknowledgePrefix + type.Name);\n            }\n        }\n\n        public static class Monitoring\n        {\n            public static readonly string DeletedStateGraphs = \"Monitoring.DeletedStateGraphs\";\n            public static readonly string AwaitingJobs = \"Monitoring.AwaitingJobs\";\n        }\n\n        public static Exception GetNotSupportedException([NotNull] string featureId)\n        {\n            if (featureId == null) throw new ArgumentNullException(nameof(featureId));\n            return new NotSupportedException(\n                $\"Current storage implementation does not support the '{featureId}' feature.\");\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/JobStorageMonitor.cs",
    "content": "// This file is part of Hangfire.\n// Copyright © 2021 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Storage.Monitoring;\n\nnamespace Hangfire.Storage\n{\n    public abstract class JobStorageMonitor : IMonitoringApi\n    {\n        public abstract IList<QueueWithTopEnqueuedJobsDto> Queues();\n        public abstract IList<ServerDto> Servers();\n        public abstract JobDetailsDto JobDetails(string jobId);\n        public abstract StatisticsDto GetStatistics();\n        public abstract JobList<EnqueuedJobDto> EnqueuedJobs(string queue, int from, int perPage);\n        public abstract JobList<FetchedJobDto> FetchedJobs(string queue, int from, int perPage);\n        public abstract JobList<ProcessingJobDto> ProcessingJobs(int from, int count);\n        public abstract JobList<ScheduledJobDto> ScheduledJobs(int from, int count);\n        public abstract JobList<SucceededJobDto> SucceededJobs(int from, int count);\n        public abstract JobList<FailedJobDto> FailedJobs(int from, int count);\n        public abstract JobList<DeletedJobDto> DeletedJobs(int from, int count);\n\n        public virtual JobList<AwaitingJobDto> AwaitingJobs(int from, int count)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Monitoring.AwaitingJobs);\n        }\n\n        public abstract long ScheduledCount();\n        public abstract long EnqueuedCount(string queue);\n        public abstract long FetchedCount(string queue);\n        public abstract long FailedCount();\n        public abstract long ProcessingCount();\n        public abstract long SucceededListCount();\n        public abstract long DeletedListCount();\n\n        public virtual long AwaitingCount()\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Monitoring.AwaitingJobs);\n        }\n\n        public abstract IDictionary<DateTime, long> SucceededByDatesCount();\n        public abstract IDictionary<DateTime, long> FailedByDatesCount();\n\n        public virtual IDictionary<DateTime, long> DeletedByDatesCount()\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Monitoring.DeletedStateGraphs);\n        }\n\n        public abstract IDictionary<DateTime, long> HourlySucceededJobs();\n        public abstract IDictionary<DateTime, long> HourlyFailedJobs();\n\n        public virtual IDictionary<DateTime, long> HourlyDeletedJobs()\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Monitoring.DeletedStateGraphs);\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/JobStorageTransaction.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.States;\n\nnamespace Hangfire.Storage\n{\n    public abstract class JobStorageTransaction : IWriteOnlyTransaction\n    {\n        public virtual void Dispose()\n        {\n            GC.SuppressFinalize(this);\n        }\n\n        public abstract void ExpireJob(string jobId, TimeSpan expireIn);\n        public abstract void PersistJob(string jobId);\n        public abstract void SetJobState(string jobId, IState state);\n        public abstract void AddJobState(string jobId, IState state);\n        public abstract void AddToQueue(string queue, string jobId);\n        public abstract void IncrementCounter(string key);\n        public abstract void IncrementCounter(string key, TimeSpan expireIn);\n        public abstract void DecrementCounter(string key);\n        public abstract void DecrementCounter(string key, TimeSpan expireIn);\n        public abstract void AddToSet(string key, string value);\n        public abstract void AddToSet(string key, string value, double score);\n        public abstract void RemoveFromSet(string key, string value);\n        public abstract void InsertToList(string key, string value);\n        public abstract void RemoveFromList(string key, string value);\n        public abstract void TrimList(string key, int keepStartingFrom, int keepEndingAt);\n        public abstract void SetRangeInHash(string key, IEnumerable<KeyValuePair<string, string>> keyValuePairs);\n        public abstract void RemoveHash(string key);\n        public abstract void Commit();\n\n        public virtual void ExpireSet([NotNull] string key, TimeSpan expireIn)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual void ExpireList([NotNull] string key, TimeSpan expireIn)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual void ExpireHash([NotNull] string key, TimeSpan expireIn)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual void PersistSet([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual void PersistList([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual void PersistHash([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual void AddRangeToSet([NotNull] string key, [NotNull] IList<string> items)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual void RemoveSet([NotNull] string key)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.ExtendedApi);\n        }\n\n        public virtual void AcquireDistributedLock([NotNull] string resource, TimeSpan timeout)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Transaction.AcquireDistributedLock);\n        }\n\n        public virtual void RemoveFromQueue([NotNull] IFetchedJob fetchedJob)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Transaction.RemoveFromQueue(fetchedJob.GetType()));\n        }\n        \n        public virtual void SetJobParameter([NotNull] string jobId, [NotNull] string name, [CanBeNull] string value)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Transaction.SetJobParameter);\n        }\n\n        public virtual string CreateJob([NotNull] Job job, [NotNull] IDictionary<string, string> parameters, DateTime createdAt, TimeSpan expireIn)\n        {\n            throw JobStorageFeatures.GetNotSupportedException(JobStorageFeatures.Transaction.CreateJob);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/AwaitingJobDto.cs",
    "content": "// This file is part of Hangfire. Copyright © 2022 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class AwaitingJobDto\n    {\n        public AwaitingJobDto()\n        {\n            InAwaitingState = true;\n        }\n\n        public Job Job { get; set; }\n        public JobLoadException LoadException { get; set; }\n        public InvocationData InvocationData { get; set; }\n        public DateTime? AwaitingAt { get; set; }\n        public bool InAwaitingState { get; set; }\n        public IDictionary<string, string> StateData { get; set; }\n        public string ParentStateName { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/DeletedJobDto.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class DeletedJobDto\n    {\n        public DeletedJobDto()\n        {\n            InDeletedState = true;\n        }\n\n        public Job Job { get; set; }\n        public JobLoadException LoadException { get; set; }\n        public InvocationData InvocationData { get; set; }\n        public DateTime? DeletedAt { get; set; }\n        public bool InDeletedState { get; set; }\n        public IDictionary<string, string> StateData { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/EnqueuedJobDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class EnqueuedJobDto\n    {\n        public EnqueuedJobDto()\n        {\n            InEnqueuedState = true;\n        }\n\n        public Job Job { get; set; }\n        public JobLoadException LoadException { get; set; }\n        public InvocationData InvocationData { get; set; }\n        public string State { get; set; }\n        public DateTime? EnqueuedAt { get; set; }\n        public bool InEnqueuedState { get; set; }\n        public IDictionary<string, string> StateData { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/FailedJobDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class FailedJobDto\n    {\n        public FailedJobDto()\n        {\n            InFailedState = true;\n        }\n\n        public Job Job { get; set; }\n        public JobLoadException LoadException { get; set; }\n        public InvocationData InvocationData { get; set; }\n        public string Reason { get; set; }\n        public DateTime? FailedAt { get; set; }\n        public string ExceptionType { get; set; }\n        public string ExceptionMessage { get; set; }\n        public string ExceptionDetails { get; set; }\n        public bool InFailedState { get; set; }\n        public IDictionary<string, string> StateData { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/FetchedJobDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class FetchedJobDto\n    {\n        public Job Job { get; set; }\n        public JobLoadException LoadException { get; set; }\n        public InvocationData InvocationData { get; set; }\n        public string State { get; set; }\n        public DateTime? FetchedAt { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/JobDetailsDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class JobDetailsDto\n    {\n        public Job Job { get; set; }\n        public JobLoadException LoadException { get; set; }\n        public InvocationData InvocationData { get; set; }\n        public DateTime? CreatedAt { get; set; }\n        public IDictionary<string, string> Properties { get; set; }\n        public IList<StateHistoryDto> History { get; set; }\n        public DateTime? ExpireAt { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/JobList.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class JobList<TDto> : List<KeyValuePair<string, TDto>>\n    {\n        public JobList(IEnumerable<KeyValuePair<string, TDto>> source)\n            : base(source)\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/ProcessingJobDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class ProcessingJobDto\n    {\n        public ProcessingJobDto()\n        {\n            InProcessingState = true;\n        }\n\n        public Job Job { get; set; }\n        public JobLoadException LoadException { get; set; }\n        public InvocationData InvocationData { get; set; }\n        public bool InProcessingState { get; set; }\n        public string ServerId { get; set; }\n        public DateTime? StartedAt { get; set; }\n        public IDictionary<string, string> StateData { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/QueueWithTopEnqueuedJobsDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class QueueWithTopEnqueuedJobsDto\n    {\n        public string Name { get; set; }\n        public long Length { get; set; }\n        public long? Fetched { get; set; }\n        public JobList<EnqueuedJobDto> FirstJobs { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/ScheduledJobDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class ScheduledJobDto\n    {\n        public ScheduledJobDto()\n        {\n            InScheduledState = true;\n        }\n\n        public Job Job { get; set; }\n        public JobLoadException LoadException { get; set; }\n        public InvocationData InvocationData { get; set; }\n        public DateTime EnqueueAt { get; set; }\n        public DateTime? ScheduledAt { get; set; }\n        public bool InScheduledState { get; set; }\n        public IDictionary<string, string> StateData { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/ServerDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class ServerDto\n    {\n        public string Name { get; set; }\n        public int WorkersCount { get; set; }\n        public DateTime StartedAt { get; set; }\n        public IList<string> Queues { get; set; }\n        public DateTime? Heartbeat { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/StateHistoryDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class StateHistoryDto\n    {\n        public string StateName { get; set; }\n        public string Reason { get; set; }\n        public DateTime CreatedAt { get; set; }\n        public IDictionary<string, string> Data { get; set; } \n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/StatisticsDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class StatisticsDto\n    {\n        public long Servers { get; set; }\n        public long Recurring { get; set; }\n        public long Enqueued { get; set; }\n        public long Queues { get; set; }\n        public long Scheduled { get; set; }\n        public long Processing { get; set; }\n        public long Succeeded { get; set; }\n        public long Failed { get; set; }\n        public long Deleted { get; set; }\n        public long? Retries { get; set; }\n        public long? Awaiting { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Storage/Monitoring/SucceededJobDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage.Monitoring\n{\n    public class SucceededJobDto\n    {\n        public SucceededJobDto()\n        {\n            InSucceededState = true;\n        }\n\n        public Job Job { get; set; }\n        public JobLoadException LoadException { get; set; }\n        public InvocationData InvocationData { get; set; }\n        public object Result { get; set; }\n        public long? TotalDuration { get; set; }\n        public DateTime? SucceededAt { get; set; }\n        public bool InSucceededState { get; set; }\n        public IDictionary<string, string> StateData { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/RecurringJobDto.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage\n{\n    public class RecurringJobDto\n    {\n        public string Id { get; set; }\n        public string Cron { get; set; }\n        public string Queue { get; set; }\n        public Job Job { get; set; }\n        public JobLoadException LoadException { get; set; }\n        public DateTime? NextExecution { get; set; }\n        public string LastJobId { get; set; }\n        public string LastJobState { get; set; }\n        public DateTime? LastExecution { get; set; }\n        public DateTime? CreatedAt { get; set; }\n        public bool Removed { get; set; }\n        public string TimeZoneId { get; set; }\n        public string Error { get; set; }\n        public int RetryAttempt { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/Storage/StateData.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\n\n// ReSharper disable NotNullMemberIsNotInitialized - Let's trust them!\n\nnamespace Hangfire.Storage\n{\n    public class StateData\n    {\n        [NotNull]\n        public string Name { get; set; }\n\n        [CanBeNull]\n        public string Reason { get; set; }\n\n        [NotNull]\n        public IDictionary<string, string> Data { get; set; } \n    }\n}"
  },
  {
    "path": "src/Hangfire.Core/Storage/StorageConnectionExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\n\nnamespace Hangfire.Storage\n{\n    public static class StorageConnectionExtensions\n    {\n        public static IDisposable AcquireDistributedJobLock(\n            [NotNull] this IStorageConnection connection, \n            [NotNull] string jobId, \n            TimeSpan timeout)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n\n            return connection.AcquireDistributedLock(\n                $\"job:{jobId}:state-lock\",\n                timeout);\n        }\n\n        public static void AcquireDistributedJobLock(\n            [NotNull] this JobStorageTransaction transaction, \n            [NotNull] string jobId, \n            TimeSpan timeout)\n        {\n            if (transaction == null) throw new ArgumentNullException(nameof(transaction));\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n\n            transaction.AcquireDistributedLock($\"job:{jobId}:state-lock\", timeout);\n        }\n\n        public static long GetRecurringJobCount([NotNull] this JobStorageConnection connection)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            return connection.GetSetCount(\"recurring-jobs\");\n        }\n\n        public static List<string> GetRecurringJobIds(\n            [NotNull] this JobStorageConnection connection,\n            int startingFrom,\n            int endingAt)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            return connection.GetRangeFromSet(\"recurring-jobs\", startingFrom, endingAt);\n        }\n\n        public static List<RecurringJobDto> GetRecurringJobs(\n            [NotNull] this JobStorageConnection connection,\n            int startingFrom,\n            int endingAt)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n\n            var ids = connection.GetRecurringJobIds(startingFrom, endingAt);\n            return GetRecurringJobDtos(connection, ids);\n        }\n\n        public static List<RecurringJobDto> GetRecurringJobs([NotNull] this IStorageConnection connection)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n\n            var ids = connection.GetAllItemsFromSet(\"recurring-jobs\");\n            return GetRecurringJobDtos(connection, ids);\n        }\n\n        public static List<RecurringJobDto> GetRecurringJobs([NotNull] this IStorageConnection connection, IEnumerable<string> ids)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            return GetRecurringJobDtos(connection, ids);\n        }\n\n        private static List<RecurringJobDto> GetRecurringJobDtos(IStorageConnection connection, IEnumerable<string> ids)\n        {\n            var result = new List<RecurringJobDto>();\n            foreach (var id in ids)\n            {\n                var hash = connection.GetAllEntriesFromHash($\"recurring-job:{id}\");\n\n                // TODO: Remove this in 2.0 (breaking change)\n                if (hash == null)\n                {\n                    result.Add(new RecurringJobDto { Id = id, Removed = true });\n                    continue;\n                }\n\n                var dto = new RecurringJobDto { Id = id };\n\n                if (hash.TryGetValue(\"Cron\", out var cron) && !String.IsNullOrWhiteSpace(cron))\n                {\n                    dto.Cron = cron;\n                }\n\n                try\n                {\n                    if (hash.TryGetValue(\"Job\", out var payload) && !String.IsNullOrWhiteSpace(payload))\n                    {\n                        var invocationData = InvocationData.DeserializePayload(payload);\n                        dto.Job = invocationData.DeserializeJob();\n                    }\n                }\n                catch (JobLoadException ex)\n                {\n                    dto.LoadException = ex;\n                }\n\n                if (hash.TryGetValue(\"NextExecution\", out var nextExecution))\n                {\n                    dto.NextExecution = JobHelper.DeserializeNullableDateTime(nextExecution);\n                }\n\n                if (hash.TryGetValue(\"LastJobId\", out var lastJobId) && !string.IsNullOrWhiteSpace(lastJobId))\n                {\n                    dto.LastJobId = lastJobId;\n\n                    var stateData = connection.GetStateData(dto.LastJobId);\n                    if (stateData != null)\n                    {\n                        dto.LastJobState = stateData.Name;\n                    }\n                }\n                \n                if (hash.TryGetValue(\"Queue\", out var queue))\n                {\n                    dto.Queue = queue;\n                }\n\n                if (hash.TryGetValue(\"LastExecution\", out var lastExecution))\n                {\n                    dto.LastExecution = JobHelper.DeserializeNullableDateTime(lastExecution);\n                }\n\n                if (hash.TryGetValue(\"TimeZoneId\", out var timeZoneId))\n                {\n                    dto.TimeZoneId = timeZoneId;\n                }\n\n                if (hash.TryGetValue(\"CreatedAt\", out var createdAt))\n                {\n                    dto.CreatedAt = JobHelper.DeserializeNullableDateTime(createdAt);\n                }\n\n                if (hash.TryGetValue(\"Error\", out var error) && !String.IsNullOrEmpty(error))\n                {\n                    dto.Error = error;\n                }\n\n                if (hash.TryGetValue(\"RetryAttempt\", out var attemptString) &&\n                    Int32.TryParse(attemptString, out var retryAttempt))\n                {\n                    dto.RetryAttempt = retryAttempt;\n                }\n                else\n                {\n                    dto.RetryAttempt = 0;\n                }\n\n                result.Add(dto);\n            }\n            \n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.Core/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \".NETFramework,Version=v4.5.1\": {\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.21.0, )\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[0.11.1, )\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"LibLog\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.5.0, )\",\n        \"resolved\": \"1.5.0\",\n        \"contentHash\": \"0bNoz50GPHYnS3fv/qTS2dFBeYgoppM1t//gWULGxY/TM5TNxxknUWqOqzE8eZgLIa2ENfIwzumvbeWFnx14rg==\"\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net451\": \"1.0.3\"\n        }\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.2.3, )\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"MoreLinq.Source.MoreEnumerable.Pairwise\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.1, )\",\n        \"resolved\": \"1.0.1\",\n        \"contentHash\": \"N1bmYqxItSwixZ72KJ/V4e3mEnIO5AtMcmjtilqe+Ulp4GpSje9DP+1Cv4j01s3uG4h7P4G1axUEODL1yWB5Og==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[5.0.1, )\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"Owin\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.0, )\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"StackTraceFormatter.Source\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.1.0, )\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"69Zxg50Sm1kq9hvxPgEo0w9li8oiD2qVT87GGsqXJP2vfYs33LVKGHRMSP2Z4iTP3UC0PJTeurCeWY2t7X/76Q==\",\n        \"dependencies\": {\n          \"MoreLinq.Source.MoreEnumerable.Pairwise\": \"[1.0.1, 2.0.0)\",\n          \"StackTraceParser.Source\": \"[1.2.0, 2.0.0)\"\n        }\n      },\n      \"StackTraceParser.Source\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.3.0, )\",\n        \"resolved\": \"1.3.0\",\n        \"contentHash\": \"tOdf1XpHE2YQJpBERplZA2kMTJQtJnKzSc7GMO/yeNNGuXNueVqySfIr/gfU1vx9k2FO8u0zD4mtnZGwKQM+Zg==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net451\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vVPinxdLrwoX81ApbNIHDBI6qymQEy8eSOxDNBgKJtc2+cifnF0oT1U2d3EFx+V5O68yaqna2myZJNsgKCpVkA==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      }\n    },\n    \".NETFramework,Version=v4.6\": {\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.21.0, )\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[0.11.1, )\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"LibLog\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.5.0, )\",\n        \"resolved\": \"1.5.0\",\n        \"contentHash\": \"0bNoz50GPHYnS3fv/qTS2dFBeYgoppM1t//gWULGxY/TM5TNxxknUWqOqzE8eZgLIa2ENfIwzumvbeWFnx14rg==\"\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net46\": \"1.0.3\"\n        }\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.2.3, )\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"MoreLinq.Source.MoreEnumerable.Pairwise\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.1, )\",\n        \"resolved\": \"1.0.1\",\n        \"contentHash\": \"N1bmYqxItSwixZ72KJ/V4e3mEnIO5AtMcmjtilqe+Ulp4GpSje9DP+1Cv4j01s3uG4h7P4G1axUEODL1yWB5Og==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[5.0.1, )\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"Owin\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.0, )\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"StackTraceFormatter.Source\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.1.0, )\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"69Zxg50Sm1kq9hvxPgEo0w9li8oiD2qVT87GGsqXJP2vfYs33LVKGHRMSP2Z4iTP3UC0PJTeurCeWY2t7X/76Q==\",\n        \"dependencies\": {\n          \"MoreLinq.Source.MoreEnumerable.Pairwise\": \"[1.0.1, 2.0.0)\",\n          \"StackTraceParser.Source\": \"[1.2.0, 2.0.0)\"\n        }\n      },\n      \"StackTraceParser.Source\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.3.0, )\",\n        \"resolved\": \"1.3.0\",\n        \"contentHash\": \"tOdf1XpHE2YQJpBERplZA2kMTJQtJnKzSc7GMO/yeNNGuXNueVqySfIr/gfU1vx9k2FO8u0zD4mtnZGwKQM+Zg==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net46\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"NiiK1wNEKbDFxF08ybLkjwEn8g6SuIrUl/Y+jlAMcf3NsTtlU0JFRWFPJENV9yyZkW6nBxk/pniBlyZtE5fHwQ==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      }\n    },\n    \".NETStandard,Version=v1.3\": {\n      \"Cronos\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[0.11.1, )\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"LibLog\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.5.0, )\",\n        \"resolved\": \"1.5.0\",\n        \"contentHash\": \"0bNoz50GPHYnS3fv/qTS2dFBeYgoppM1t//gWULGxY/TM5TNxxknUWqOqzE8eZgLIa2ENfIwzumvbeWFnx14rg==\"\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"MoreLinq.Source.MoreEnumerable.Pairwise\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.1, )\",\n        \"resolved\": \"1.0.1\",\n        \"contentHash\": \"N1bmYqxItSwixZ72KJ/V4e3mEnIO5AtMcmjtilqe+Ulp4GpSje9DP+1Cv4j01s3uG4h7P4G1axUEODL1yWB5Og==\"\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.6.1, )\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.1, )\",\n        \"resolved\": \"9.0.1\",\n        \"contentHash\": \"U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==\",\n        \"dependencies\": {\n          \"Microsoft.CSharp\": \"4.0.1\",\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Dynamic.Runtime\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.IO\": \"4.1.0\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Extensions\": \"4.0.1\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.Serialization.Primitives\": \"4.1.1\",\n          \"System.Text.Encoding\": \"4.0.11\",\n          \"System.Text.Encoding.Extensions\": \"4.0.11\",\n          \"System.Text.RegularExpressions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\",\n          \"System.Threading.Tasks\": \"4.0.11\",\n          \"System.Xml.ReaderWriter\": \"4.0.11\",\n          \"System.Xml.XDocument\": \"4.0.11\"\n        }\n      },\n      \"StackTraceFormatter.Source\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.1.0, )\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"69Zxg50Sm1kq9hvxPgEo0w9li8oiD2qVT87GGsqXJP2vfYs33LVKGHRMSP2Z4iTP3UC0PJTeurCeWY2t7X/76Q==\",\n        \"dependencies\": {\n          \"MoreLinq.Source.MoreEnumerable.Pairwise\": \"[1.0.1, 2.0.0)\",\n          \"StackTraceParser.Source\": \"[1.2.0, 2.0.0)\"\n        }\n      },\n      \"StackTraceParser.Source\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.3.0, )\",\n        \"resolved\": \"1.3.0\",\n        \"contentHash\": \"tOdf1XpHE2YQJpBERplZA2kMTJQtJnKzSc7GMO/yeNNGuXNueVqySfIr/gfU1vx9k2FO8u0zD4mtnZGwKQM+Zg==\"\n      },\n      \"System.Threading.Thread\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.0.0, )\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"gIdJqDXlOr5W9zeqFErLw3dsOsiShSCYtF9SEHitACycmvNvY8odf9kiKvp6V7aibc8C4HzzNBkWXjyfn7plbQ==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Threading.ThreadPool\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.0.10, )\",\n        \"resolved\": \"4.0.10\",\n        \"contentHash\": \"IMXgB5Vf/5Qw1kpoVgJMOvUO1l32aC+qC3OaIZjWJOjvcxuxNWOK2ZTWWYXfij22NHxT2j1yWX5vlAeQWld9vA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Handles\": \"4.0.1\"\n        }\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"17h8b5mXa87XYKrrVqdgZ38JefSUqLChUQpXgSnpzsM0nDOhE40FTeNWOJ/YmySGV6tG6T8+hjz6vxbknHJr6A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Dynamic.Runtime\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Extensions\": \"4.0.1\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Reflection.TypeExtensions\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.InteropServices\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Dynamic.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.11\",\n        \"contentHash\": \"db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Emit\": \"4.0.1\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.0.1\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Reflection.TypeExtensions\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"P2wqAj72fFjpP6wb9nSfDqNBMab+2ovzSDzUZK7MVIm54tBJEPr9jWfSjjoTpPwj1LeKcmX3vr0ttyjSSFM47g==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.0.1\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"Ov6dU8Bu15Bc7zuqttgHF12J5lwSWyTf1S+FJouUXVMSqImLZzYaQ+vRr1rQ0OZ0HqsrwWl4dsKHELckQkVpgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"tsQ/ptQ3H5FYfON8lL4MxRk/8kFyE0A+tGPXmVP967cT/gzLHYxIejIYSxp4JmIeFHVP78g/F2FE1mUUTbDtrg==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Serialization.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.1\",\n        \"contentHash\": \"HZ6Du5QrTG8MNJbf4e4qMO3JRAkIboGT5Fk804uZtg3Gq516S7hAqTm2UZKUHa7/6HUGdVy3AqMQKbns06G/cg==\",\n        \"dependencies\": {\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      }\n    },\n    \".NETStandard,Version=v2.0\": {\n      \"Cronos\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[0.11.1, )\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"LibLog\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.5.0, )\",\n        \"resolved\": \"1.5.0\",\n        \"contentHash\": \"0bNoz50GPHYnS3fv/qTS2dFBeYgoppM1t//gWULGxY/TM5TNxxknUWqOqzE8eZgLIa2ENfIwzumvbeWFnx14rg==\"\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.4.0, )\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"vvVR/B08YVghQ4jHEloxqw2ZWzEGE1AOA5E0DioUM3ujbXz6FD3AfB/0Jl2ohJPd0nXYGwmPe1En6HTsSriq1A==\"\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"MoreLinq.Source.MoreEnumerable.Pairwise\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.1, )\",\n        \"resolved\": \"1.0.1\",\n        \"contentHash\": \"N1bmYqxItSwixZ72KJ/V4e3mEnIO5AtMcmjtilqe+Ulp4GpSje9DP+1Cv4j01s3uG4h7P4G1axUEODL1yWB5Og==\"\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.3, )\",\n        \"resolved\": \"2.0.3\",\n        \"contentHash\": \"st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[11.0.1, )\",\n        \"resolved\": \"11.0.1\",\n        \"contentHash\": \"pNN4l+J6LlpIvHOeNdXlwxv39NPJ2B5klz+Rd2UQZIx30Squ5oND1Yy3wEAUoKn0GPUj6Yxt9lxlYWQqfZcvKg==\"\n      },\n      \"StackTraceFormatter.Source\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.1.0, )\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"69Zxg50Sm1kq9hvxPgEo0w9li8oiD2qVT87GGsqXJP2vfYs33LVKGHRMSP2Z4iTP3UC0PJTeurCeWY2t7X/76Q==\",\n        \"dependencies\": {\n          \"MoreLinq.Source.MoreEnumerable.Pairwise\": \"[1.0.1, 2.0.0)\",\n          \"StackTraceParser.Source\": \"[1.2.0, 2.0.0)\"\n        }\n      },\n      \"StackTraceParser.Source\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.3.0, )\",\n        \"resolved\": \"1.3.0\",\n        \"contentHash\": \"tOdf1XpHE2YQJpBERplZA2kMTJQtJnKzSc7GMO/yeNNGuXNueVqySfIr/gfU1vx9k2FO8u0zD4mtnZGwKQM+Zg==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/Hangfire.NetCore/AspNetCore/AspNetCoreJobActivator.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace Hangfire.AspNetCore\n{\n    public class AspNetCoreJobActivator : JobActivator\n    {\n        private readonly IServiceScopeFactory _serviceScopeFactory;\n\n        public AspNetCoreJobActivator([NotNull] IServiceScopeFactory serviceScopeFactory)\n        {\n            if (serviceScopeFactory == null) throw new ArgumentNullException(nameof(serviceScopeFactory));\n            _serviceScopeFactory = serviceScopeFactory;\n        }\n\n        public override JobActivatorScope BeginScope(JobActivatorContext context)\n        {\n            return new AspNetCoreJobActivatorScope(_serviceScopeFactory.CreateScope());\n        }\n\n#pragma warning disable CS0672 // Member overrides obsolete member\n        public override JobActivatorScope BeginScope()\n#pragma warning restore CS0672 // Member overrides obsolete member\n        {\n            return new AspNetCoreJobActivatorScope(_serviceScopeFactory.CreateScope());\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.NetCore/AspNetCore/AspNetCoreJobActivatorScope.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace Hangfire.AspNetCore\n{\n    internal sealed class AspNetCoreJobActivatorScope : JobActivatorScope\n    {\n        private readonly IServiceScope _serviceScope;\n\n        public AspNetCoreJobActivatorScope([NotNull] IServiceScope serviceScope)\n        {\n            if (serviceScope == null) throw new ArgumentNullException(nameof(serviceScope));\n            _serviceScope = serviceScope;\n        }\n\n        public override object Resolve(Type type)\n        {\n            return ActivatorUtilities.GetServiceOrCreateInstance(_serviceScope.ServiceProvider, type);\n        }\n\n        public override void DisposeScope()\n        {\n#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1\n            if (_serviceScope is IAsyncDisposable asyncDisposable)\n            {\n                // Service scope disposal is triggered inside a dedicated background thread,\n                // while Task result is being set in CLR's Thread Pool, so no deadlocks on\n                // wait should happen.\n#pragma warning disable CA2012\n                asyncDisposable.DisposeAsync().ConfigureAwait(false).GetAwaiter().GetResult();\n#pragma warning restore CA2012\n                return;\n            }\n#endif\n            _serviceScope.Dispose();\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.NetCore/AspNetCore/AspNetCoreLog.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing Microsoft.Extensions.Logging;\nusing LogLevel = Microsoft.Extensions.Logging.LogLevel;\n\nnamespace Hangfire.AspNetCore\n{\n    internal sealed class AspNetCoreLog : ILog\n    {\n        private static readonly Func<object, Exception, string> MessageFormatterFunc = MessageFormatter;\n\n        private readonly ILogger _targetLogger;\n\n        public AspNetCoreLog([NotNull] ILogger targetLogger)\n        {\n            if (targetLogger == null) throw new ArgumentNullException(nameof(targetLogger));\n            _targetLogger = targetLogger;\n        }\n\n        public bool Log(Logging.LogLevel logLevel, Func<string> messageFunc, Exception exception = null)\n        {\n            var targetLogLevel = ToTargetLogLevel(logLevel);\n\n            // When messageFunc is null, Hangfire.Logging\n            // just determines is logging enabled.\n            if (messageFunc == null)\n            {\n                return _targetLogger.IsEnabled(targetLogLevel);\n            }\n\n            _targetLogger.Log(targetLogLevel, 0, messageFunc(), exception, MessageFormatterFunc);\n            return true;\n        }\n\n        private static LogLevel ToTargetLogLevel(Logging.LogLevel logLevel)\n        {\n            switch (logLevel)\n            {\n                case Logging.LogLevel.Trace:\n                    return LogLevel.Trace;\n                case Logging.LogLevel.Debug:\n                    return LogLevel.Debug;\n                case Logging.LogLevel.Info:\n                    return LogLevel.Information;\n                case Logging.LogLevel.Warn:\n                    return LogLevel.Warning;\n                case Logging.LogLevel.Error:\n                    return LogLevel.Error;\n                case Logging.LogLevel.Fatal:\n                    return LogLevel.Critical;\n            }\n\n            return LogLevel.None;\n        }\n\n        private static string MessageFormatter(object state, Exception exception)\n        {\n            return state.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.NetCore/AspNetCore/AspNetCoreLogProvider.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing Microsoft.Extensions.Logging;\n\nnamespace Hangfire.AspNetCore\n{\n    public class AspNetCoreLogProvider : ILogProvider\n    {\n        private readonly ILoggerFactory _loggerFactory;\n\n        public AspNetCoreLogProvider([NotNull] ILoggerFactory loggerFactory)\n        {\n            if (loggerFactory == null) throw new ArgumentNullException(nameof(loggerFactory));\n            _loggerFactory = loggerFactory;\n        }\n\n        public ILog GetLogger(string name)\n        {\n            return new AspNetCoreLog(_loggerFactory.CreateLogger(name));\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.NetCore/BackgroundJobServerHostedService.cs",
    "content": "// This file is part of Hangfire. Copyright © 2019 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\n#if !NET451 && !NETSTANDARD1_3\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Server;\nusing Hangfire.States;\nusing Microsoft.Extensions.Hosting;\n\nnamespace Hangfire\n{\n    public class BackgroundJobServerHostedService : IHostedService, IDisposable\n    {\n        private readonly BackgroundJobServerOptions _options;\n        private readonly JobStorage _storage;\n        private readonly IEnumerable<IBackgroundProcess> _additionalProcesses;\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\n        private readonly IHostApplicationLifetime _hostApplicationLifetime;\n#endif\n        private readonly IBackgroundJobFactory _factory;\n        private readonly IBackgroundJobPerformer _performer;\n        private readonly IBackgroundJobStateChanger _stateChanger;\n\n        private IBackgroundProcessingServer _processingServer;\n\n        public BackgroundJobServerHostedService(\n            [NotNull] JobStorage storage,\n            [NotNull] BackgroundJobServerOptions options,\n            [NotNull] IEnumerable<IBackgroundProcess> additionalProcesses)\n#pragma warning disable 618\n            : this(storage, options, additionalProcesses, null, null, null)\n#pragma warning restore 618\n        {\n        }\n\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\n        public BackgroundJobServerHostedService(\n            [NotNull] JobStorage storage,\n            [NotNull] BackgroundJobServerOptions options,\n            [NotNull] IEnumerable<IBackgroundProcess> additionalProcesses,\n            [CanBeNull] IHostApplicationLifetime hostApplicationLifetime)\n#pragma warning disable 618\n            : this(storage, options, additionalProcesses, null, null, null, hostApplicationLifetime)\n#pragma warning restore 618\n        {\n        }\n#endif\n\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\n        [Obsolete(\"This constructor uses an obsolete constructor overload of the BackgroundJobServer type that will be removed in 2.0.0.\")]\n        public BackgroundJobServerHostedService(\n            [NotNull] JobStorage storage,\n            [NotNull] BackgroundJobServerOptions options,\n            [NotNull] IEnumerable<IBackgroundProcess> additionalProcesses,\n            [CanBeNull] IBackgroundJobFactory factory,\n            [CanBeNull] IBackgroundJobPerformer performer,\n            [CanBeNull] IBackgroundJobStateChanger stateChanger)\n            : this(storage, options, additionalProcesses, factory, performer, stateChanger, null)\n        {\n        }\n#endif\n\n        [Obsolete(\"This constructor uses an obsolete constructor overload of the BackgroundJobServer type that will be removed in 2.0.0.\")]\n        public BackgroundJobServerHostedService(\n            [NotNull] JobStorage storage,\n            [NotNull] BackgroundJobServerOptions options,\n            [NotNull] IEnumerable<IBackgroundProcess> additionalProcesses,\n            [CanBeNull] IBackgroundJobFactory factory,\n            [CanBeNull] IBackgroundJobPerformer performer,\n            [CanBeNull] IBackgroundJobStateChanger stateChanger\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\n            ,\n            [CanBeNull] IHostApplicationLifetime hostApplicationLifetime\n#endif\n            )\n        {\n            _options = options ?? throw new ArgumentNullException(nameof(options));\n            _storage = storage ?? throw new ArgumentNullException(nameof(storage));\n\n            _additionalProcesses = additionalProcesses;\n\n            _factory = factory;\n            _performer = performer;\n            _stateChanger = stateChanger;\n\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\n            _hostApplicationLifetime = hostApplicationLifetime;\n            _hostApplicationLifetime?.ApplicationStopping.Register(SendStopSignal);\n#endif\n        }\n\n        public Task StartAsync(CancellationToken cancellationToken)\n        {\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\n            if (_hostApplicationLifetime != null)\n            {\n                // https://github.com/HangfireIO/Hangfire/issues/2117\n                _hostApplicationLifetime.ApplicationStarted.Register(InitializeProcessingServer);\n            }\n            else\n#endif\n            {\n                InitializeProcessingServer();\n            }\n\n            return Task.CompletedTask;\n        }\n\n        public async Task StopAsync(CancellationToken cancellationToken)\n        {\n            var server = _processingServer;\n            if (server == null) return;\n\n            try\n            {\n                server.SendStop();\n                await server.WaitForShutdownAsync(cancellationToken);\n            }\n            catch (ObjectDisposedException)\n            {\n                // Due to a bug in ASP.NET Core's Testing package, the StopAsync method\n                // can be called twice and from different threads, please see\n                // https://github.com/dotnet/aspnetcore/issues/40271 for details.\n                // This is not a case for regular applications but can fail integration tests\n                // when the second call to the StopAsync method attempts to be performed on\n                // an already disposed object.\n                // This is not a big deal, however, it's still a workaround that should be\n                // removed one day.\n                // TODO: Remove this workaround and don't rely on this behavior for simplicity.\n            }\n        }\n\n        public void Dispose()\n        {\n            _processingServer?.Dispose();\n            _processingServer = null;\n            GC.SuppressFinalize(this);\n        }\n        \n        private void InitializeProcessingServer()\n        {\n            _processingServer = _factory != null && _performer != null && _stateChanger != null\n#pragma warning disable 618\n                ? new BackgroundJobServer(_options, _storage, _additionalProcesses, null, null, _factory, _performer,\n                    _stateChanger)\n#pragma warning restore 618\n                : new BackgroundJobServer(_options, _storage, _additionalProcesses);\n        }\n\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\n        private void SendStopSignal()\n        {\n            try\n            {\n                _processingServer?.SendStop();\n            }\n            catch (ObjectDisposedException)\n            {\n                // Please see the comment regarding this exception above.\n            }\n        }\n#endif\n    }\n}\n#endif\n"
  },
  {
    "path": "src/Hangfire.NetCore/BackgroundProcessingServerHostedService.cs",
    "content": "// This file is part of Hangfire.\n// Copyright © 2021 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\n#if !NET451 && !NETSTANDARD1_3\n\nusing System;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Server;\nusing Microsoft.Extensions.Hosting;\n\nnamespace Hangfire\n{\n    public sealed class BackgroundProcessingServerHostedService : IHostedService, IDisposable\n    {\n        private IBackgroundProcessingServer _server;\n\n#if NETSTANDARD2_1\n        public BackgroundProcessingServerHostedService([NotNull] IBackgroundProcessingServer server)\n            : this(server, null)\n        {\n        }\n\n        public BackgroundProcessingServerHostedService(\n            [NotNull] IBackgroundProcessingServer server,\n            [CanBeNull] IHostApplicationLifetime lifetime)\n        {\n            _server = server ?? throw new ArgumentNullException(nameof(server));\n            lifetime?.ApplicationStopping.Register(server.SendStop);\n        }\n#else\n        public BackgroundProcessingServerHostedService([NotNull] IBackgroundProcessingServer server)\n        {\n            _server = server ?? throw new ArgumentNullException(nameof(server));\n        }\n#endif\n\n        public Task StartAsync(CancellationToken cancellationToken)\n        {\n            return Task.CompletedTask;\n        }\n\n        public Task StopAsync(CancellationToken cancellationToken)\n        {\n            _server?.SendStop();\n            return _server?.WaitForShutdownAsync(cancellationToken) ?? Task.CompletedTask;\n        }\n\n        public void Dispose()\n        {\n            _server?.Dispose();\n            _server = null;\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Hangfire.NetCore/DefaultClientManagerFactory.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2021 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace Hangfire\n{\n    internal sealed class DefaultClientManagerFactory : IBackgroundJobClientFactoryV2, IRecurringJobManagerFactoryV2\n    {\n        private readonly IServiceProvider _serviceProvider;\n\n        public DefaultClientManagerFactory([NotNull] IServiceProvider serviceProvider)\n        {\n            _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));\n        }\n\n        public IBackgroundJobClientV2 GetClientV2(JobStorage storage)\n        {\n            if (HangfireServiceCollectionExtensions.GetInternalServices(_serviceProvider, out var factory, out var stateChanger, out _))\n            {\n                return new BackgroundJobClient(storage, factory, stateChanger);\n            }\n\n            return new BackgroundJobClient(\n                storage,\n                _serviceProvider.GetRequiredService<IJobFilterProvider>());\n        }\n\n        public IBackgroundJobClient GetClient(JobStorage storage)\n        {\n            return GetClientV2(storage);\n        }\n\n        public IRecurringJobManagerV2 GetManagerV2(JobStorage storage)\n        {\n            if (HangfireServiceCollectionExtensions.GetInternalServices(_serviceProvider, out var factory, out _, out _))\n            {\n                return new RecurringJobManager(\n                    storage,\n                    factory,\n                    _serviceProvider.GetRequiredService<ITimeZoneResolver>());\n            }\n\n            return new RecurringJobManager(\n                storage,\n                _serviceProvider.GetRequiredService<IJobFilterProvider>(),\n                _serviceProvider.GetRequiredService<ITimeZoneResolver>());\n        }\n\n        public IRecurringJobManager GetManager(JobStorage storage)\n        {\n            return GetManagerV2(storage);\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.NetCore/Hangfire.NetCore.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n  <PropertyGroup>\r\n    <TargetFrameworks>net451;net461;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>\r\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\r\n    <RootNamespace>Hangfire</RootNamespace>\r\n  </PropertyGroup>\r\n\r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\Hangfire.Core\\Hangfire.Core.csproj\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net451' or '$(TargetFramework)'=='net461'\">\r\n    <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies\" Version=\"1.0.3\" PrivateAssets=\"all\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net451' or '$(TargetFramework)'=='netstandard1.3'\">    \r\n    <PackageReference Include=\"Microsoft.Extensions.DependencyInjection.Abstractions\" Version=\"1.0.0\" />\r\n    <PackageReference Include=\"Microsoft.Extensions.Logging.Abstractions\" Version=\"1.0.0\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net461' or '$(TargetFramework)'=='netstandard2.0'\">\r\n    <PackageReference Include=\"Microsoft.Extensions.DependencyInjection.Abstractions\" Version=\"2.0.0\" />\r\n    <PackageReference Include=\"Microsoft.Extensions.Hosting.Abstractions\" Version=\"2.0.0\" />\r\n    <PackageReference Include=\"Microsoft.Extensions.Logging.Abstractions\" Version=\"2.0.0\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='netstandard2.1'\">\r\n    <PackageReference Include=\"Microsoft.Extensions.DependencyInjection.Abstractions\" Version=\"3.0.0\" />\r\n    <PackageReference Include=\"Microsoft.Extensions.Hosting.Abstractions\" Version=\"3.0.0\" />\r\n    <PackageReference Include=\"Microsoft.Extensions.Logging.Abstractions\" Version=\"3.0.0\" />\r\n  </ItemGroup>\r\n</Project>\r\n"
  },
  {
    "path": "src/Hangfire.NetCore/HangfireServiceCollectionExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2016 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing Hangfire.Annotations;\nusing Hangfire.AspNetCore;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.Dashboard;\nusing Hangfire.Server;\nusing Hangfire.States;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.DependencyInjection.Extensions;\nusing Microsoft.Extensions.Logging;\n#if !NET451 && !NETSTANDARD1_3\nusing Microsoft.Extensions.Hosting;\n#endif\n\nnamespace Hangfire\n{\n    public static class HangfireServiceCollectionExtensions\n    {\n        public static IServiceCollection AddHangfire(\n            [NotNull] this IServiceCollection services,\n            [NotNull] Action<IGlobalConfiguration> configuration)\n        {\n            return AddHangfire(services, (provider, config) => configuration(config));\n        }\n\n        public static IServiceCollection AddHangfire(\n            [NotNull] this IServiceCollection services,\n            [NotNull] Action<IServiceProvider, IGlobalConfiguration> configuration)\n        {\n            if (services == null) throw new ArgumentNullException(nameof(services));\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            services.TryAddSingletonChecked(static _ => JobStorage.Current);\n            services.TryAddSingletonChecked(static _ => JobActivator.Current);\n\n            services.TryAddSingleton(static _ => DashboardRoutes.Routes);\n            services.TryAddSingleton<IJobFilterProvider>(static _ => JobFilterProviders.Providers);\n            services.TryAddSingleton<ITimeZoneResolver>(static _ => new DefaultTimeZoneResolver());\n            \n            services.TryAddSingleton(static x => new DefaultClientManagerFactory(x));\n            services.TryAddSingletonChecked<IBackgroundJobClientFactory>(static x => x.GetService<DefaultClientManagerFactory>());\n            services.TryAddSingletonChecked<IBackgroundJobClientFactoryV2>(static x => x.GetService<DefaultClientManagerFactory>());\n            services.TryAddSingletonChecked<IRecurringJobManagerFactory>(static x => x.GetService<DefaultClientManagerFactory>());\n            services.TryAddSingletonChecked<IRecurringJobManagerFactoryV2>(static x => x.GetService<DefaultClientManagerFactory>());\n\n            services.TryAddSingletonChecked(static x => x\n                .GetService<IBackgroundJobClientFactory>().GetClient(x.GetService<JobStorage>()));\n            services.TryAddSingletonChecked(static x => x\n                .GetService<IBackgroundJobClientFactoryV2>().GetClientV2(x.GetService<JobStorage>()));\n\n            services.TryAddSingletonChecked(static x => x\n                .GetService<IRecurringJobManagerFactory>().GetManager(x.GetService<JobStorage>()));\n            services.TryAddSingletonChecked(static x => x\n                .GetService<IRecurringJobManagerFactoryV2>().GetManagerV2(x.GetService<JobStorage>()));\n\n            // IGlobalConfiguration serves as a marker indicating that Hangfire's services \n            // were added to the service container (checked by IApplicationBuilder extensions).\n            // \n            // Being a singleton, it also guarantees that the configuration callback will be \n            // executed just once upon initialization, so there's no need to double-check that.\n            // \n            // It should never be replaced by another implementation !!!\n            // AddSingleton() will throw an exception if it was already registered\n\n            services.AddSingleton<IGlobalConfiguration>(serviceProvider =>\n            {\n                var configurationInstance = GlobalConfiguration.Configuration;\n\n                // init defaults for log provider and job activator\n                // they may be overwritten by the configuration callback later\n\n                var loggerFactory = serviceProvider.GetService<ILoggerFactory>();\n                if (loggerFactory != null)\n                {\n                    configurationInstance.UseLogProvider(new AspNetCoreLogProvider(loggerFactory));\n                }\n\n                var scopeFactory = serviceProvider.GetService<IServiceScopeFactory>();\n                if (scopeFactory != null)\n                {\n                    configurationInstance.UseActivator(new AspNetCoreJobActivator(scopeFactory));\n                }\n\n                // do configuration inside callback\n\n                configuration(serviceProvider, configurationInstance);\n                \n                return configurationInstance;\n            });\n            \n            return services;\n        }\n\n#if !NET451 && !NETSTANDARD1_3\n        public static IServiceCollection AddHangfireServer(\n            [NotNull] this IServiceCollection services,\n            [NotNull] Action<BackgroundJobServerOptions> optionsAction)\n        {\n            if (services == null) throw new ArgumentNullException(nameof(services));\n            if (optionsAction == null) throw new ArgumentNullException(nameof(optionsAction));\n\n            return AddHangfireServerInner(services, null, null, (provider, options) => optionsAction(options));\n        }\n\n        public static IServiceCollection AddHangfireServer(\n            [NotNull] this IServiceCollection services,\n            [NotNull] Action<IServiceProvider, BackgroundJobServerOptions> optionsAction)\n        {\n            if (services == null) throw new ArgumentNullException(nameof(services));\n            if (optionsAction == null) throw new ArgumentNullException(nameof(optionsAction));\n\n            return AddHangfireServerInner(services, null, null, optionsAction);\n        }\n\n        public static IServiceCollection AddHangfireServer(\n            [NotNull] this IServiceCollection services,\n            [NotNull] Action<IServiceProvider, BackgroundJobServerOptions> optionsAction,\n            [NotNull] JobStorage storage)\n        {\n            if (services == null) throw new ArgumentNullException(nameof(services));\n            if (optionsAction == null) throw new ArgumentNullException(nameof(optionsAction));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n\n            return AddHangfireServerInner(services, storage, null, optionsAction);\n        }\n\n        public static IServiceCollection AddHangfireServer(\n            [NotNull] this IServiceCollection services,\n            [NotNull] Action<IServiceProvider, BackgroundJobServerOptions> optionsAction,\n            [NotNull] JobStorage storage,\n            [NotNull] IEnumerable<IBackgroundProcess> additionalProcesses)\n        {\n            if (services == null) throw new ArgumentNullException(nameof(services));\n            if (optionsAction == null) throw new ArgumentNullException(nameof(optionsAction));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (additionalProcesses == null) throw new ArgumentNullException(nameof(additionalProcesses));\n\n            return AddHangfireServerInner(services, storage, additionalProcesses, optionsAction);\n        }\n\n        public static IServiceCollection AddHangfireServer([NotNull] this IServiceCollection services)\n        {\n            if (services == null) throw new ArgumentNullException(nameof(services));\n            return AddHangfireServerInner(services, null, null);\n        }\n\n        public static IServiceCollection AddHangfireServer(\n            [NotNull] this IServiceCollection services,\n            [NotNull] JobStorage storage)\n        {\n            if (services == null) throw new ArgumentNullException(nameof(services));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n\n            return AddHangfireServerInner(services, storage, null);\n        }\n\n        public static IServiceCollection AddHangfireServer(\n            [NotNull] this IServiceCollection services,\n            [NotNull] JobStorage storage,\n            [NotNull] IEnumerable<IBackgroundProcess> additionalProcesses)\n        {\n            if (services == null) throw new ArgumentNullException(nameof(services));\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (additionalProcesses == null) throw new ArgumentNullException(nameof(additionalProcesses));\n\n            return AddHangfireServerInner(services, storage, additionalProcesses);\n        }\n\n        private static IServiceCollection AddHangfireServerInner(\n            [NotNull] IServiceCollection services,\n            [CanBeNull] JobStorage storage,\n            [CanBeNull] IEnumerable<IBackgroundProcess> additionalProcesses)\n        {\n            services.AddTransient<IHostedService, BackgroundJobServerHostedService>(provider =>\n            {\n                var options = provider.GetService<BackgroundJobServerOptions>() ?? new BackgroundJobServerOptions();\n                return CreateBackgroundJobServerHostedService(provider, storage, additionalProcesses, options);\n            });\n\n            return services;\n        }\n\n        private static IServiceCollection AddHangfireServerInner(\n            [NotNull] IServiceCollection services,\n            [CanBeNull] JobStorage storage,\n            [CanBeNull] IEnumerable<IBackgroundProcess> additionalProcesses,\n            [NotNull] Action<IServiceProvider, BackgroundJobServerOptions> optionsAction)\n        {\n            services.AddTransient<IHostedService, BackgroundJobServerHostedService>(provider =>\n            {\n                var options = new BackgroundJobServerOptions();\n                optionsAction(provider, options);\n\n                return CreateBackgroundJobServerHostedService(provider, storage, additionalProcesses, options);\n            });\n\n            return services;\n        }\n\n        public static IServiceCollection AddHangfireServer(\n            [NotNull] this IServiceCollection services,\n            [NotNull] Func<IServiceProvider, IBackgroundProcessingServer> implementationFactory)\n        {\n            if (services == null) throw new ArgumentNullException(nameof(services));\n            if (implementationFactory == null) throw new ArgumentNullException(nameof(implementationFactory));\n\n            services.AddTransient<IHostedService, BackgroundProcessingServerHostedService>(\n                provider => new BackgroundProcessingServerHostedService(\n                    implementationFactory(provider)\n#if NETSTANDARD2_1\n                    , provider.GetService<IHostApplicationLifetime>()\n#endif\n                    ));\n\n            return services;\n        }\n\n        private static BackgroundJobServerHostedService CreateBackgroundJobServerHostedService(\n            IServiceProvider provider,\n            JobStorage storage,\n            IEnumerable<IBackgroundProcess> additionalProcesses,\n            BackgroundJobServerOptions options)\n        {\n            ThrowIfNotConfigured(provider);\n\n            storage = storage ?? provider.GetService<JobStorage>() ?? JobStorage.Current;\n            additionalProcesses = additionalProcesses ?? provider.GetServices<IBackgroundProcess>();\n\n            options.Activator = options.Activator ?? provider.GetService<JobActivator>();\n            options.FilterProvider = options.FilterProvider ?? provider.GetService<IJobFilterProvider>();\n            options.TimeZoneResolver = options.TimeZoneResolver ?? provider.GetService<ITimeZoneResolver>();\n\n            GetInternalServices(provider, out var factory, out var stateChanger, out var performer);\n\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\n            var lifetime = provider.GetService<IHostApplicationLifetime>();\n#endif\n\n#pragma warning disable 618\n            return new BackgroundJobServerHostedService(\n#pragma warning restore 618\n                storage, options, additionalProcesses, factory, performer, stateChanger\n#if NETSTANDARD2_1 || NETCOREAPP3_0_OR_GREATER\n                , lifetime\n#endif\n                );\n        }\n#endif\n\n        public static bool GetInternalServices(\n            IServiceProvider provider,\n            out IBackgroundJobFactory factory,\n            out IBackgroundJobStateChanger stateChanger,\n            out IBackgroundJobPerformer performer)\n        {\n            factory = provider.GetService<IBackgroundJobFactory>();\n            performer = provider.GetService<IBackgroundJobPerformer>();\n            stateChanger = provider.GetService<IBackgroundJobStateChanger>();\n\n            if (factory != null && performer != null && stateChanger != null)\n            {\n                return true;\n            }\n\n            factory = null;\n            performer = null;\n            stateChanger = null;\n\n            return false;\n        }\n\n        private static void TryAddSingletonChecked<T>(\n            [NotNull] this IServiceCollection serviceCollection, \n            [NotNull] Func<IServiceProvider, T> implementationFactory)\n            where T : class\n        {\n            serviceCollection.TryAddSingleton<T>(serviceProvider =>\n            {\n                if (serviceProvider == null) throw new ArgumentNullException(nameof(serviceProvider));\n\n                // ensure the configuration was performed\n                serviceProvider.GetRequiredService<IGlobalConfiguration>();\n\n                return implementationFactory(serviceProvider);\n            });\n        }\n\n        public static void ThrowIfNotConfigured(IServiceProvider serviceProvider)\n        {\n            var configuration = serviceProvider.GetService<IGlobalConfiguration>();\n            if (configuration == null)\n            {\n                throw new InvalidOperationException(\n                    \"Unable to find the required services. Please add all the required services by calling 'IServiceCollection.AddHangfire' inside the call to 'ConfigureServices(...)' in the application startup code.\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.NetCore/IBackgroundJobClientFactory.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2021 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire\n{\n    public interface IBackgroundJobClientFactoryV2 : IBackgroundJobClientFactory\n    {\n        IBackgroundJobClientV2 GetClientV2(JobStorage storage);\n    }\n\n    public interface IBackgroundJobClientFactory\n    {\n        IBackgroundJobClient GetClient(JobStorage storage);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.NetCore/IRecurringJobManagerFactory.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2021 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire\n{\n    public interface IRecurringJobManagerFactoryV2 : IRecurringJobManagerFactory\n    {\n        IRecurringJobManagerV2 GetManagerV2(JobStorage storage);\n    }\n\n    public interface IRecurringJobManagerFactory\n    {\n        IRecurringJobManager GetManager(JobStorage storage);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.NetCore/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \".NETFramework,Version=v4.5.1\": {\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.0, )\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"+XwaNo3o9RhLQhUnnOBCaukeRi1X9yYc0Fzye9RlErSflKZdw0VgHtn6rvKo0FTionsW0x8QVULhKH+nkqVjQA==\",\n        \"dependencies\": {\n          \"System.ComponentModel\": \"4.0.1\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.0, )\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"wHT6oY50q36mAXBRKtFaB7u07WxKC5u2M8fi3PqHOOnHyUo9gD0u1TlCNR8UObHQxKMYwqlgI8TLcErpt29n8A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Collections.Concurrent\": \"4.0.12\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.InteropServices\": \"4.1.0\"\n        }\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net451\": \"1.0.3\"\n        }\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net451\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vVPinxdLrwoX81ApbNIHDBI6qymQEy8eSOxDNBgKJtc2+cifnF0oT1U2d3EFx+V5O68yaqna2myZJNsgKCpVkA==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.11\",\n        \"contentHash\": \"YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==\"\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.12\",\n        \"contentHash\": \"2gBcbb3drMLgxlI0fBfxMA31ec6AEyYCHygGse4vxceJan8mRIWeKJ24BFzN7+bi/NFTgdIgufzb94LWO5EERQ==\"\n      },\n      \"System.ComponentModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"oBZFnm7seFiVfugsIyOvQCWobNZs7FzqDV/B7tx20Ep/l3UUFCPDkdTnCNaJZTU27zjeODmy2C/cP60u3D4c9w==\"\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.11\",\n        \"contentHash\": \"w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==\"\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.11\",\n        \"contentHash\": \"B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==\"\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"bQ0iYFOQI0nuTnt+NQADns6ucV4DUvMdwN6CbkB1yj8i7arTGiTN5eok1kQwdnnNWSDZfIUySQY+J3d5KjWn0g==\"\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"I+y02iqkgmCAyfbqOmSDOgqdZQ5tTj80Akm5BPSS8EeB0VGWdy6X1KCoYe8Pk6pwDoAKZUOdLVxnTJcExiv5zw==\"\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==\"\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==\"\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==\"\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      }\n    },\n    \".NETFramework,Version=v4.6.1\": {\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.0, )\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"eUdJ0Q/GfVyUJc0Jal5L1QZLceL78pvEM9wEKcHeI24KorqMDoVX+gWsMGLulQMfOwsUaPtkpQM2pFERTzSfSg==\"\n      },\n      \"Microsoft.Extensions.Hosting.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.0, )\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"qPG6Ip/AdHxMJ7j3z8FkkpCbV8yjtiFpf/aOpN3TwfJWbtYpN+BKV8Q+pqPMgk7XZivcju9yARaEVCS++hWopA==\"\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.0, )\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net461\": \"1.0.3\"\n        }\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net461\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      }\n    },\n    \".NETStandard,Version=v1.3\": {\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.0, )\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"+XwaNo3o9RhLQhUnnOBCaukeRi1X9yYc0Fzye9RlErSflKZdw0VgHtn6rvKo0FTionsW0x8QVULhKH+nkqVjQA==\",\n        \"dependencies\": {\n          \"System.ComponentModel\": \"4.0.1\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.0, )\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"wHT6oY50q36mAXBRKtFaB7u07WxKC5u2M8fi3PqHOOnHyUo9gD0u1TlCNR8UObHQxKMYwqlgI8TLcErpt29n8A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Collections.Concurrent\": \"4.0.12\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.InteropServices\": \"4.1.0\"\n        }\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.6.1, )\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"17h8b5mXa87XYKrrVqdgZ38JefSUqLChUQpXgSnpzsM0nDOhE40FTeNWOJ/YmySGV6tG6T8+hjz6vxbknHJr6A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Dynamic.Runtime\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Extensions\": \"4.0.1\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Reflection.TypeExtensions\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.InteropServices\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"9.0.1\",\n        \"contentHash\": \"U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==\",\n        \"dependencies\": {\n          \"Microsoft.CSharp\": \"4.0.1\",\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Dynamic.Runtime\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.IO\": \"4.1.0\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Extensions\": \"4.0.1\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.Serialization.Primitives\": \"4.1.1\",\n          \"System.Text.Encoding\": \"4.0.11\",\n          \"System.Text.Encoding.Extensions\": \"4.0.11\",\n          \"System.Text.RegularExpressions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\",\n          \"System.Threading.Tasks\": \"4.0.11\",\n          \"System.Xml.ReaderWriter\": \"4.0.11\",\n          \"System.Xml.XDocument\": \"4.0.11\"\n        }\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"oBZFnm7seFiVfugsIyOvQCWobNZs7FzqDV/B7tx20Ep/l3UUFCPDkdTnCNaJZTU27zjeODmy2C/cP60u3D4c9w==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Dynamic.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.11\",\n        \"contentHash\": \"db34f6LHYM0U0JpE+sOmjar27BnqTVkbLJhgfwMpTdgTigG/Hna3m2MYVwnFzGGKnEJk2UXFuoVTr8WUbU91/A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Emit\": \"4.0.1\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.0.1\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Reflection.TypeExtensions\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"P2wqAj72fFjpP6wb9nSfDqNBMab+2ovzSDzUZK7MVIm54tBJEPr9jWfSjjoTpPwj1LeKcmX3vr0ttyjSSFM47g==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.1.0\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.0.1\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"Ov6dU8Bu15Bc7zuqttgHF12J5lwSWyTf1S+FJouUXVMSqImLZzYaQ+vRr1rQ0OZ0HqsrwWl4dsKHELckQkVpgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.0\",\n        \"contentHash\": \"tsQ/ptQ3H5FYfON8lL4MxRk/8kFyE0A+tGPXmVP967cT/gzLHYxIejIYSxp4JmIeFHVP78g/F2FE1mUUTbDtrg==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Serialization.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.1\",\n        \"contentHash\": \"HZ6Du5QrTG8MNJbf4e4qMO3JRAkIboGT5Fk804uZtg3Gq516S7hAqTm2UZKUHa7/6HUGdVy3AqMQKbns06G/cg==\",\n        \"dependencies\": {\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Thread\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.0\",\n        \"contentHash\": \"gIdJqDXlOr5W9zeqFErLw3dsOsiShSCYtF9SEHitACycmvNvY8odf9kiKvp6V7aibc8C4HzzNBkWXjyfn7plbQ==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Threading.ThreadPool\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.10\",\n        \"contentHash\": \"IMXgB5Vf/5Qw1kpoVgJMOvUO1l32aC+qC3OaIZjWJOjvcxuxNWOK2ZTWWYXfij22NHxT2j1yWX5vlAeQWld9vA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Handles\": \"4.0.1\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"NETStandard.Library\": \"[1.6.1, )\",\n          \"Newtonsoft.Json\": \"[9.0.1, )\",\n          \"System.Threading.Thread\": \"[4.0.0, )\",\n          \"System.Threading.ThreadPool\": \"[4.0.10, )\"\n        }\n      }\n    },\n    \".NETStandard,Version=v2.0\": {\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.0, )\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"eUdJ0Q/GfVyUJc0Jal5L1QZLceL78pvEM9wEKcHeI24KorqMDoVX+gWsMGLulQMfOwsUaPtkpQM2pFERTzSfSg==\"\n      },\n      \"Microsoft.Extensions.Hosting.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.0, )\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"qPG6Ip/AdHxMJ7j3z8FkkpCbV8yjtiFpf/aOpN3TwfJWbtYpN+BKV8Q+pqPMgk7XZivcju9yARaEVCS++hWopA==\"\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.0, )\",\n        \"resolved\": \"2.0.0\",\n        \"contentHash\": \"6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==\"\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.3, )\",\n        \"resolved\": \"2.0.3\",\n        \"contentHash\": \"st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"vvVR/B08YVghQ4jHEloxqw2ZWzEGE1AOA5E0DioUM3ujbXz6FD3AfB/0Jl2ohJPd0nXYGwmPe1En6HTsSriq1A==\"\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.0.1\",\n        \"contentHash\": \"pNN4l+J6LlpIvHOeNdXlwxv39NPJ2B5klz+Rd2UQZIx30Squ5oND1Yy3wEAUoKn0GPUj6Yxt9lxlYWQqfZcvKg==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.CSharp\": \"[4.4.0, )\",\n          \"Newtonsoft.Json\": \"[11.0.1, )\"\n        }\n      }\n    },\n    \".NETStandard,Version=v2.1\": {\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[3.0.0, )\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"ofQRroDlzJ0xKOtzNuaVt6QKNImFkhkG0lIMpGl7PtXnIf5SuLWBeiQZAP8DNSxDBJJdcsPkiJiMYK2WA5H8dQ==\"\n      },\n      \"Microsoft.Extensions.Hosting.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[3.0.0, )\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"qeDWS5ErmkUN96BdQqpmeCmLk5HJWQ/SPw3ux5v5/Qb0hKZS5wojBMulnBC7JUEiBwg7Ir71Yjf1lFiRT5MdtQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Configuration.Abstractions\": \"3.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"3.0.0\",\n          \"Microsoft.Extensions.FileProviders.Abstractions\": \"3.0.0\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"3.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[3.0.0, )\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"+PsosTYZn+omucI0ff9eywo9QcPLwcbIWf7dz7ZLM1zGR8gVZXJ3wo6+tkuIedUNW5iWENlVJPEvrGjiVeoNNQ==\"\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"vvVR/B08YVghQ4jHEloxqw2ZWzEGE1AOA5E0DioUM3ujbXz6FD3AfB/0Jl2ohJPd0nXYGwmPe1En6HTsSriq1A==\"\n      },\n      \"Microsoft.Extensions.Configuration.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"Lge/PbXC53jI1MF2J92X5EZOeKV8Q/rlB1aV3H9I/ZTDyQGOyBcL03IAvnviWpHKj43BDkNy6kU2KKoh8kAS0g==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"3.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.FileProviders.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"kahEeykb6FyQytoZNNXuz74X85B4weIEt8Kd+0klK48bkXDWOIHAOvNjlGsPMcS9CL935Te8QGQS83JqCbpdHA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"3.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"3.0.0\",\n        \"contentHash\": \"6gwewTbmOh+ZVBicVkL1XRp79sx4O7BVY6Yy+7OYZdwn3pyOKe9lOam+3gXJ3TZMjhJZdV0Ub8hxHt2vkrmN5Q==\",\n        \"dependencies\": {\n          \"System.Memory\": \"4.5.2\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"4.6.0\"\n        }\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.0.1\",\n        \"contentHash\": \"pNN4l+J6LlpIvHOeNdXlwxv39NPJ2B5klz+Rd2UQZIx30Squ5oND1Yy3wEAUoKn0GPUj6Yxt9lxlYWQqfZcvKg==\"\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==\"\n      },\n      \"System.Memory\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.2\",\n        \"contentHash\": \"fvq1GNmUFwbKv+aLVYYdgu/+gc8Nu9oFujOxIjPrsf+meis9JBzTPDL6aP/eeGOz9yPj6rRLUbOjKMpsMEWpNg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.4.0\",\n          \"System.Numerics.Vectors\": \"4.4.0\",\n          \"System.Runtime.CompilerServices.Unsafe\": \"4.5.2\"\n        }\n      },\n      \"System.Numerics.Vectors\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==\"\n      },\n      \"System.Runtime.CompilerServices.Unsafe\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.6.0\",\n        \"contentHash\": \"HxozeSlipUK7dAroTYwIcGwKDeOVpQnJlpVaOkBz7CM4TsE5b/tKlQBZecTjh6FzcSbxndYaxxpsBMz+wMJeyw==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.CSharp\": \"[4.4.0, )\",\n          \"Newtonsoft.Json\": \"[11.0.1, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/Constants.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class Constants\n    {\n        public static readonly string DefaultSchema = \"HangFire\";\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/CountersAggregator.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Server;\n\nnamespace Hangfire.SqlServer\n{\n#pragma warning disable 618\n    internal sealed class CountersAggregator : IServerComponent, IBackgroundProcess\n#pragma warning restore 618\n    {\n        // This number should be high enough to aggregate counters efficiently,\n        // but low enough to not to cause large amount of row locks to be taken.\n        // Lock escalation to page locks may pause the background processing.\n        private const int NumberOfRecordsInSinglePass = 1000;\n        private static readonly TimeSpan DelayBetweenPasses = TimeSpan.FromMilliseconds(500);\n\n        private readonly ILog _logger = LogProvider.For<CountersAggregator>();\n        private readonly SqlServerStorage _storage;\n        private readonly TimeSpan _interval;\n\n        public CountersAggregator(SqlServerStorage storage, TimeSpan interval)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n\n            _storage = storage;\n            _interval = interval;\n        }\n\n        public void Execute(BackgroundProcessContext context)\n        {\n            if (context.Storage is not SqlServerStorage storage)\n            {\n                return;\n            }\n\n            ExecuteCore(storage, context.StoppingToken);\n        }\n\n        public void Execute(CancellationToken cancellationToken)\n        {\n            ExecuteCore(_storage, cancellationToken);\n        }\n\n        private void ExecuteCore(SqlServerStorage storage, CancellationToken cancellationToken)\n        {\n            _logger.Debug(\"Aggregating records in 'Counter' table...\");\n\n            int removedCount;\n\n            do\n            {\n                removedCount = storage.UseConnection(null, static (storage, connection) => connection.Execute(\n                    GetAggregationQuery(storage),\n                    new { now = DateTime.UtcNow, count = NumberOfRecordsInSinglePass },\n                    commandTimeout: 0));\n\n                if (removedCount >= NumberOfRecordsInSinglePass)\n                {\n                    cancellationToken.Wait(DelayBetweenPasses);\n                    cancellationToken.ThrowIfCancellationRequested();\n                }\n                // ReSharper disable once LoopVariableIsNeverChangedInsideLoop\n            } while (removedCount >= NumberOfRecordsInSinglePass);\n\n            _logger.Trace(\"Records from the 'Counter' table aggregated.\");\n\n            cancellationToken.Wait(_interval);\n        }\n\n        public override string ToString()\n        {\n            return GetType().ToString();\n        }\n\n        private static string GetAggregationQuery(SqlServerStorage storage)\n        {\n            // Starting from SQL Server 2014 it's possible to get a query with\n            // much lower cost by adding a clustered index on [Key] column.\n            // However extended support for SQL Server 2012 SP4 ends only on\n            // July 12, 2022.\n            return storage.GetQueryFromTemplate(static schemaName =>\n$@\"DECLARE @RecordsToAggregate TABLE\n(\n\t[Key] NVARCHAR(100) COLLATE DATABASE_DEFAULT NOT NULL,\n\t[Value] INT NOT NULL,\n\t[ExpireAt] DATETIME NULL\n)\n\nSET XACT_ABORT ON\nSET TRANSACTION ISOLATION LEVEL READ COMMITTED\nSET DEADLOCK_PRIORITY LOW\nBEGIN TRAN\n\nDELETE TOP (@count) C\nOUTPUT DELETED.[Key], DELETED.[Value], DELETED.[ExpireAt] INTO @RecordsToAggregate\nFROM [{schemaName}].[Counter] C WITH (READPAST, XLOCK, INDEX(0))\n\nSET NOCOUNT ON\n\n;MERGE [{schemaName}].[AggregatedCounter] WITH (FORCESEEK, HOLDLOCK) AS [Target]\nUSING (\n\tSELECT [Key], SUM([Value]) as [Value], MAX([ExpireAt]) AS [ExpireAt] FROM @RecordsToAggregate\n\tGROUP BY [Key]) AS [Source] ([Key], [Value], [ExpireAt])\nON [Target].[Key] COLLATE DATABASE_DEFAULT = [Source].[Key] COLLATE DATABASE_DEFAULT\nWHEN MATCHED THEN UPDATE SET \n\t[Target].[Value] = [Target].[Value] + [Source].[Value],\n\t[Target].[ExpireAt] = (SELECT MAX([ExpireAt]) FROM (VALUES ([Source].ExpireAt), ([Target].[ExpireAt])) AS MaxExpireAt([ExpireAt]))\nWHEN NOT MATCHED THEN INSERT ([Key], [Value], [ExpireAt]) VALUES ([Source].[Key], [Source].[Value], [Source].[ExpireAt]);\n\nCOMMIT TRAN\");\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/Hangfire.SqlServer/DbCommandExtensions.cs",
    "content": "// This file is part of Hangfire. Copyright © 2024 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Globalization;\nusing System.Linq;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.SqlServer\n{\n    internal static class DbCommandExtensions\n    {\n        private static readonly ConcurrentDictionary<KeyValuePair<string, KeyValuePair<string, int>>, string> ExpandedQueries =\n            new ConcurrentDictionary<KeyValuePair<string, KeyValuePair<string, int>>, string>();\n\n        public static DbCommand Create(\n            [NotNull] this DbConnection connection,\n            [NotNull] string text,\n            CommandType type = CommandType.Text,\n            int? timeout = null)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            if (text == null) throw new ArgumentNullException(nameof(text));\n\n            var command = connection.CreateCommand();\n            command.CommandType = type;\n            command.CommandText = text;\n\n            if (timeout.HasValue)\n            {\n                command.CommandTimeout = timeout.Value;\n            }\n\n            return command;\n        }\n\n        public static DbCommand AddParameter(\n            [NotNull] this DbCommand command,\n            [NotNull] string parameterName,\n            [CanBeNull] object value,\n            DbType dbType,\n            [CanBeNull] int? size = null)\n        {\n            if (command == null) throw new ArgumentNullException(nameof(command));\n            if (parameterName == null) throw new ArgumentNullException(nameof(parameterName));\n\n            var parameter = AddParameterInternal(command, parameterName, dbType, size);\n            parameter.Value = value ?? DBNull.Value;\n\n            return command;\n        }\n\n        public static DbCommand AddReturnParameter(\n            [NotNull] this DbCommand command,\n            string parameterName,\n            out DbParameter parameter,\n            DbType dbType,\n            int? size = null)\n        {\n            if (command == null) throw new ArgumentNullException(nameof(command));\n            if (parameterName == null) throw new ArgumentNullException(nameof(parameterName));\n\n            parameter = AddParameterInternal(command, parameterName, dbType, size);\n            parameter.Direction = ParameterDirection.ReturnValue;\n            parameter.Value = DBNull.Value;\n\n            return command;\n        }\n\n        public static DbCommand AddExpandedParameter<T>(\n            [NotNull] this DbCommand command,\n            [NotNull] string parameterName,\n            [NotNull] T[] parameterValues,\n            DbType parameterType,\n            int? parameterSize = null)\n        {\n            if (command == null) throw new ArgumentNullException(nameof(command));\n            if (parameterName == null) throw new ArgumentNullException(nameof(parameterName));\n            if (parameterValues == null) throw new ArgumentNullException(nameof(parameterValues));\n\n            command.CommandText = ExpandedQueries.GetOrAdd(\n                new KeyValuePair<string, KeyValuePair<string, int>>(command.CommandText, new KeyValuePair<string, int>(parameterName, parameterValues.Length)),\n                static pair =>\n                {\n                    return pair.Key.Replace(\n                        pair.Value.Key, \n                        \"(\" + String.Join(\",\", Enumerable.Range(0, pair.Value.Value).Select(i => pair.Value.Key + i.ToString(CultureInfo.InvariantCulture))) + \")\");\n                });\n\n            for (var i = 0; i < parameterValues.Length; i++)\n            {\n                var parameter = AddParameterInternal(command, parameterName + i.ToString(CultureInfo.InvariantCulture), parameterType, parameterSize);\n                parameter.Value = (object)parameterValues[i] ?? DBNull.Value;\n            }\n\n            return command;\n        }\n\n        public static T GetParameterValue<T>([NotNull] this DbParameter parameter)\n        {\n            if (parameter == null) throw new ArgumentNullException(nameof(parameter));\n\n            switch (parameter.Value)\n            {\n                case null or DBNull: return default;\n                case T typed: return typed;\n                default:\n                    var type = typeof(T);\n                    type = Nullable.GetUnderlyingType(type) ?? type;\n                    return (T)Convert.ChangeType(parameter.Value, type, CultureInfo.InvariantCulture);                    \n            }\n        }\n\n        private static DbParameter AddParameterInternal(\n            DbCommand command,\n            string parameterName,\n            DbType dbType,\n            int? size)\n        {\n            var parameter = command.CreateParameter();\n            parameter.ParameterName = parameterName;\n            parameter.DbType = dbType;\n\n            if (size.HasValue) parameter.Size = size.Value;\n\n            command.Parameters.Add(parameter);\n            return parameter;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/DefaultInstall.sql",
    "content": "﻿\n-- This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n-- \n-- Hangfire is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Lesser General Public License as \n-- published by the Free Software Foundation, either version 3 \n-- of the License, or any later version.\n-- \n-- Hangfire is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Lesser General Public License for more details.\n-- \n-- You should have received a copy of the GNU Lesser General Public \n-- License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nSET NOCOUNT ON\nSET XACT_ABORT ON\nDECLARE @TARGET_SCHEMA_VERSION INT;\nDECLARE @DISABLE_HEAVY_MIGRATIONS BIT;\nSET @TARGET_SCHEMA_VERSION = 9;\n--SET @DISABLE_HEAVY_MIGRATIONS = 1;\n\nPRINT 'Installing Hangfire SQL objects...';\n\nBEGIN TRANSACTION;\n\n-- Acquire exclusive lock to prevent deadlocks caused by schema creation / version update\nDECLARE @SchemaLockResult INT;\nEXEC @SchemaLockResult = sp_getapplock @Resource = 'HangFire:SchemaLock', @LockMode = 'Exclusive'\n\n-- Create the database schema if it doesn't exists\nIF NOT EXISTS (SELECT [schema_id] FROM [sys].[schemas] WHERE [name] = 'HangFire')\nBEGIN\n    EXEC (N'CREATE SCHEMA [HangFire]');\n    PRINT 'Created database schema [HangFire]';\nEND\nELSE\n    PRINT 'Database schema [HangFire] already exists';\n    \nDECLARE @SCHEMA_ID int;\nSELECT @SCHEMA_ID = [schema_id] FROM [sys].[schemas] WHERE [name] = 'HangFire';\n\n-- Create the [HangFire].Schema table if not exists\nIF NOT EXISTS(SELECT [object_id] FROM [sys].[tables] \n    WHERE [name] = 'Schema' AND [schema_id] = @SCHEMA_ID)\nBEGIN\n    CREATE TABLE [HangFire].[Schema](\n        [Version] [int] NOT NULL,\n        CONSTRAINT [PK_HangFire_Schema] PRIMARY KEY CLUSTERED ([Version] ASC)\n    );\n    PRINT 'Created table [HangFire].[Schema]';\nEND\nELSE\n    PRINT 'Table [HangFire].[Schema] already exists';\n    \nDECLARE @CURRENT_SCHEMA_VERSION int;\nSELECT @CURRENT_SCHEMA_VERSION = [Version] FROM [HangFire].[Schema];\n\nPRINT 'Current Hangfire schema version: ' + CASE WHEN @CURRENT_SCHEMA_VERSION IS NULL THEN 'none' ELSE CONVERT(nvarchar, @CURRENT_SCHEMA_VERSION) END;\n\nIF @CURRENT_SCHEMA_VERSION IS NOT NULL AND @CURRENT_SCHEMA_VERSION > @TARGET_SCHEMA_VERSION\nBEGIN\n    ROLLBACK TRANSACTION;\n    PRINT 'Hangfire current database schema version ' + CAST(@CURRENT_SCHEMA_VERSION AS NVARCHAR) +\n          ' is newer than the configured SqlServerStorage schema version ' + CAST(@TARGET_SCHEMA_VERSION AS NVARCHAR) +\n          '. Will not apply any migrations.';\n    RETURN;\nEND\n\n-- Install [HangFire] schema objects\nIF @CURRENT_SCHEMA_VERSION IS NULL\nBEGIN\n    IF @DISABLE_HEAVY_MIGRATIONS = 1\n    BEGIN\n        SET @DISABLE_HEAVY_MIGRATIONS = 0;\n        PRINT 'Enabling HEAVY_MIGRATIONS, because we are installing objects from scratch';\n    END\n\n    PRINT 'Installing schema version 1';\n        \n    -- Create job tables\n    CREATE TABLE [HangFire].[Job] (\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n\t\t[StateId] [int] NULL,\n\t\t[StateName] [nvarchar](20) NULL, -- To speed-up queries.\n        [InvocationData] [nvarchar](max) NOT NULL,\n        [Arguments] [nvarchar](max) NOT NULL,\n        [CreatedAt] [datetime] NOT NULL,\n        [ExpireAt] [datetime] NULL,\n\n        CONSTRAINT [PK_HangFire_Job] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [HangFire].[Job]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Job_StateName] ON [HangFire].[Job] ([StateName] ASC);\n\tPRINT 'Created index [IX_HangFire_Job_StateName]';\n        \n    -- Job history table\n        \n    CREATE TABLE [HangFire].[State] (\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [JobId] [int] NOT NULL,\n\t\t[Name] [nvarchar](20) NOT NULL,\n\t\t[Reason] [nvarchar](100) NULL,\n        [CreatedAt] [datetime] NOT NULL,\n        [Data] [nvarchar](max) NULL,\n            \n        CONSTRAINT [PK_HangFire_State] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [HangFire].[State]';\n\n    ALTER TABLE [HangFire].[State] ADD CONSTRAINT [FK_HangFire_State_Job] FOREIGN KEY([JobId])\n        REFERENCES [HangFire].[Job] ([Id])\n        ON UPDATE CASCADE\n        ON DELETE CASCADE;\n    PRINT 'Created constraint [FK_HangFire_State_Job]';\n        \n    CREATE NONCLUSTERED INDEX [IX_HangFire_State_JobId] ON [HangFire].[State] ([JobId] ASC);\n    PRINT 'Created index [IX_HangFire_State_JobId]';\n        \n    -- Job parameters table\n        \n    CREATE TABLE [HangFire].[JobParameter](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [JobId] [int] NOT NULL,\n        [Name] [nvarchar](40) NOT NULL,\n        [Value] [nvarchar](max) NULL,\n            \n        CONSTRAINT [PK_HangFire_JobParameter] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [HangFire].[JobParameter]';\n\n    ALTER TABLE [HangFire].[JobParameter] ADD CONSTRAINT [FK_HangFire_JobParameter_Job] FOREIGN KEY([JobId])\n        REFERENCES [HangFire].[Job] ([Id])\n        ON UPDATE CASCADE\n        ON DELETE CASCADE;\n    PRINT 'Created constraint [FK_HangFire_JobParameter_Job]';\n        \n    CREATE NONCLUSTERED INDEX [IX_HangFire_JobParameter_JobIdAndName] ON [HangFire].[JobParameter] (\n        [JobId] ASC,\n        [Name] ASC\n    );\n    PRINT 'Created index [IX_HangFire_JobParameter_JobIdAndName]';\n        \n    -- Job queue table\n        \n    CREATE TABLE [HangFire].[JobQueue](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [JobId] [int] NOT NULL,\n        [Queue] [nvarchar](20) NOT NULL,\n        [FetchedAt] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_JobQueue] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [HangFire].[JobQueue]';\n        \n    CREATE NONCLUSTERED INDEX [IX_HangFire_JobQueue_JobIdAndQueue] ON [HangFire].[JobQueue] (\n        [JobId] ASC,\n        [Queue] ASC\n    );\n    PRINT 'Created index [IX_HangFire_JobQueue_JobIdAndQueue]';\n        \n    CREATE NONCLUSTERED INDEX [IX_HangFire_JobQueue_QueueAndFetchedAt] ON [HangFire].[JobQueue] (\n        [Queue] ASC,\n        [FetchedAt] ASC\n    );\n    PRINT 'Created index [IX_HangFire_JobQueue_QueueAndFetchedAt]';\n        \n    -- Servers table\n        \n    CREATE TABLE [HangFire].[Server](\n        [Id] [nvarchar](200) NOT NULL,\n        [Data] [nvarchar](max) NULL,\n        [LastHeartbeat] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_Server] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [HangFire].[Server]';\n        \n    -- Extension tables\n        \n    CREATE TABLE [HangFire].[Hash](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [Key] [nvarchar](100) NOT NULL,\n        [Name] [nvarchar](40) NOT NULL,\n        [StringValue] [nvarchar](max) NULL,\n        [IntValue] [int] NULL,\n        [ExpireAt] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_Hash] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [HangFire].[Hash]';\n        \n    CREATE UNIQUE NONCLUSTERED INDEX [UX_HangFire_Hash_KeyAndName] ON [HangFire].[Hash] (\n        [Key] ASC,\n        [Name] ASC\n    );\n    PRINT 'Created index [UX_HangFire_Hash_KeyAndName]';\n        \n    CREATE TABLE [HangFire].[List](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [Key] [nvarchar](100) NOT NULL,\n        [Value] [nvarchar](max) NULL,\n        [ExpireAt] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_List] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [HangFire].[List]';\n        \n    CREATE TABLE [HangFire].[Set](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [Key] [nvarchar](100) NOT NULL,\n        [Score] [float] NOT NULL,\n        [Value] [nvarchar](256) NOT NULL,\n        [ExpireAt] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_Set] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [HangFire].[Set]';\n        \n    CREATE UNIQUE NONCLUSTERED INDEX [UX_HangFire_Set_KeyAndValue] ON [HangFire].[Set] (\n        [Key] ASC,\n        [Value] ASC\n    );\n    PRINT 'Created index [UX_HangFire_Set_KeyAndValue]';\n        \n    CREATE TABLE [HangFire].[Value](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [Key] [nvarchar](100) NOT NULL,\n        [StringValue] [nvarchar](max) NULL,\n        [IntValue] [int] NULL,\n        [ExpireAt] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_Value] PRIMARY KEY CLUSTERED (\n            [Id] ASC\n        )\n    );\n    PRINT 'Created table [HangFire].[Value]';\n        \n    CREATE UNIQUE NONCLUSTERED INDEX [UX_HangFire_Value_Key] ON [HangFire].[Value] (\n        [Key] ASC\n    );\n    PRINT 'Created index [UX_HangFire_Value_Key]';\n\n\tCREATE TABLE [HangFire].[Counter](\n\t\t[Id] [int] IDENTITY(1,1) NOT NULL,\n\t\t[Key] [nvarchar](100) NOT NULL,\n\t\t[Value] [tinyint] NOT NULL,\n\t\t[ExpireAt] [datetime] NULL,\n\n\t\tCONSTRAINT [PK_HangFire_Counter] PRIMARY KEY CLUSTERED ([Id] ASC)\n\t);\n\tPRINT 'Created table [HangFire].[Counter]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Counter_Key] ON [HangFire].[Counter] ([Key] ASC)\n\tINCLUDE ([Value]);\n\tPRINT 'Created index [IX_HangFire_Counter_Key]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 1;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 1\nBEGIN\n\tPRINT 'Installing schema version 2';\n\n\t-- https://github.com/odinserj/HangFire/issues/83\n\n\tDROP INDEX [IX_HangFire_Counter_Key] ON [HangFire].[Counter];\n\n\tALTER TABLE [HangFire].[Counter] ALTER COLUMN [Value] SMALLINT NOT NULL;\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Counter_Key] ON [HangFire].[Counter] ([Key] ASC)\n\tINCLUDE ([Value]);\n\tPRINT 'Index [IX_HangFire_Counter_Key] re-created';\n\n\tDROP TABLE [HangFire].[Value];\n\tDROP TABLE [HangFire].[Hash];\n\tPRINT 'Dropped tables [HangFire].[Value] and [HangFire].[Hash]'\n\n\tDELETE FROM [HangFire].[Server] WHERE [LastHeartbeat] IS NULL;\n\tALTER TABLE [HangFire].[Server] ALTER COLUMN [LastHeartbeat] DATETIME NOT NULL;\n\n\tSET @CURRENT_SCHEMA_VERSION = 2;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 2\nBEGIN\n\tPRINT 'Installing schema version 3';\n\n\tDROP INDEX [IX_HangFire_JobQueue_JobIdAndQueue] ON [HangFire].[JobQueue];\n\tPRINT 'Dropped index [IX_HangFire_JobQueue_JobIdAndQueue]';\n\n\tCREATE TABLE [HangFire].[Hash](\n\t\t[Id] [int] IDENTITY(1,1) NOT NULL,\n\t\t[Key] [nvarchar](100) NOT NULL,\n\t\t[Field] [nvarchar](100) NOT NULL,\n\t\t[Value] [nvarchar](max) NULL,\n\t\t[ExpireAt] [datetime2](7) NULL,\n\t\t\n\t\tCONSTRAINT [PK_HangFire_Hash] PRIMARY KEY CLUSTERED ([Id] ASC)\n\t);\n\tPRINT 'Created table [HangFire].[Hash]';\n\n\tCREATE UNIQUE NONCLUSTERED INDEX [UX_HangFire_Hash_Key_Field] ON [HangFire].[Hash] (\n\t\t[Key] ASC,\n\t\t[Field] ASC\n\t);\n\tPRINT 'Created index [UX_HangFire_Hash_Key_Field]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 3;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 3\nBEGIN\n\tPRINT 'Installing schema version 4';\n\n\tCREATE TABLE [HangFire].[AggregatedCounter] (\n\t\t[Id] [int] IDENTITY(1,1) NOT NULL,\n\t\t[Key] [nvarchar](100) NOT NULL,\n\t\t[Value] [bigint] NOT NULL,\n\t\t[ExpireAt] [datetime] NULL,\n\n\t\tCONSTRAINT [PK_HangFire_CounterAggregated] PRIMARY KEY CLUSTERED ([Id] ASC)\n\t);\n\tPRINT 'Created table [HangFire].[AggregatedCounter]';\n\n\tCREATE UNIQUE NONCLUSTERED INDEX [UX_HangFire_CounterAggregated_Key] ON [HangFire].[AggregatedCounter] (\n\t\t[Key] ASC\n\t) INCLUDE ([Value]);\n\tPRINT 'Created index [UX_HangFire_CounterAggregated_Key]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Hash_ExpireAt] ON [HangFire].[Hash] ([ExpireAt])\n\tINCLUDE ([Id]);\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Job_ExpireAt] ON [HangFire].[Job] ([ExpireAt])\n\tINCLUDE ([Id]);\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_List_ExpireAt] ON [HangFire].[List] ([ExpireAt])\n\tINCLUDE ([Id]);\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Set_ExpireAt] ON [HangFire].[Set] ([ExpireAt])\n\tINCLUDE ([Id]);\n\n\tPRINT 'Created indexes for [ExpireAt] columns';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Hash_Key] ON [HangFire].[Hash] ([Key] ASC)\n\tINCLUDE ([ExpireAt]);\n\tPRINT 'Created index [IX_HangFire_Hash_Key]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_List_Key] ON [HangFire].[List] ([Key] ASC)\n\tINCLUDE ([ExpireAt], [Value]);\n\tPRINT 'Created index [IX_HangFire_List_Key]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Set_Key] ON [HangFire].[Set] ([Key] ASC)\n\tINCLUDE ([ExpireAt], [Value]);\n\tPRINT 'Created index [IX_HangFire_Set_Key]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 4;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 4\nBEGIN\n\tPRINT 'Installing schema version 5';\n\n\tDROP INDEX [IX_HangFire_JobQueue_QueueAndFetchedAt] ON [HangFire].[JobQueue];\n\tPRINT 'Dropped index [IX_HangFire_JobQueue_QueueAndFetchedAt] to modify the [HangFire].[JobQueue].[Queue] column';\n\n\tALTER TABLE [HangFire].[JobQueue] ALTER COLUMN [Queue] NVARCHAR (50) NOT NULL;\n\tPRINT 'Modified [HangFire].[JobQueue].[Queue] length to 50';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_JobQueue_QueueAndFetchedAt] ON [HangFire].[JobQueue] (\n        [Queue] ASC,\n        [FetchedAt] ASC\n    );\n    PRINT 'Re-created index [IX_HangFire_JobQueue_QueueAndFetchedAt]';\n\n\tALTER TABLE [HangFire].[Server] DROP CONSTRAINT [PK_HangFire_Server]\n    PRINT 'Dropped constraint [PK_HangFire_Server] to modify the [HangFire].[Server].[Id] column';\n\n\tALTER TABLE [HangFire].[Server] ALTER COLUMN [Id] NVARCHAR (200) NOT NULL;\n\tPRINT 'Modified [HangFire].[Server].[Id] length to 200';\n\n\tALTER TABLE [HangFire].[Server] ADD  CONSTRAINT [PK_HangFire_Server] PRIMARY KEY CLUSTERED\n\t(\n\t\t[Id] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_Server]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 5;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 5 AND @DISABLE_HEAVY_MIGRATIONS = 1\nBEGIN\n    PRINT 'Migration process STOPPED at schema version ' + CAST(@CURRENT_SCHEMA_VERSION AS NVARCHAR) +\n          '. WILL NOT upgrade to schema version ' + CAST(@TARGET_SCHEMA_VERSION AS NVARCHAR) +\n          ', because @DISABLE_HEAVY_MIGRATIONS option is set.';\nEND\nELSE IF @CURRENT_SCHEMA_VERSION = 5\nBEGIN\n\tPRINT 'Installing schema version 6';\n\n\t-- First, we will drop all the secondary indexes on the HangFire.Set table, because we will\n\t-- modify that table, and unknown indexes may be added there (see https://github.com/HangfireIO/Hangfire/issues/844).\n\t-- So, we'll drop all of them, and then re-create the required index with a well-known name.\n\n\tDECLARE @dropIndexSql NVARCHAR(MAX) = N'';\n\tSELECT @dropIndexSql += N'DROP INDEX ' + QUOTENAME(SCHEMA_NAME(o.[schema_id])) + '.' + QUOTENAME(o.name) + '.' + QUOTENAME(i.name) + ';'\n\tFROM sys.indexes AS i\n\tINNER JOIN sys.tables AS o\n\tON i.[object_id] = o.[object_id]\n\tWHERE i.is_primary_key = 0\n\tAND i.index_id <> 0\n\tAND o.is_ms_shipped = 0\n\tAND SCHEMA_NAME(o.[schema_id]) = 'HangFire'\n\tAND o.name = 'Set';\n\n\tEXEC sp_executesql @dropIndexSql;\n\tPRINT 'Dropped all secondary indexes on the [Set] table';\n\n\t-- Next, we'll remove the unnecessary indexes. They were unnecessary in the previous schema,\n\t-- and are unnecessary in the new schema as well. We'll not re-create them.\n\n\tDROP INDEX [IX_HangFire_Hash_Key] ON [HangFire].[Hash];\n\tPRINT 'Dropped unnecessary index [IX_HangFire_Hash_Key]';\n\n\t-- Next, all the indexes that cover expiration will be filtered, to include only non-null values. This\n\t-- will prevent unnecessary index modifications – we are seeking these indexes only for non-null\n\t-- expiration time. Also, they include the Id column by a mistake. So we'll re-create them later in the\n\t-- migration.\n\n\tDROP INDEX [IX_HangFire_Hash_ExpireAt] ON [HangFire].[Hash];\n\tPRINT 'Dropped index [IX_HangFire_Hash_ExpireAt]';\n\n\tDROP INDEX [IX_HangFire_Job_ExpireAt] ON [HangFire].[Job];\n\tPRINT 'Dropped index [IX_HangFire_Job_ExpireAt]';\n\n\tDROP INDEX [IX_HangFire_List_ExpireAt] ON [HangFire].[List];\n\tPRINT 'Dropped index [IX_HangFire_List_ExpireAt]';\n\n\t-- IX_HangFire_Job_StateName index can also be optimized, since we are querying it only with a\n\t-- non-null state name. This will decrease the number of operations, when creating a background job.\n\t-- It will be recreated later in the migration.\n\n\tDROP INDEX [IX_HangFire_Job_StateName] ON [HangFire].Job;\n\tPRINT 'Dropped index [IX_HangFire_Job_StateName]';\n\n\t-- Dropping foreign key constraints based on the JobId column, because we need to modify the underlying\n\t-- column type of the clustered index to BIGINT. We'll recreate them later in the migration.\n\n\tALTER TABLE [HangFire].[JobParameter] DROP CONSTRAINT [FK_HangFire_JobParameter_Job];\n\tPRINT 'Dropped constraint [FK_HangFire_JobParameter_Job]';\n\n\tALTER TABLE [HangFire].[State] DROP CONSTRAINT [FK_HangFire_State_Job];\n\tPRINT 'Dropped constraint [FK_HangFire_State_Job]';\n\n\t-- We are going to create composite clustered indexes that are more natural for the following tables,\n\t-- so the following indexes will be unnecessary. Natural sorting will keep related data close to each\n\t-- other, and simplify the index modifications by the cost of fragmentation and additional page splits.\n\n\tDROP INDEX [UX_HangFire_CounterAggregated_Key] ON [HangFire].[AggregatedCounter];\n\tPRINT 'Dropped index [UX_HangFire_CounterAggregated_Key]';\n\n\tDROP INDEX [IX_HangFire_Counter_Key] ON [HangFire].[Counter];\n\tPRINT 'Dropped index [IX_HangFire_Counter_Key]';\n\n\tDROP INDEX [IX_HangFire_JobParameter_JobIdAndName] ON [HangFire].[JobParameter];\n\tPRINT 'Dropped index [IX_HangFire_JobParameter_JobIdAndName]';\n\n\tDROP INDEX [IX_HangFire_JobQueue_QueueAndFetchedAt] ON [HangFire].[JobQueue];\n\tPRINT 'Dropped index [IX_HangFire_JobQueue_QueueAndFetchedAt]';\n\n\tDROP INDEX [UX_HangFire_Hash_Key_Field] ON [HangFire].[Hash];\n\tPRINT 'Dropped index [UX_HangFire_Hash_Key_Field]';\n\n\tDROP INDEX [IX_HangFire_List_Key] ON [HangFire].[List];\n\tPRINT 'Dropped index [IX_HangFire_List_Key]';\n\n\tDROP INDEX [IX_HangFire_State_JobId] ON [HangFire].[State];\n\tPRINT 'Dropped index [IX_HangFire_State_JobId]';\n\n\t-- Then, we need to drop the primary key constraints, to modify id columns to the BIGINT type. Some of them\n\t-- will be re-created later in the migration. But some of them would be removed forever, because their\n\t-- uniqueness property sometimes unnecessary.\n\n\tALTER TABLE [HangFire].[AggregatedCounter] DROP CONSTRAINT [PK_HangFire_CounterAggregated];\n\tPRINT 'Dropped constraint [PK_HangFire_CounterAggregated]';\n\n\tALTER TABLE [HangFire].[Counter] DROP CONSTRAINT [PK_HangFire_Counter];\n\tPRINT 'Dropped constraint [PK_HangFire_Counter]';\n\n\tALTER TABLE [HangFire].[Hash] DROP CONSTRAINT [PK_HangFire_Hash];\n\tPRINT 'Dropped constraint [PK_HangFire_Hash]';\n\n\tALTER TABLE [HangFire].[Job] DROP CONSTRAINT [PK_HangFire_Job];\n\tPRINT 'Dropped constraint [PK_HangFire_Job]';\n\n\tALTER TABLE [HangFire].[JobParameter] DROP CONSTRAINT [PK_HangFire_JobParameter];\n\tPRINT 'Dropped constraint [PK_HangFire_JobParameter]';\n\n\tALTER TABLE [HangFire].[JobQueue] DROP CONSTRAINT [PK_HangFire_JobQueue];\n\tPRINT 'Dropped constraint [PK_HangFire_JobQueue]';\n\n\tALTER TABLE [HangFire].[List] DROP CONSTRAINT [PK_HangFire_List];\n\tPRINT 'Dropped constraint [PK_HangFire_List]';\n\n\tALTER TABLE [HangFire].[Set] DROP CONSTRAINT [PK_HangFire_Set];\n\tPRINT 'Dropped constraint [PK_HangFire_Set]';\n\n\tALTER TABLE [HangFire].[State] DROP CONSTRAINT [PK_HangFire_State];\n\tPRINT 'Dropped constraint [PK_HangFire_State]';\n\n\t-- We are removing identity columns of the following tables completely, their clustered\n\t-- index will be based on natural values. So, instead of modifying them to BIGINT, we\n\t-- are dropping them.\n\n\tALTER TABLE [HangFire].[AggregatedCounter] DROP COLUMN [Id];\n\tPRINT 'Dropped [AggregatedCounter].[Id] column, we will cluster on [Key] column with uniqufier';\n\n\tALTER TABLE [HangFire].[Counter] DROP COLUMN [Id];\n\tPRINT 'Dropped [Counter].[Id] column, we will cluster on [Key] column';\n\n\tALTER TABLE [HangFire].[Hash] DROP COLUMN [Id];\n\tPRINT 'Dropped [Hash].[Id] column, we will cluster on [Key]/[Field] columns';\n\n\tALTER TABLE [HangFire].[Set] DROP COLUMN [Id];\n\tPRINT 'Dropped [Set].[Id] column, we will cluster on [Key]/[Value] columns';\n\n\tALTER TABLE [HangFire].[JobParameter] DROP COLUMN [Id];\n\tPRINT 'Dropped [JobParameter].[Id] column, we will cluster on [JobId]/[Name] columns';\n\n\t-- Then we need to modify all the remaining Id columns to be of type BIGINT.\n\n\tALTER TABLE [HangFire].[List] ALTER COLUMN [Id] BIGINT NOT NULL;\n\tPRINT 'Modified [List].[Id] type to BIGINT';\n\n\tALTER TABLE [HangFire].[Job] ALTER COLUMN [Id] BIGINT NOT NULL;\n\tPRINT 'Modified [Job].[Id] type to BIGINT';\n\n\tALTER TABLE [HangFire].[Job] ALTER COLUMN [StateId] BIGINT NULL;\n\tPRINT 'Modified [Job].[StateId] type to BIGINT';\n\n\tALTER TABLE [HangFire].[JobParameter] ALTER COLUMN [JobId] BIGINT NOT NULL;\n\tPRINT 'Modified [JobParameter].[JobId] type to BIGINT';\n\n\tALTER TABLE [HangFire].[JobQueue] ALTER COLUMN [JobId] BIGINT NOT NULL;\n\tPRINT 'Modified [JobQueue].[JobId] type to BIGINT';\n\n\tALTER TABLE [HangFire].[JobQueue] ALTER COLUMN [Id] BIGINT NOT NULL;\n\tPRINT 'Modified [JobQueue].[Id] type to BIGINT';\n\n\tALTER TABLE [HangFire].[State] ALTER COLUMN [Id] BIGINT NOT NULL;\n\tPRINT 'Modified [State].[Id] type to BIGINT';\n\n\tALTER TABLE [HangFire].[State] ALTER COLUMN [JobId] BIGINT NOT NULL;\n\tPRINT 'Modified [State].[JobId] type to BIGINT';\n\n\tALTER TABLE [HangFire].[Counter] ALTER COLUMN [Value] INT NOT NULL;\n\tPRINT 'Modified [Counter].[Value] type to INT';\n\n\t-- Adding back all the Primary Key constraints or clustered indexes where PKs aren't appropriate.\n\n\tALTER TABLE [HangFire].[AggregatedCounter] ADD CONSTRAINT [PK_HangFire_CounterAggregated] PRIMARY KEY CLUSTERED (\n\t\t[Key] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_CounterAggregated]';\n\n\tCREATE CLUSTERED INDEX [CX_HangFire_Counter] ON [HangFire].[Counter] ([Key]);\n\tPRINT 'Created clustered index [CX_HangFire_Counter]';\n\n\tALTER TABLE [HangFire].[Hash] ADD CONSTRAINT [PK_HangFire_Hash] PRIMARY KEY CLUSTERED (\n\t\t[Key] ASC,\n\t\t[Field] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_Hash]';\n\n\tALTER TABLE [HangFire].[Job] ADD CONSTRAINT [PK_HangFire_Job] PRIMARY KEY CLUSTERED ([Id] ASC);\n\tPRINT 'Re-created constraint [PK_HangFire_Job]';\n\t\n\tALTER TABLE [HangFire].[JobParameter] ADD CONSTRAINT [PK_HangFire_JobParameter] PRIMARY KEY CLUSTERED (\n\t\t[JobId] ASC,\n\t\t[Name] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_JobParameter]';\n\n\tALTER TABLE [HangFire].[JobQueue] ADD CONSTRAINT [PK_HangFire_JobQueue] PRIMARY KEY CLUSTERED (\n\t\t[Queue] ASC,\n\t\t[Id] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_JobQueue]';\n\n\tALTER TABLE [HangFire].[List] ADD CONSTRAINT [PK_HangFire_List] PRIMARY KEY CLUSTERED (\n\t\t[Key] ASC,\n\t\t[Id] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_List]';\n\n\tALTER TABLE [HangFire].[Set] ADD CONSTRAINT [PK_HangFire_Set] PRIMARY KEY CLUSTERED (\n\t\t[Key] ASC,\n\t\t[Value] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_Set]';\n\n\tALTER TABLE [HangFire].[State] ADD CONSTRAINT [PK_HangFire_State] PRIMARY KEY CLUSTERED (\n\t\t[JobId] ASC,\n\t\t[Id]\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_State]';\n\n\t-- Creating secondary, nonclustered indexes\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Job_StateName] ON [HangFire].[Job] ([StateName])\n\tWHERE [StateName] IS NOT NULL;\n\tPRINT 'Re-created index [IX_HangFire_Job_StateName]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Set_Score] ON [HangFire].[Set] ([Score])\n\tWHERE [Score] IS NOT NULL;\n\tPRINT 'Created index [IX_HangFire_Set_Score]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Server_LastHeartbeat] ON [HangFire].[Server] ([LastHeartbeat]);\n\tPRINT 'Created index [IX_HangFire_Server_LastHeartbeat]';\n\n\t-- Creating filtered indexes for ExpireAt columns\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_AggregatedCounter_ExpireAt] ON [HangFire].[AggregatedCounter] ([ExpireAt])\n\tWHERE [ExpireAt] IS NOT NULL;\n\tPRINT 'Created index [IX_HangFire_AggregatedCounter_ExpireAt]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Hash_ExpireAt] ON [HangFire].[Hash] ([ExpireAt])\n\tWHERE [ExpireAt] IS NOT NULL;\n\tPRINT 'Re-created index [IX_HangFire_Hash_ExpireAt]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Job_ExpireAt] ON [HangFire].[Job] ([ExpireAt])\n\tINCLUDE ([StateName])\n\tWHERE [ExpireAt] IS NOT NULL;\n\tPRINT 'Re-created index [IX_HangFire_Job_ExpireAt]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_List_ExpireAt] ON [HangFire].[List] ([ExpireAt])\n\tWHERE [ExpireAt] IS NOT NULL;\n\tPRINT 'Re-created index [IX_HangFire_List_ExpireAt]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Set_ExpireAt] ON [HangFire].[Set] ([ExpireAt])\n\tWHERE [ExpireAt] IS NOT NULL;\n\tPRINT 'Re-created index [IX_HangFire_Set_ExpireAt]';\n\n\t-- Restoring foreign keys\n\n\tALTER TABLE [HangFire].[State] ADD CONSTRAINT [FK_HangFire_State_Job] FOREIGN KEY([JobId])\n\t\tREFERENCES [HangFire].[Job] ([Id])\n\t\tON UPDATE CASCADE\n\t\tON DELETE CASCADE;\n\tPRINT 'Re-created constraint [FK_HangFire_State_Job]';\n\n\tALTER TABLE [HangFire].[JobParameter] ADD CONSTRAINT [FK_HangFire_JobParameter_Job] FOREIGN KEY([JobId])\n\t\tREFERENCES [HangFire].[Job] ([Id])\n\t\tON UPDATE CASCADE\n\t\tON DELETE CASCADE;\n\tPRINT 'Re-created constraint [FK_HangFire_JobParameter_Job]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 6;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 6\nBEGIN\n\tPRINT 'Installing schema version 7';\n\n\tDROP INDEX [IX_HangFire_Set_Score] ON [HangFire].[Set];\n\tPRINT 'Dropped index [IX_HangFire_Set_Score]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Set_Score] ON [HangFire].[Set] ([Key], [Score]);\n\tPRINT 'Created index [IX_HangFire_Set_Score] with the proper composite key';\n\n\tSET @CURRENT_SCHEMA_VERSION = 7;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 7 AND @DISABLE_HEAVY_MIGRATIONS = 1\nBEGIN\n\tPRINT 'Migration process STOPPED at schema version ' + CAST(@CURRENT_SCHEMA_VERSION AS NVARCHAR) +\n\t\t'. WILL NOT upgrade to schema version ' + CAST(@TARGET_SCHEMA_VERSION AS NVARCHAR) +\n\t\t', because @DISABLE_HEAVY_MIGRATIONS option is set.';\nEND\nELSE IF @CURRENT_SCHEMA_VERSION = 7\nBEGIN\n\tPRINT 'Installing schema version 8';\n\n\tALTER TABLE [HangFire].[Server] DROP CONSTRAINT [PK_HangFire_Server]\n\tPRINT 'Dropped constraint [PK_HangFire_Server] to modify the [HangFire].[Server].[Id] column';\n\n\tALTER TABLE [HangFire].[Server] ALTER COLUMN [Id] NVARCHAR (200) NOT NULL;\n\tPRINT 'Modified [HangFire].[Server].[Id] length to 200';\n\n\tALTER TABLE [HangFire].[Server] ADD  CONSTRAINT [PK_HangFire_Server] PRIMARY KEY CLUSTERED ([Id] ASC);\n\tPRINT 'Re-created constraint [PK_HangFire_Server]';\n\n\t-- Nothing complicated - we just collecting all the secondary indexes and primary key names to delete them.\n\t-- We should expect nothing here, because custom columns and indexes can be applied for the [Counter] table\n\t-- to make replication work on Microsoft Azure, like in the issue below.\n\t-- https://github.com/HangfireIO/Hangfire/issues/1500\n\tDECLARE @dropIndexSql2 NVARCHAR(MAX) = N'';\n\tSELECT @dropIndexSql2 += N'DROP INDEX ' + QUOTENAME(SCHEMA_NAME(o.[schema_id])) + '.' + QUOTENAME(o.name) + '.' + QUOTENAME(i.name) + ';'\n\tFROM sys.indexes AS i\n\tINNER JOIN sys.tables AS o\n\tON i.[object_id] = o.[object_id]\n\tWHERE i.is_primary_key = 0\n\tAND i.index_id <> 0\n\tAND o.is_ms_shipped = 0\n\tAND SCHEMA_NAME(o.[schema_id]) = 'HangFire'\n\tAND o.name = 'Counter';\n\n\tSELECT @dropIndexSql2 += N'ALTER TABLE' + QUOTENAME(SCHEMA_NAME(o.[schema_id])) + '.' + QUOTENAME(o.name) + ' DROP CONSTRAINT ' + QUOTENAME(c.name) + ';'\n\tFROM sys.key_constraints c\n\tINNER JOIN sys.tables AS o\n\tON c.[parent_object_id] = o.[object_id]\n\tWHERE o.is_ms_shipped = 0\n\tAND SCHEMA_NAME(o.[schema_id]) = 'HangFire'\n\tAND o.name = 'Counter'\n\n\tEXEC sp_executesql @dropIndexSql2;\n\tPRINT 'Dropped all indexes on the [HangFire].[Counter] table';\n\n\t-- [Counter].[Id] column can already be added to make replication work as written above, so we will re-create it\n\t-- to ensure it is in the expected format.\n\tPRINT 'Checking for existence of the [HangFire].[Counter].[Id] column';\n\tIF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Counter' AND COLUMN_NAME = 'Id' AND TABLE_SCHEMA='HangFire')\n\tBEGIN\n\t\tALTER TABLE [HangFire].[Counter] DROP COLUMN [Id];\n\t\tPRINT 'Dropped [HangFire].[Counter].[Id] column';\n\tEND\n\n\tALTER TABLE [HangFire].[Counter] ADD [Id] BIGINT IDENTITY(1, 1);\n\tPRINT 'Created [HangFire].[Counter].[Id] column';\n\n\tALTER TABLE [HangFire].[Counter] ADD  CONSTRAINT [PK_HangFire_Counter] PRIMARY KEY CLUSTERED (\n\t\t[Key] ASC,\n\t\t[Id] ASC\n\t);\n\tPRINT 'Created clustered primary key PK_HangFire_Counter ([Key], [Id])';\n\n\t-- SqlServerStorageOptions.UseIgnoreDupKeyOption will yield much better results with INSERT/UPDATE operators\n\t-- instead of MERGE for [Set] and [Hash] tables. This change is also compatible with older clients, since\n\t-- MERGE operator is used there, so those clients are forward-compatible with these changes.\n\tALTER TABLE [HangFire].[Set] REBUILD WITH (IGNORE_DUP_KEY = ON);\n\tPRINT 'Enabled IGNORE_DUP_KEY option for [HangFire].[Set] table';\n\n\tALTER TABLE [HangFire].[Hash] REBUILD WITH (IGNORE_DUP_KEY = ON);\n\tPRINT 'Enabled IGNORE_DUP_KEY option for [HangFire].[Hash] table';\n\n\tALTER TABLE [HangFire].[JobQueue] DROP CONSTRAINT [PK_HangFire_JobQueue];\n\tPRINT 'Dropped constraint [PK_HangFire_JobQueue] to modify the [HangFire].[JobQueue].[Id] column';\n\n\tALTER TABLE [HangFire].[JobQueue] ALTER COLUMN [Id] BIGINT NOT NULL;\n\tPRINT 'Changed [HangFire].[JobQueue].[Id] column type to BIGINT';\n\n\tALTER TABLE [HangFire].[JobQueue] ADD CONSTRAINT [PK_HangFire_JobQueue] PRIMARY KEY CLUSTERED (\n\t\t[Queue] ASC,\n\t\t[Id] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_JobQueue]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 8;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 8\nBEGIN\n\tPRINT 'Installing schema version 9';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_State_CreatedAt] ON [HangFire].[State] ([CreatedAt] ASC)\n\n\tSET @CURRENT_SCHEMA_VERSION = 9;\nEND\n\n/*IF @CURRENT_SCHEMA_VERSION = 9\nBEGIN\n\tPRINT 'Installing schema version 10';\n\n\t Insert migration here\n\n\tSET @CURRENT_SCHEMA_VERSION = 10;\nEND*/\n\nUPDATE [HangFire].[Schema] SET [Version] = @CURRENT_SCHEMA_VERSION\nIF @@ROWCOUNT = 0 \n\tINSERT INTO [HangFire].[Schema] ([Version]) VALUES (@CURRENT_SCHEMA_VERSION)        \n\nPRINT 'Hangfire database schema installed';\n\nCOMMIT TRANSACTION;\nPRINT 'Hangfire SQL objects installed';\n\n"
  },
  {
    "path": "src/Hangfire.SqlServer/DefaultInstall.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ assembly name=\"$(TargetPath)\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n<#@ output extension=\".sql\" #>\n\n<#\n    var script = GetStringResource(\n        typeof(Hangfire.SqlServer.IPersistentJobQueue).Assembly, \n        \"Hangfire.SqlServer.Install.sql\");\n\n    script = script.Replace(\"$(HangFireSchema)\", \"HangFire\");\n\n    Write(script);\n#>\n\n<#+\nprivate string GetStringResource(System.Reflection.Assembly assembly, string resourceName)\n{\n    using (var stream = assembly.GetManifestResourceStream(resourceName))\n    {\n        if (stream == null) \n        {\n            throw new InvalidOperationException(String.Format(\n                \"Requested resource `{0}` was not found in the assembly `{1}`.\",\n                resourceName,\n                assembly));\n        }\n\n        using (var reader = new System.IO.StreamReader(stream))\n        {\n            return reader.ReadToEnd();\n        }\n    }\n}\n#>\n"
  },
  {
    "path": "src/Hangfire.SqlServer/EnqueuedAndFetchedCountDto.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.SqlServer\n{\n    public class EnqueuedAndFetchedCountDto\n    {\n        public int? EnqueuedCount { get; set; }\n        public int? FetchedCount { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/Entities/JobParameter.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.SqlServer.Entities\n{\n    internal sealed class JobParameter\n    {\n        public long JobId { get; set; }\n        public string Name { get; set; }\n        public string Value { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/Entities/Server.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.SqlServer.Entities\n{\n    internal sealed class Server\n    {\n        public string Id { get; set; }\n        public string Data { get; set; }\n        public DateTime LastHeartbeat { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/Entities/ServerData.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.SqlServer.Entities\n{\n    internal sealed class ServerData\n    {\n        public int WorkerCount { get; set; }\n        public string[] Queues { get; set; }\n        public DateTime? StartedAt { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/Entities/SqlHash.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.SqlServer.Entities\n{\n    internal sealed class SqlHash\n    {\n        public string Key { get; set; }\n        public string Field { get; set; }\n        public string Value { get; set; }\n        public DateTime? ExpireAt { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/Entities/SqlJob.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.SqlServer.Entities\n{\n    internal sealed class SqlJob\n    {\n        public long Id { get; set; }\n        public string InvocationData { get; set; }\n        public string Arguments { get; set; }\n        public DateTime CreatedAt { get; set; }\n        public DateTime? ExpireAt { get; set; }\n\n        public DateTime? FetchedAt { get; set; }\n\n        public string StateName { get; set; }\n        public string StateReason { get; set; }\n        public string StateData { get; set; }\n        public DateTime? StateChanged { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/Entities/SqlState.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.SqlServer.Entities\n{\n    internal sealed class SqlState\n    {\n        public long JobId { get; set; }\n        public string Name { get; set; }\n        public string Reason { get; set; }\n        public DateTime CreatedAt { get; set; }\n        public string Data { get; set; }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/ExceptionTypeHelper.cs",
    "content": "// This file is part of Hangfire. Copyright © 2022 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Reflection;\n\nnamespace Hangfire.SqlServer\n{\n    internal static class ExceptionTypeHelper\n    {\n        private static readonly Type OutOfMemoryType = typeof(OutOfMemoryException);\n#if !NETSTANDARD1_3\n        private static readonly Type StackOverflowType = typeof(StackOverflowException);\n        private static readonly Type ThreadAbortType = typeof(System.Threading.ThreadAbortException);\n        private static readonly Type AccessViolationType = typeof(AccessViolationException);\n#endif\n        private static readonly Type SecurityType = typeof(System.Security.SecurityException);\n \n        internal static bool IsCatchableExceptionType(this Exception e)\n        {\n            var type = e.GetType();\n            return type != OutOfMemoryType &&\n#if !NETSTANDARD1_3\n                   type != StackOverflowType &&\n                   type != ThreadAbortType &&\n                   type != AccessViolationType &&\n#endif\n                   !SecurityType.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo());\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/ExpirationManager.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Data;\nusing System.Data.Common;\nusing System.Globalization;\nusing System.Threading;\nusing Hangfire.Common;\nusing Hangfire.Logging;\nusing Hangfire.Server;\nusing Hangfire.Storage;\n\nnamespace Hangfire.SqlServer\n{\n#pragma warning disable 618\n    internal sealed class ExpirationManager : IServerComponent, IBackgroundProcess\n#pragma warning restore 618\n    {\n        private const string DistributedLockKey = \"locks:expirationmanager\";\n        private static readonly TimeSpan DefaultLockTimeout = TimeSpan.FromMinutes(5);\n        \n        // This value should be high enough to optimize the deletion as much, as possible,\n        // reducing the number of queries. But low enough to cause lock escalations (it\n        // appears, when ~5000 locks were taken, but this number is a subject of version).\n        // Note, that lock escalation may also happen during the cascade deletions for\n        // State (3-5 rows/job usually) and JobParameters (2-3 rows/job usually) tables.\n        private const int DefaultNumberOfRecordsInSinglePass = 1000;\n        \n        private static readonly string[] ProcessedTables =\n        {\n            \"AggregatedCounter\",\n            \"Job\",\n            \"List\",\n            \"Set\",\n            \"Hash\",\n        };\n\n        private readonly ILog _logger = LogProvider.For<ExpirationManager>();\n        private readonly SqlServerStorage _storage;\n        private readonly TimeSpan _stateExpirationTimeout;\n        private readonly TimeSpan _checkInterval;\n\n        public ExpirationManager(SqlServerStorage storage, TimeSpan stateExpirationTimeout, TimeSpan checkInterval)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (stateExpirationTimeout < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(stateExpirationTimeout), \"Timeout value should be equal to or greater than zero.\");\n            if (checkInterval <= TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(checkInterval), \"Timeout value should be greater than zero.\");\n\n            _storage = storage;\n            _stateExpirationTimeout = stateExpirationTimeout;\n            _checkInterval = checkInterval;\n        }\n\n        public void Execute(CancellationToken cancellationToken)\n        {\n            ExecuteCore(_storage, cancellationToken);\n        }\n\n        public void Execute(BackgroundProcessContext context)\n        {\n            if (context.Storage is not SqlServerStorage storage)\n            {\n                return;\n            }\n\n            ExecuteCore(storage, context.StoppingToken);\n        }\n        \n        private void ExecuteCore(SqlServerStorage storage, CancellationToken cancellationToken)\n        {\n            var numberOfRecordsInSinglePass = storage.Options.DeleteExpiredBatchSize;\n            if (numberOfRecordsInSinglePass <= 0 || numberOfRecordsInSinglePass > 100_000)\n            {\n                numberOfRecordsInSinglePass = DefaultNumberOfRecordsInSinglePass;\n            }\n\n            foreach (var table in ProcessedTables)\n            {\n                try\n                {\n                    CleanupTable(storage, GetExpireQuery(storage, table), table, numberOfRecordsInSinglePass, cancellationToken);\n                }\n                catch (DbException ex)\n                {\n                    _logger.ErrorException($\"Error occurred while cleaning up the '{table}' table: {ex.Message}\", ex);\n                }\n            }\n\n            if (_stateExpirationTimeout > TimeSpan.Zero)\n            {\n                try\n                {\n                    CleanupTable(storage, GetStateCleanupQuery(storage), \"State\", numberOfRecordsInSinglePass,\n                        cancellationToken,\n                        command => command.AddParameter(\"@expireMin\", (long)_stateExpirationTimeout.Negate().TotalMinutes, DbType.Int64));\n                }\n                catch (DbException ex)\n                {\n                    _logger.ErrorException($\"Error occurred while cleaning up the 'State' table: {ex.Message}\", ex);\n                }\n            }\n\n            cancellationToken.Wait(_checkInterval);\n        }\n\n        public override string ToString()\n        {\n            return GetType().ToString();\n        }\n\n        private void CleanupTable(SqlServerStorage storage, string query, string table, int numberOfRecordsInSinglePass, CancellationToken cancellationToken, Action<DbCommand> additionalActions = null)\n        {\n            _logger.Debug($\"Removing outdated records from the '{table}' table...\");\n\n            UseConnectionDistributedLock(storage, connection =>\n            {\n                int affected;\n\n                do\n                {\n                    affected = ExecuteNonQuery(\n                        connection,\n                        query,\n                        numberOfRecordsInSinglePass,\n                        additionalActions,\n                        cancellationToken);\n                } while (affected == numberOfRecordsInSinglePass);\n\n                return affected;\n            });\n\n            _logger.Trace($\"Outdated records removed from the '{table}' table.\");\n        }\n\n        private T UseConnectionDistributedLock<T>(SqlServerStorage storage, Func<DbConnection, T> action)\n        {\n            try\n            {\n                return storage.UseConnection(null, static (_, connection, ctx) =>\n                {\n                    SqlServerDistributedLock.Acquire(connection, DistributedLockKey, DefaultLockTimeout);\n\n                    try\n                    {\n                        return ctx(connection);\n                    }\n                    finally\n                    {\n                        SqlServerDistributedLock.Release(connection, DistributedLockKey);\n                    }\n                }, action);\n            }\n            catch (DistributedLockTimeoutException e) when (e.Resource == DistributedLockKey)\n            {\n                // DistributedLockTimeoutException here doesn't mean that outdated records weren't removed.\n                // It just means another Hangfire server did this work.\n                _logger.Log(\n                    LogLevel.Debug,\n                    () => $@\"An exception was thrown during acquiring distributed lock on the {DistributedLockKey} resource within {DefaultLockTimeout.TotalSeconds} seconds. Outdated records were not removed.\nIt will be retried in {_checkInterval.TotalSeconds} seconds.\",\n                    e);\n                return default;\n            }\n        }\n\n        private static string GetExpireQuery(SqlServerStorage storage, string table)\n        {\n            if (table.Equals(\"AggregatedCounter\", StringComparison.OrdinalIgnoreCase))\n            {\n                // Schema 5, which still should be supported by Hangfire doesn't have an index that covers\n                // the `ExpireAt` column, making it impossible to run the query.\n                return String.Format(CultureInfo.InvariantCulture, storage.GetQueryFromTemplate(static schemaName => $@\"\nset deadlock_priority low;\nset transaction isolation level read committed;\nset xact_abort on;\nset lock_timeout 1000;\ndelete top (@count) T from [{schemaName}].[{{0}}] T\nwhere ExpireAt < @now\noption (loop join, optimize for (@count = 20000));\"), table);\n            }\n\n            return String.Format(CultureInfo.InvariantCulture, storage.GetQueryFromTemplate(static schemaName => $@\"\nset deadlock_priority low;\nset transaction isolation level read committed;\nset xact_abort on;\nset lock_timeout 1000;\ndelete top (@count) T from [{schemaName}].[{{0}}] T with (forceseek)\nwhere ExpireAt < @now\noption (loop join, optimize for (@count = 20000));\"), table);\n        }\n\n        private static string GetStateCleanupQuery(SqlServerStorage storage)\n        {\n            // TODO: Make expiration condition configurable\n            return storage.GetQueryFromTemplate(static schemaName => $@\"\nset deadlock_priority low;\nset transaction isolation level read committed;\nset xact_abort on;\nset lock_timeout 1000;\n\n;with cte as (\n\tselect s.[JobId], s.[Id]\n\tfrom [{schemaName}].[State] s with (forceseek)\n\twhere s.[CreatedAt] < dateadd(minute, @expireMin, @now)\n\tand exists (\n\t\tselect * from [{schemaName}].[Job] j with (forceseek)\n\t\twhere j.[Id] = s.[JobId] and j.[StateId] != s.[Id]))\ndelete top(@count) from cte option (maxdop 1);\");\n        }\n\n        private static int ExecuteNonQuery(\n            DbConnection connection,\n            string commandText,\n            int numberOfRecordsInSinglePass,\n            Action<DbCommand> additionalActions,\n            CancellationToken cancellationToken)\n        {\n            using var command = connection.Create(commandText, timeout: 0)\n                .AddParameter(\"@count\", numberOfRecordsInSinglePass, DbType.Int32)\n                .AddParameter(\"@now\", DateTime.UtcNow, DbType.DateTime);\n\n            additionalActions?.Invoke(command);\n\n            using (cancellationToken.Register(static state => ((DbCommand)state).Cancel(), command))\n            {\n                try\n                {\n                    return command.ExecuteNonQuery();\n                }\n                catch (DbException ex) when (cancellationToken.IsCancellationRequested || ex.Message.Contains(\"Lock request time out period exceeded\"))\n                {\n                    // Exception was triggered due to the Cancel method call, ignoring\n                    return 0;\n                }\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/Hangfire.SqlServer/Hangfire.SqlServer.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n  <PropertyGroup>\r\n    <TargetFrameworks>net451;netstandard1.3;netstandard2.0;</TargetFrameworks>\r\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\r\n    <RootNamespace>Hangfire.SqlServer</RootNamespace>\r\n  </PropertyGroup>\r\n  \r\n  <PropertyGroup Condition=\"'$(TargetFramework)'=='net451'\">\r\n    <DefineConstants>$(DefineConstants);FEATURE_TRANSACTIONSCOPE;FEATURE_CONFIGURATIONMANAGER</DefineConstants>\r\n  </PropertyGroup>\r\n  \r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\Hangfire.Core\\Hangfire.Core.csproj\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net451'\">\r\n    <Reference Include=\"System.Configuration\" />\r\n    <Reference Include=\"System.Transactions\" />\r\n\r\n    <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies\" Version=\"1.0.3\" PrivateAssets=\"all\" />\r\n    <PackageReference Include=\"Dapper\" Version=\"1.60.6\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='netstandard1.3'\">\r\n    <PackageReference Include=\"Dapper\" Version=\"1.60.6\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='netstandard2.0'\">\r\n    <PackageReference Include=\"Dapper\" Version=\"2.1.28\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='netstandard1.3' or '$(TargetFramework)'=='netstandard2.0'\">\r\n    <Compile Remove=\"SqlServerBootstrapperConfigurationExtensions.cs\" />\r\n  </ItemGroup>\r\n  \r\n  <ItemGroup>\r\n    <EmbeddedResource Include=\"Install.sql\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Service Include=\"{508349B6-6B84-4DF5-91F0-309BEEBAD82D}\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <None Update=\"DefaultInstall.sql\">\r\n      <DesignTime>True</DesignTime>\r\n      <AutoGen>True</AutoGen>\r\n      <DependentUpon>DefaultInstall.tt</DependentUpon>\r\n    </None>\r\n    <None Update=\"DefaultInstall.tt\">\r\n      <Generator>TextTemplatingFileGenerator</Generator>\r\n      <LastGenOutput>DefaultInstall.sql</LastGenOutput>\r\n    </None>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "src/Hangfire.SqlServer/IPersistentJobQueue.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Diagnostics.CodeAnalysis;\nusing System.Threading;\nusing Hangfire.Storage;\n\nnamespace Hangfire.SqlServer\n{\n    [SuppressMessage(\"Naming\", \"CA1711:Identifiers should not have incorrect suffix\", Justification = \"This interface represents a persistent queue by design.\")]\n    public interface IPersistentJobQueue\n    {\n        IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken);\n\n#if FEATURE_TRANSACTIONSCOPE\n        void Enqueue(System.Data.IDbConnection connection, string queue, string jobId);\n#else\n        void Enqueue(\n            System.Data.Common.DbConnection connection, \n            System.Data.Common.DbTransaction transaction, \n            string queue, \n            string jobId);\n#endif\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/IPersistentJobQueueMonitoringApi.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\n\nnamespace Hangfire.SqlServer\n{\n    public interface IPersistentJobQueueMonitoringApi\n    {\n        IEnumerable<string> GetQueues();\n\n        IEnumerable<long> GetEnqueuedJobIds(string queue, int from, int perPage);\n\n        // TODO: Extend return type by including DateTime to allow getting the FetchedAt value in 2.0\n        IEnumerable<long> GetFetchedJobIds(string queue, int from, int perPage);\n\n        EnqueuedAndFetchedCountDto GetEnqueuedAndFetchedCount(string queue);\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/IPersistentJobQueueProvider.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.SqlServer\n{\n    public interface IPersistentJobQueueProvider\n    {\n        IPersistentJobQueue GetJobQueue();\n        IPersistentJobQueueMonitoringApi GetJobQueueMonitoringApi();\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/Install.sql",
    "content": "﻿-- This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n-- \n-- Hangfire is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Lesser General Public License as \n-- published by the Free Software Foundation, either version 3 \n-- of the License, or any later version.\n-- \n-- Hangfire is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Lesser General Public License for more details.\n-- \n-- You should have received a copy of the GNU Lesser General Public \n-- License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nSET NOCOUNT ON\nSET XACT_ABORT ON\nDECLARE @TARGET_SCHEMA_VERSION INT;\nDECLARE @DISABLE_HEAVY_MIGRATIONS BIT;\nSET @TARGET_SCHEMA_VERSION = 9;\n--SET @DISABLE_HEAVY_MIGRATIONS = 1;\n\nPRINT 'Installing Hangfire SQL objects...';\n\nBEGIN TRANSACTION;\n\n-- Acquire exclusive lock to prevent deadlocks caused by schema creation / version update\nDECLARE @SchemaLockResult INT;\nEXEC @SchemaLockResult = sp_getapplock @Resource = '$(HangFireSchema):SchemaLock', @LockMode = 'Exclusive'\n\n-- Create the database schema if it doesn't exists\nIF NOT EXISTS (SELECT [schema_id] FROM [sys].[schemas] WHERE [name] = '$(HangFireSchema)')\nBEGIN\n    EXEC (N'CREATE SCHEMA [$(HangFireSchema)]');\n    PRINT 'Created database schema [$(HangFireSchema)]';\nEND\nELSE\n    PRINT 'Database schema [$(HangFireSchema)] already exists';\n    \nDECLARE @SCHEMA_ID int;\nSELECT @SCHEMA_ID = [schema_id] FROM [sys].[schemas] WHERE [name] = '$(HangFireSchema)';\n\n-- Create the [$(HangFireSchema)].Schema table if not exists\nIF NOT EXISTS(SELECT [object_id] FROM [sys].[tables] \n    WHERE [name] = 'Schema' AND [schema_id] = @SCHEMA_ID)\nBEGIN\n    CREATE TABLE [$(HangFireSchema)].[Schema](\n        [Version] [int] NOT NULL,\n        CONSTRAINT [PK_HangFire_Schema] PRIMARY KEY CLUSTERED ([Version] ASC)\n    );\n    PRINT 'Created table [$(HangFireSchema)].[Schema]';\nEND\nELSE\n    PRINT 'Table [$(HangFireSchema)].[Schema] already exists';\n    \nDECLARE @CURRENT_SCHEMA_VERSION int;\nSELECT @CURRENT_SCHEMA_VERSION = [Version] FROM [$(HangFireSchema)].[Schema];\n\nPRINT 'Current Hangfire schema version: ' + CASE WHEN @CURRENT_SCHEMA_VERSION IS NULL THEN 'none' ELSE CONVERT(nvarchar, @CURRENT_SCHEMA_VERSION) END;\n\nIF @CURRENT_SCHEMA_VERSION IS NOT NULL AND @CURRENT_SCHEMA_VERSION > @TARGET_SCHEMA_VERSION\nBEGIN\n    ROLLBACK TRANSACTION;\n    PRINT 'Hangfire current database schema version ' + CAST(@CURRENT_SCHEMA_VERSION AS NVARCHAR) +\n          ' is newer than the configured SqlServerStorage schema version ' + CAST(@TARGET_SCHEMA_VERSION AS NVARCHAR) +\n          '. Will not apply any migrations.';\n    RETURN;\nEND\n\n-- Install [$(HangFireSchema)] schema objects\nIF @CURRENT_SCHEMA_VERSION IS NULL\nBEGIN\n    IF @DISABLE_HEAVY_MIGRATIONS = 1\n    BEGIN\n        SET @DISABLE_HEAVY_MIGRATIONS = 0;\n        PRINT 'Enabling HEAVY_MIGRATIONS, because we are installing objects from scratch';\n    END\n\n    PRINT 'Installing schema version 1';\n        \n    -- Create job tables\n    CREATE TABLE [$(HangFireSchema)].[Job] (\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n\t\t[StateId] [int] NULL,\n\t\t[StateName] [nvarchar](20) NULL, -- To speed-up queries.\n        [InvocationData] [nvarchar](max) NOT NULL,\n        [Arguments] [nvarchar](max) NOT NULL,\n        [CreatedAt] [datetime] NOT NULL,\n        [ExpireAt] [datetime] NULL,\n\n        CONSTRAINT [PK_HangFire_Job] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [$(HangFireSchema)].[Job]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Job_StateName] ON [$(HangFireSchema)].[Job] ([StateName] ASC);\n\tPRINT 'Created index [IX_HangFire_Job_StateName]';\n        \n    -- Job history table\n        \n    CREATE TABLE [$(HangFireSchema)].[State] (\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [JobId] [int] NOT NULL,\n\t\t[Name] [nvarchar](20) NOT NULL,\n\t\t[Reason] [nvarchar](100) NULL,\n        [CreatedAt] [datetime] NOT NULL,\n        [Data] [nvarchar](max) NULL,\n            \n        CONSTRAINT [PK_HangFire_State] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [$(HangFireSchema)].[State]';\n\n    ALTER TABLE [$(HangFireSchema)].[State] ADD CONSTRAINT [FK_HangFire_State_Job] FOREIGN KEY([JobId])\n        REFERENCES [$(HangFireSchema)].[Job] ([Id])\n        ON UPDATE CASCADE\n        ON DELETE CASCADE;\n    PRINT 'Created constraint [FK_HangFire_State_Job]';\n        \n    CREATE NONCLUSTERED INDEX [IX_HangFire_State_JobId] ON [$(HangFireSchema)].[State] ([JobId] ASC);\n    PRINT 'Created index [IX_HangFire_State_JobId]';\n        \n    -- Job parameters table\n        \n    CREATE TABLE [$(HangFireSchema)].[JobParameter](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [JobId] [int] NOT NULL,\n        [Name] [nvarchar](40) NOT NULL,\n        [Value] [nvarchar](max) NULL,\n            \n        CONSTRAINT [PK_HangFire_JobParameter] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [$(HangFireSchema)].[JobParameter]';\n\n    ALTER TABLE [$(HangFireSchema)].[JobParameter] ADD CONSTRAINT [FK_HangFire_JobParameter_Job] FOREIGN KEY([JobId])\n        REFERENCES [$(HangFireSchema)].[Job] ([Id])\n        ON UPDATE CASCADE\n        ON DELETE CASCADE;\n    PRINT 'Created constraint [FK_HangFire_JobParameter_Job]';\n        \n    CREATE NONCLUSTERED INDEX [IX_HangFire_JobParameter_JobIdAndName] ON [$(HangFireSchema)].[JobParameter] (\n        [JobId] ASC,\n        [Name] ASC\n    );\n    PRINT 'Created index [IX_HangFire_JobParameter_JobIdAndName]';\n        \n    -- Job queue table\n        \n    CREATE TABLE [$(HangFireSchema)].[JobQueue](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [JobId] [int] NOT NULL,\n        [Queue] [nvarchar](20) NOT NULL,\n        [FetchedAt] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_JobQueue] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [$(HangFireSchema)].[JobQueue]';\n        \n    CREATE NONCLUSTERED INDEX [IX_HangFire_JobQueue_JobIdAndQueue] ON [$(HangFireSchema)].[JobQueue] (\n        [JobId] ASC,\n        [Queue] ASC\n    );\n    PRINT 'Created index [IX_HangFire_JobQueue_JobIdAndQueue]';\n        \n    CREATE NONCLUSTERED INDEX [IX_HangFire_JobQueue_QueueAndFetchedAt] ON [$(HangFireSchema)].[JobQueue] (\n        [Queue] ASC,\n        [FetchedAt] ASC\n    );\n    PRINT 'Created index [IX_HangFire_JobQueue_QueueAndFetchedAt]';\n        \n    -- Servers table\n        \n    CREATE TABLE [$(HangFireSchema)].[Server](\n        [Id] [nvarchar](200) NOT NULL,\n        [Data] [nvarchar](max) NULL,\n        [LastHeartbeat] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_Server] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [$(HangFireSchema)].[Server]';\n        \n    -- Extension tables\n        \n    CREATE TABLE [$(HangFireSchema)].[Hash](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [Key] [nvarchar](100) NOT NULL,\n        [Name] [nvarchar](40) NOT NULL,\n        [StringValue] [nvarchar](max) NULL,\n        [IntValue] [int] NULL,\n        [ExpireAt] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_Hash] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [$(HangFireSchema)].[Hash]';\n        \n    CREATE UNIQUE NONCLUSTERED INDEX [UX_HangFire_Hash_KeyAndName] ON [$(HangFireSchema)].[Hash] (\n        [Key] ASC,\n        [Name] ASC\n    );\n    PRINT 'Created index [UX_HangFire_Hash_KeyAndName]';\n        \n    CREATE TABLE [$(HangFireSchema)].[List](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [Key] [nvarchar](100) NOT NULL,\n        [Value] [nvarchar](max) NULL,\n        [ExpireAt] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_List] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [$(HangFireSchema)].[List]';\n        \n    CREATE TABLE [$(HangFireSchema)].[Set](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [Key] [nvarchar](100) NOT NULL,\n        [Score] [float] NOT NULL,\n        [Value] [nvarchar](256) NOT NULL,\n        [ExpireAt] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_Set] PRIMARY KEY CLUSTERED ([Id] ASC)\n    );\n    PRINT 'Created table [$(HangFireSchema)].[Set]';\n        \n    CREATE UNIQUE NONCLUSTERED INDEX [UX_HangFire_Set_KeyAndValue] ON [$(HangFireSchema)].[Set] (\n        [Key] ASC,\n        [Value] ASC\n    );\n    PRINT 'Created index [UX_HangFire_Set_KeyAndValue]';\n        \n    CREATE TABLE [$(HangFireSchema)].[Value](\n        [Id] [int] IDENTITY(1,1) NOT NULL,\n        [Key] [nvarchar](100) NOT NULL,\n        [StringValue] [nvarchar](max) NULL,\n        [IntValue] [int] NULL,\n        [ExpireAt] [datetime] NULL,\n            \n        CONSTRAINT [PK_HangFire_Value] PRIMARY KEY CLUSTERED (\n            [Id] ASC\n        )\n    );\n    PRINT 'Created table [$(HangFireSchema)].[Value]';\n        \n    CREATE UNIQUE NONCLUSTERED INDEX [UX_HangFire_Value_Key] ON [$(HangFireSchema)].[Value] (\n        [Key] ASC\n    );\n    PRINT 'Created index [UX_HangFire_Value_Key]';\n\n\tCREATE TABLE [$(HangFireSchema)].[Counter](\n\t\t[Id] [int] IDENTITY(1,1) NOT NULL,\n\t\t[Key] [nvarchar](100) NOT NULL,\n\t\t[Value] [tinyint] NOT NULL,\n\t\t[ExpireAt] [datetime] NULL,\n\n\t\tCONSTRAINT [PK_HangFire_Counter] PRIMARY KEY CLUSTERED ([Id] ASC)\n\t);\n\tPRINT 'Created table [$(HangFireSchema)].[Counter]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Counter_Key] ON [$(HangFireSchema)].[Counter] ([Key] ASC)\n\tINCLUDE ([Value]);\n\tPRINT 'Created index [IX_HangFire_Counter_Key]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 1;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 1\nBEGIN\n\tPRINT 'Installing schema version 2';\n\n\t-- https://github.com/odinserj/HangFire/issues/83\n\n\tDROP INDEX [IX_HangFire_Counter_Key] ON [$(HangFireSchema)].[Counter];\n\n\tALTER TABLE [$(HangFireSchema)].[Counter] ALTER COLUMN [Value] SMALLINT NOT NULL;\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Counter_Key] ON [$(HangFireSchema)].[Counter] ([Key] ASC)\n\tINCLUDE ([Value]);\n\tPRINT 'Index [IX_HangFire_Counter_Key] re-created';\n\n\tDROP TABLE [$(HangFireSchema)].[Value];\n\tDROP TABLE [$(HangFireSchema)].[Hash];\n\tPRINT 'Dropped tables [$(HangFireSchema)].[Value] and [$(HangFireSchema)].[Hash]'\n\n\tDELETE FROM [$(HangFireSchema)].[Server] WHERE [LastHeartbeat] IS NULL;\n\tALTER TABLE [$(HangFireSchema)].[Server] ALTER COLUMN [LastHeartbeat] DATETIME NOT NULL;\n\n\tSET @CURRENT_SCHEMA_VERSION = 2;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 2\nBEGIN\n\tPRINT 'Installing schema version 3';\n\n\tDROP INDEX [IX_HangFire_JobQueue_JobIdAndQueue] ON [$(HangFireSchema)].[JobQueue];\n\tPRINT 'Dropped index [IX_HangFire_JobQueue_JobIdAndQueue]';\n\n\tCREATE TABLE [$(HangFireSchema)].[Hash](\n\t\t[Id] [int] IDENTITY(1,1) NOT NULL,\n\t\t[Key] [nvarchar](100) NOT NULL,\n\t\t[Field] [nvarchar](100) NOT NULL,\n\t\t[Value] [nvarchar](max) NULL,\n\t\t[ExpireAt] [datetime2](7) NULL,\n\t\t\n\t\tCONSTRAINT [PK_HangFire_Hash] PRIMARY KEY CLUSTERED ([Id] ASC)\n\t);\n\tPRINT 'Created table [$(HangFireSchema)].[Hash]';\n\n\tCREATE UNIQUE NONCLUSTERED INDEX [UX_HangFire_Hash_Key_Field] ON [$(HangFireSchema)].[Hash] (\n\t\t[Key] ASC,\n\t\t[Field] ASC\n\t);\n\tPRINT 'Created index [UX_HangFire_Hash_Key_Field]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 3;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 3\nBEGIN\n\tPRINT 'Installing schema version 4';\n\n\tCREATE TABLE [$(HangFireSchema)].[AggregatedCounter] (\n\t\t[Id] [int] IDENTITY(1,1) NOT NULL,\n\t\t[Key] [nvarchar](100) NOT NULL,\n\t\t[Value] [bigint] NOT NULL,\n\t\t[ExpireAt] [datetime] NULL,\n\n\t\tCONSTRAINT [PK_HangFire_CounterAggregated] PRIMARY KEY CLUSTERED ([Id] ASC)\n\t);\n\tPRINT 'Created table [$(HangFireSchema)].[AggregatedCounter]';\n\n\tCREATE UNIQUE NONCLUSTERED INDEX [UX_HangFire_CounterAggregated_Key] ON [$(HangFireSchema)].[AggregatedCounter] (\n\t\t[Key] ASC\n\t) INCLUDE ([Value]);\n\tPRINT 'Created index [UX_HangFire_CounterAggregated_Key]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Hash_ExpireAt] ON [$(HangFireSchema)].[Hash] ([ExpireAt])\n\tINCLUDE ([Id]);\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Job_ExpireAt] ON [$(HangFireSchema)].[Job] ([ExpireAt])\n\tINCLUDE ([Id]);\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_List_ExpireAt] ON [$(HangFireSchema)].[List] ([ExpireAt])\n\tINCLUDE ([Id]);\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Set_ExpireAt] ON [$(HangFireSchema)].[Set] ([ExpireAt])\n\tINCLUDE ([Id]);\n\n\tPRINT 'Created indexes for [ExpireAt] columns';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Hash_Key] ON [$(HangFireSchema)].[Hash] ([Key] ASC)\n\tINCLUDE ([ExpireAt]);\n\tPRINT 'Created index [IX_HangFire_Hash_Key]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_List_Key] ON [$(HangFireSchema)].[List] ([Key] ASC)\n\tINCLUDE ([ExpireAt], [Value]);\n\tPRINT 'Created index [IX_HangFire_List_Key]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Set_Key] ON [$(HangFireSchema)].[Set] ([Key] ASC)\n\tINCLUDE ([ExpireAt], [Value]);\n\tPRINT 'Created index [IX_HangFire_Set_Key]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 4;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 4\nBEGIN\n\tPRINT 'Installing schema version 5';\n\n\tDROP INDEX [IX_HangFire_JobQueue_QueueAndFetchedAt] ON [$(HangFireSchema)].[JobQueue];\n\tPRINT 'Dropped index [IX_HangFire_JobQueue_QueueAndFetchedAt] to modify the [$(HangFireSchema)].[JobQueue].[Queue] column';\n\n\tALTER TABLE [$(HangFireSchema)].[JobQueue] ALTER COLUMN [Queue] NVARCHAR (50) NOT NULL;\n\tPRINT 'Modified [$(HangFireSchema)].[JobQueue].[Queue] length to 50';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_JobQueue_QueueAndFetchedAt] ON [$(HangFireSchema)].[JobQueue] (\n        [Queue] ASC,\n        [FetchedAt] ASC\n    );\n    PRINT 'Re-created index [IX_HangFire_JobQueue_QueueAndFetchedAt]';\n\n\tALTER TABLE [$(HangFireSchema)].[Server] DROP CONSTRAINT [PK_HangFire_Server]\n    PRINT 'Dropped constraint [PK_HangFire_Server] to modify the [HangFire].[Server].[Id] column';\n\n\tALTER TABLE [$(HangFireSchema)].[Server] ALTER COLUMN [Id] NVARCHAR (200) NOT NULL;\n\tPRINT 'Modified [$(HangFireSchema)].[Server].[Id] length to 200';\n\n\tALTER TABLE [$(HangFireSchema)].[Server] ADD  CONSTRAINT [PK_HangFire_Server] PRIMARY KEY CLUSTERED\n\t(\n\t\t[Id] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_Server]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 5;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 5 AND @DISABLE_HEAVY_MIGRATIONS = 1\nBEGIN\n    PRINT 'Migration process STOPPED at schema version ' + CAST(@CURRENT_SCHEMA_VERSION AS NVARCHAR) +\n          '. WILL NOT upgrade to schema version ' + CAST(@TARGET_SCHEMA_VERSION AS NVARCHAR) +\n          ', because @DISABLE_HEAVY_MIGRATIONS option is set.';\nEND\nELSE IF @CURRENT_SCHEMA_VERSION = 5\nBEGIN\n\tPRINT 'Installing schema version 6';\n\n\t-- First, we will drop all the secondary indexes on the HangFire.Set table, because we will\n\t-- modify that table, and unknown indexes may be added there (see https://github.com/HangfireIO/Hangfire/issues/844).\n\t-- So, we'll drop all of them, and then re-create the required index with a well-known name.\n\n\tDECLARE @dropIndexSql NVARCHAR(MAX) = N'';\n\tSELECT @dropIndexSql += N'DROP INDEX ' + QUOTENAME(SCHEMA_NAME(o.[schema_id])) + '.' + QUOTENAME(o.name) + '.' + QUOTENAME(i.name) + ';'\n\tFROM sys.indexes AS i\n\tINNER JOIN sys.tables AS o\n\tON i.[object_id] = o.[object_id]\n\tWHERE i.is_primary_key = 0\n\tAND i.index_id <> 0\n\tAND o.is_ms_shipped = 0\n\tAND SCHEMA_NAME(o.[schema_id]) = '$(HangFireSchema)'\n\tAND o.name = 'Set';\n\n\tEXEC sp_executesql @dropIndexSql;\n\tPRINT 'Dropped all secondary indexes on the [Set] table';\n\n\t-- Next, we'll remove the unnecessary indexes. They were unnecessary in the previous schema,\n\t-- and are unnecessary in the new schema as well. We'll not re-create them.\n\n\tDROP INDEX [IX_HangFire_Hash_Key] ON [$(HangFireSchema)].[Hash];\n\tPRINT 'Dropped unnecessary index [IX_HangFire_Hash_Key]';\n\n\t-- Next, all the indexes that cover expiration will be filtered, to include only non-null values. This\n\t-- will prevent unnecessary index modifications – we are seeking these indexes only for non-null\n\t-- expiration time. Also, they include the Id column by a mistake. So we'll re-create them later in the\n\t-- migration.\n\n\tDROP INDEX [IX_HangFire_Hash_ExpireAt] ON [$(HangFireSchema)].[Hash];\n\tPRINT 'Dropped index [IX_HangFire_Hash_ExpireAt]';\n\n\tDROP INDEX [IX_HangFire_Job_ExpireAt] ON [$(HangFireSchema)].[Job];\n\tPRINT 'Dropped index [IX_HangFire_Job_ExpireAt]';\n\n\tDROP INDEX [IX_HangFire_List_ExpireAt] ON [$(HangFireSchema)].[List];\n\tPRINT 'Dropped index [IX_HangFire_List_ExpireAt]';\n\n\t-- IX_HangFire_Job_StateName index can also be optimized, since we are querying it only with a\n\t-- non-null state name. This will decrease the number of operations, when creating a background job.\n\t-- It will be recreated later in the migration.\n\n\tDROP INDEX [IX_HangFire_Job_StateName] ON [$(HangFireSchema)].Job;\n\tPRINT 'Dropped index [IX_HangFire_Job_StateName]';\n\n\t-- Dropping foreign key constraints based on the JobId column, because we need to modify the underlying\n\t-- column type of the clustered index to BIGINT. We'll recreate them later in the migration.\n\n\tALTER TABLE [$(HangFireSchema)].[JobParameter] DROP CONSTRAINT [FK_HangFire_JobParameter_Job];\n\tPRINT 'Dropped constraint [FK_HangFire_JobParameter_Job]';\n\n\tALTER TABLE [$(HangFireSchema)].[State] DROP CONSTRAINT [FK_HangFire_State_Job];\n\tPRINT 'Dropped constraint [FK_HangFire_State_Job]';\n\n\t-- We are going to create composite clustered indexes that are more natural for the following tables,\n\t-- so the following indexes will be unnecessary. Natural sorting will keep related data close to each\n\t-- other, and simplify the index modifications by the cost of fragmentation and additional page splits.\n\n\tDROP INDEX [UX_HangFire_CounterAggregated_Key] ON [$(HangFireSchema)].[AggregatedCounter];\n\tPRINT 'Dropped index [UX_HangFire_CounterAggregated_Key]';\n\n\tDROP INDEX [IX_HangFire_Counter_Key] ON [$(HangFireSchema)].[Counter];\n\tPRINT 'Dropped index [IX_HangFire_Counter_Key]';\n\n\tDROP INDEX [IX_HangFire_JobParameter_JobIdAndName] ON [$(HangFireSchema)].[JobParameter];\n\tPRINT 'Dropped index [IX_HangFire_JobParameter_JobIdAndName]';\n\n\tDROP INDEX [IX_HangFire_JobQueue_QueueAndFetchedAt] ON [$(HangFireSchema)].[JobQueue];\n\tPRINT 'Dropped index [IX_HangFire_JobQueue_QueueAndFetchedAt]';\n\n\tDROP INDEX [UX_HangFire_Hash_Key_Field] ON [$(HangFireSchema)].[Hash];\n\tPRINT 'Dropped index [UX_HangFire_Hash_Key_Field]';\n\n\tDROP INDEX [IX_HangFire_List_Key] ON [$(HangFireSchema)].[List];\n\tPRINT 'Dropped index [IX_HangFire_List_Key]';\n\n\tDROP INDEX [IX_HangFire_State_JobId] ON [$(HangFireSchema)].[State];\n\tPRINT 'Dropped index [IX_HangFire_State_JobId]';\n\n\t-- Then, we need to drop the primary key constraints, to modify id columns to the BIGINT type. Some of them\n\t-- will be re-created later in the migration. But some of them would be removed forever, because their\n\t-- uniqueness property sometimes unnecessary.\n\n\tALTER TABLE [$(HangFireSchema)].[AggregatedCounter] DROP CONSTRAINT [PK_HangFire_CounterAggregated];\n\tPRINT 'Dropped constraint [PK_HangFire_CounterAggregated]';\n\n\tALTER TABLE [$(HangFireSchema)].[Counter] DROP CONSTRAINT [PK_HangFire_Counter];\n\tPRINT 'Dropped constraint [PK_HangFire_Counter]';\n\n\tALTER TABLE [$(HangFireSchema)].[Hash] DROP CONSTRAINT [PK_HangFire_Hash];\n\tPRINT 'Dropped constraint [PK_HangFire_Hash]';\n\n\tALTER TABLE [$(HangFireSchema)].[Job] DROP CONSTRAINT [PK_HangFire_Job];\n\tPRINT 'Dropped constraint [PK_HangFire_Job]';\n\n\tALTER TABLE [$(HangFireSchema)].[JobParameter] DROP CONSTRAINT [PK_HangFire_JobParameter];\n\tPRINT 'Dropped constraint [PK_HangFire_JobParameter]';\n\n\tALTER TABLE [$(HangFireSchema)].[JobQueue] DROP CONSTRAINT [PK_HangFire_JobQueue];\n\tPRINT 'Dropped constraint [PK_HangFire_JobQueue]';\n\n\tALTER TABLE [$(HangFireSchema)].[List] DROP CONSTRAINT [PK_HangFire_List];\n\tPRINT 'Dropped constraint [PK_HangFire_List]';\n\n\tALTER TABLE [$(HangFireSchema)].[Set] DROP CONSTRAINT [PK_HangFire_Set];\n\tPRINT 'Dropped constraint [PK_HangFire_Set]';\n\n\tALTER TABLE [$(HangFireSchema)].[State] DROP CONSTRAINT [PK_HangFire_State];\n\tPRINT 'Dropped constraint [PK_HangFire_State]';\n\n\t-- We are removing identity columns of the following tables completely, their clustered\n\t-- index will be based on natural values. So, instead of modifying them to BIGINT, we\n\t-- are dropping them.\n\n\tALTER TABLE [$(HangFireSchema)].[AggregatedCounter] DROP COLUMN [Id];\n\tPRINT 'Dropped [AggregatedCounter].[Id] column, we will cluster on [Key] column with uniqufier';\n\n\tALTER TABLE [$(HangFireSchema)].[Counter] DROP COLUMN [Id];\n\tPRINT 'Dropped [Counter].[Id] column, we will cluster on [Key] column';\n\n\tALTER TABLE [$(HangFireSchema)].[Hash] DROP COLUMN [Id];\n\tPRINT 'Dropped [Hash].[Id] column, we will cluster on [Key]/[Field] columns';\n\n\tALTER TABLE [$(HangFireSchema)].[Set] DROP COLUMN [Id];\n\tPRINT 'Dropped [Set].[Id] column, we will cluster on [Key]/[Value] columns';\n\n\tALTER TABLE [$(HangFireSchema)].[JobParameter] DROP COLUMN [Id];\n\tPRINT 'Dropped [JobParameter].[Id] column, we will cluster on [JobId]/[Name] columns';\n\n\t-- Then we need to modify all the remaining Id columns to be of type BIGINT.\n\n\tALTER TABLE [$(HangFireSchema)].[List] ALTER COLUMN [Id] BIGINT NOT NULL;\n\tPRINT 'Modified [List].[Id] type to BIGINT';\n\n\tALTER TABLE [$(HangFireSchema)].[Job] ALTER COLUMN [Id] BIGINT NOT NULL;\n\tPRINT 'Modified [Job].[Id] type to BIGINT';\n\n\tALTER TABLE [$(HangFireSchema)].[Job] ALTER COLUMN [StateId] BIGINT NULL;\n\tPRINT 'Modified [Job].[StateId] type to BIGINT';\n\n\tALTER TABLE [$(HangFireSchema)].[JobParameter] ALTER COLUMN [JobId] BIGINT NOT NULL;\n\tPRINT 'Modified [JobParameter].[JobId] type to BIGINT';\n\n\tALTER TABLE [$(HangFireSchema)].[JobQueue] ALTER COLUMN [JobId] BIGINT NOT NULL;\n\tPRINT 'Modified [JobQueue].[JobId] type to BIGINT';\n\n\tALTER TABLE [$(HangFireSchema)].[JobQueue] ALTER COLUMN [Id] BIGINT NOT NULL;\n\tPRINT 'Modified [JobQueue].[Id] type to BIGINT';\n\n\tALTER TABLE [$(HangFireSchema)].[State] ALTER COLUMN [Id] BIGINT NOT NULL;\n\tPRINT 'Modified [State].[Id] type to BIGINT';\n\n\tALTER TABLE [$(HangFireSchema)].[State] ALTER COLUMN [JobId] BIGINT NOT NULL;\n\tPRINT 'Modified [State].[JobId] type to BIGINT';\n\n\tALTER TABLE [$(HangFireSchema)].[Counter] ALTER COLUMN [Value] INT NOT NULL;\n\tPRINT 'Modified [Counter].[Value] type to INT';\n\n\t-- Adding back all the Primary Key constraints or clustered indexes where PKs aren't appropriate.\n\n\tALTER TABLE [$(HangFireSchema)].[AggregatedCounter] ADD CONSTRAINT [PK_HangFire_CounterAggregated] PRIMARY KEY CLUSTERED (\n\t\t[Key] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_CounterAggregated]';\n\n\tCREATE CLUSTERED INDEX [CX_HangFire_Counter] ON [$(HangFireSchema)].[Counter] ([Key]);\n\tPRINT 'Created clustered index [CX_HangFire_Counter]';\n\n\tALTER TABLE [$(HangFireSchema)].[Hash] ADD CONSTRAINT [PK_HangFire_Hash] PRIMARY KEY CLUSTERED (\n\t\t[Key] ASC,\n\t\t[Field] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_Hash]';\n\n\tALTER TABLE [$(HangFireSchema)].[Job] ADD CONSTRAINT [PK_HangFire_Job] PRIMARY KEY CLUSTERED ([Id] ASC);\n\tPRINT 'Re-created constraint [PK_HangFire_Job]';\n\t\n\tALTER TABLE [$(HangFireSchema)].[JobParameter] ADD CONSTRAINT [PK_HangFire_JobParameter] PRIMARY KEY CLUSTERED (\n\t\t[JobId] ASC,\n\t\t[Name] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_JobParameter]';\n\n\tALTER TABLE [$(HangFireSchema)].[JobQueue] ADD CONSTRAINT [PK_HangFire_JobQueue] PRIMARY KEY CLUSTERED (\n\t\t[Queue] ASC,\n\t\t[Id] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_JobQueue]';\n\n\tALTER TABLE [$(HangFireSchema)].[List] ADD CONSTRAINT [PK_HangFire_List] PRIMARY KEY CLUSTERED (\n\t\t[Key] ASC,\n\t\t[Id] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_List]';\n\n\tALTER TABLE [$(HangFireSchema)].[Set] ADD CONSTRAINT [PK_HangFire_Set] PRIMARY KEY CLUSTERED (\n\t\t[Key] ASC,\n\t\t[Value] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_Set]';\n\n\tALTER TABLE [$(HangFireSchema)].[State] ADD CONSTRAINT [PK_HangFire_State] PRIMARY KEY CLUSTERED (\n\t\t[JobId] ASC,\n\t\t[Id]\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_State]';\n\n\t-- Creating secondary, nonclustered indexes\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Job_StateName] ON [$(HangFireSchema)].[Job] ([StateName])\n\tWHERE [StateName] IS NOT NULL;\n\tPRINT 'Re-created index [IX_HangFire_Job_StateName]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Set_Score] ON [$(HangFireSchema)].[Set] ([Score])\n\tWHERE [Score] IS NOT NULL;\n\tPRINT 'Created index [IX_HangFire_Set_Score]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Server_LastHeartbeat] ON [$(HangFireSchema)].[Server] ([LastHeartbeat]);\n\tPRINT 'Created index [IX_HangFire_Server_LastHeartbeat]';\n\n\t-- Creating filtered indexes for ExpireAt columns\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_AggregatedCounter_ExpireAt] ON [$(HangFireSchema)].[AggregatedCounter] ([ExpireAt])\n\tWHERE [ExpireAt] IS NOT NULL;\n\tPRINT 'Created index [IX_HangFire_AggregatedCounter_ExpireAt]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Hash_ExpireAt] ON [$(HangFireSchema)].[Hash] ([ExpireAt])\n\tWHERE [ExpireAt] IS NOT NULL;\n\tPRINT 'Re-created index [IX_HangFire_Hash_ExpireAt]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Job_ExpireAt] ON [$(HangFireSchema)].[Job] ([ExpireAt])\n\tINCLUDE ([StateName])\n\tWHERE [ExpireAt] IS NOT NULL;\n\tPRINT 'Re-created index [IX_HangFire_Job_ExpireAt]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_List_ExpireAt] ON [$(HangFireSchema)].[List] ([ExpireAt])\n\tWHERE [ExpireAt] IS NOT NULL;\n\tPRINT 'Re-created index [IX_HangFire_List_ExpireAt]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Set_ExpireAt] ON [$(HangFireSchema)].[Set] ([ExpireAt])\n\tWHERE [ExpireAt] IS NOT NULL;\n\tPRINT 'Re-created index [IX_HangFire_Set_ExpireAt]';\n\n\t-- Restoring foreign keys\n\n\tALTER TABLE [$(HangFireSchema)].[State] ADD CONSTRAINT [FK_HangFire_State_Job] FOREIGN KEY([JobId])\n\t\tREFERENCES [$(HangFireSchema)].[Job] ([Id])\n\t\tON UPDATE CASCADE\n\t\tON DELETE CASCADE;\n\tPRINT 'Re-created constraint [FK_HangFire_State_Job]';\n\n\tALTER TABLE [$(HangFireSchema)].[JobParameter] ADD CONSTRAINT [FK_HangFire_JobParameter_Job] FOREIGN KEY([JobId])\n\t\tREFERENCES [$(HangFireSchema)].[Job] ([Id])\n\t\tON UPDATE CASCADE\n\t\tON DELETE CASCADE;\n\tPRINT 'Re-created constraint [FK_HangFire_JobParameter_Job]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 6;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 6\nBEGIN\n\tPRINT 'Installing schema version 7';\n\n\tDROP INDEX [IX_HangFire_Set_Score] ON [$(HangFireSchema)].[Set];\n\tPRINT 'Dropped index [IX_HangFire_Set_Score]';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_Set_Score] ON [$(HangFireSchema)].[Set] ([Key], [Score]);\n\tPRINT 'Created index [IX_HangFire_Set_Score] with the proper composite key';\n\n\tSET @CURRENT_SCHEMA_VERSION = 7;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 7 AND @DISABLE_HEAVY_MIGRATIONS = 1\nBEGIN\n\tPRINT 'Migration process STOPPED at schema version ' + CAST(@CURRENT_SCHEMA_VERSION AS NVARCHAR) +\n\t\t'. WILL NOT upgrade to schema version ' + CAST(@TARGET_SCHEMA_VERSION AS NVARCHAR) +\n\t\t', because @DISABLE_HEAVY_MIGRATIONS option is set.';\nEND\nELSE IF @CURRENT_SCHEMA_VERSION = 7\nBEGIN\n\tPRINT 'Installing schema version 8';\n\n\tALTER TABLE [$(HangFireSchema)].[Server] DROP CONSTRAINT [PK_HangFire_Server]\n\tPRINT 'Dropped constraint [PK_HangFire_Server] to modify the [HangFire].[Server].[Id] column';\n\n\tALTER TABLE [$(HangFireSchema)].[Server] ALTER COLUMN [Id] NVARCHAR (200) NOT NULL;\n\tPRINT 'Modified [$(HangFireSchema)].[Server].[Id] length to 200';\n\n\tALTER TABLE [$(HangFireSchema)].[Server] ADD  CONSTRAINT [PK_HangFire_Server] PRIMARY KEY CLUSTERED ([Id] ASC);\n\tPRINT 'Re-created constraint [PK_HangFire_Server]';\n\n\t-- Nothing complicated - we just collecting all the secondary indexes and primary key names to delete them.\n\t-- We should expect nothing here, because custom columns and indexes can be applied for the [Counter] table\n\t-- to make replication work on Microsoft Azure, like in the issue below.\n\t-- https://github.com/HangfireIO/Hangfire/issues/1500\n\tDECLARE @dropIndexSql2 NVARCHAR(MAX) = N'';\n\tSELECT @dropIndexSql2 += N'DROP INDEX ' + QUOTENAME(SCHEMA_NAME(o.[schema_id])) + '.' + QUOTENAME(o.name) + '.' + QUOTENAME(i.name) + ';'\n\tFROM sys.indexes AS i\n\tINNER JOIN sys.tables AS o\n\tON i.[object_id] = o.[object_id]\n\tWHERE i.is_primary_key = 0\n\tAND i.index_id <> 0\n\tAND o.is_ms_shipped = 0\n\tAND SCHEMA_NAME(o.[schema_id]) = '$(HangFireSchema)'\n\tAND o.name = 'Counter';\n\n\tSELECT @dropIndexSql2 += N'ALTER TABLE' + QUOTENAME(SCHEMA_NAME(o.[schema_id])) + '.' + QUOTENAME(o.name) + ' DROP CONSTRAINT ' + QUOTENAME(c.name) + ';'\n\tFROM sys.key_constraints c\n\tINNER JOIN sys.tables AS o\n\tON c.[parent_object_id] = o.[object_id]\n\tWHERE o.is_ms_shipped = 0\n\tAND SCHEMA_NAME(o.[schema_id]) = '$(HangFireSchema)'\n\tAND o.name = 'Counter'\n\n\tEXEC sp_executesql @dropIndexSql2;\n\tPRINT 'Dropped all indexes on the [$(HangFireSchema)].[Counter] table';\n\n\t-- [Counter].[Id] column can already be added to make replication work as written above, so we will re-create it\n\t-- to ensure it is in the expected format.\n\tPRINT 'Checking for existence of the [$(HangFireSchema)].[Counter].[Id] column';\n\tIF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Counter' AND COLUMN_NAME = 'Id' AND TABLE_SCHEMA='$(HangFireSchema)')\n\tBEGIN\n\t\tALTER TABLE [$(HangFireSchema)].[Counter] DROP COLUMN [Id];\n\t\tPRINT 'Dropped [$(HangFireSchema)].[Counter].[Id] column';\n\tEND\n\n\tALTER TABLE [$(HangFireSchema)].[Counter] ADD [Id] BIGINT IDENTITY(1, 1);\n\tPRINT 'Created [$(HangFireSchema)].[Counter].[Id] column';\n\n\tALTER TABLE [$(HangFireSchema)].[Counter] ADD  CONSTRAINT [PK_HangFire_Counter] PRIMARY KEY CLUSTERED (\n\t\t[Key] ASC,\n\t\t[Id] ASC\n\t);\n\tPRINT 'Created clustered primary key PK_HangFire_Counter ([Key], [Id])';\n\n\t-- SqlServerStorageOptions.UseIgnoreDupKeyOption will yield much better results with INSERT/UPDATE operators\n\t-- instead of MERGE for [Set] and [Hash] tables. This change is also compatible with older clients, since\n\t-- MERGE operator is used there, so those clients are forward-compatible with these changes.\n\tALTER TABLE [$(HangFireSchema)].[Set] REBUILD WITH (IGNORE_DUP_KEY = ON);\n\tPRINT 'Enabled IGNORE_DUP_KEY option for [$(HangFireSchema)].[Set] table';\n\n\tALTER TABLE [$(HangFireSchema)].[Hash] REBUILD WITH (IGNORE_DUP_KEY = ON);\n\tPRINT 'Enabled IGNORE_DUP_KEY option for [$(HangFireSchema)].[Hash] table';\n\n\tALTER TABLE [$(HangFireSchema)].[JobQueue] DROP CONSTRAINT [PK_HangFire_JobQueue];\n\tPRINT 'Dropped constraint [PK_HangFire_JobQueue] to modify the [$(HangFireSchema)].[JobQueue].[Id] column';\n\n\tALTER TABLE [$(HangFireSchema)].[JobQueue] ALTER COLUMN [Id] BIGINT NOT NULL;\n\tPRINT 'Changed [$(HangFireSchema)].[JobQueue].[Id] column type to BIGINT';\n\n\tALTER TABLE [$(HangFireSchema)].[JobQueue] ADD CONSTRAINT [PK_HangFire_JobQueue] PRIMARY KEY CLUSTERED (\n\t\t[Queue] ASC,\n\t\t[Id] ASC\n\t);\n\tPRINT 'Re-created constraint [PK_HangFire_JobQueue]';\n\n\tSET @CURRENT_SCHEMA_VERSION = 8;\nEND\n\nIF @CURRENT_SCHEMA_VERSION = 8\nBEGIN\n\tPRINT 'Installing schema version 9';\n\n\tCREATE NONCLUSTERED INDEX [IX_HangFire_State_CreatedAt] ON [$(HangFireSchema)].[State] ([CreatedAt] ASC)\n\n\tSET @CURRENT_SCHEMA_VERSION = 9;\nEND\n\n/*IF @CURRENT_SCHEMA_VERSION = 9\nBEGIN\n\tPRINT 'Installing schema version 10';\n\n\t Insert migration here\n\n\tSET @CURRENT_SCHEMA_VERSION = 10;\nEND*/\n\nUPDATE [$(HangFireSchema)].[Schema] SET [Version] = @CURRENT_SCHEMA_VERSION\nIF @@ROWCOUNT = 0 \n\tINSERT INTO [$(HangFireSchema)].[Schema] ([Version]) VALUES (@CURRENT_SCHEMA_VERSION)        \n\nPRINT 'Hangfire database schema installed';\n\nCOMMIT TRANSACTION;\nPRINT 'Hangfire SQL objects installed';\n"
  },
  {
    "path": "src/Hangfire.SqlServer/PersistentJobQueueProviderCollection.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace Hangfire.SqlServer\n{\n    public class PersistentJobQueueProviderCollection : IEnumerable<IPersistentJobQueueProvider>\n    {\n        private readonly List<IPersistentJobQueueProvider> _providers\n            = new List<IPersistentJobQueueProvider>(); \n        private readonly Dictionary<string, IPersistentJobQueueProvider> _providersByQueue\n            = new Dictionary<string, IPersistentJobQueueProvider>(StringComparer.OrdinalIgnoreCase);\n\n        private readonly IPersistentJobQueueProvider _defaultProvider;\n\n        public PersistentJobQueueProviderCollection(IPersistentJobQueueProvider defaultProvider)\n        {\n            if (defaultProvider == null) throw new ArgumentNullException(nameof(defaultProvider));\n\n            _defaultProvider = defaultProvider;\n\n            _providers.Add(_defaultProvider);\n        }\n\n        public void Add(IPersistentJobQueueProvider provider, IEnumerable<string> queues)\n        {\n            if (provider == null) throw new ArgumentNullException(nameof(provider));\n            if (queues == null) throw new ArgumentNullException(nameof(queues));\n\n            _providers.Add(provider);\n\n            foreach (var queue in queues)\n            {\n                _providersByQueue.Add(queue, provider);\n            }\n        }\n\n        public IPersistentJobQueueProvider GetProvider(string queue)\n        {\n            return _providersByQueue.TryGetValue(queue, out var provider) \n                ? provider\n                : _defaultProvider;\n        }\n\n        public IEnumerator<IPersistentJobQueueProvider> GetEnumerator()\n        {\n            return _providers.GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/Properties/AssemblyInfo.cs",
    "content": "﻿using System;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n[assembly: AssemblyTitle(\"Hangfire.SqlServer\")]\n[assembly: AssemblyDescription(\"SQL Server job storage for Hangfire\")]\n[assembly: Guid(\"3d96bf2f-8854-4872-aee3-faf81d121a4d\")]\n[assembly: CLSCompliant(true)]\n\n[assembly: InternalsVisibleTo(\"Hangfire.SqlServer.Tests\")]\n// Allow the generation of mocks for internal types\n[assembly: InternalsVisibleTo(\"DynamicProxyGenAssembly2\")]\n"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlCommandBatch.cs",
    "content": "// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data.Common;\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class SqlCommandBatch : IDisposable\n    {\n        private readonly List<DbCommand> _commandList = new List<DbCommand>();\n        private readonly SqlCommandSet _commandSet;\n        private readonly int _defaultTimeout;\n\n        public SqlCommandBatch(DbConnection connection, DbTransaction transaction, bool preferBatching)\n        {\n            Connection = connection;\n            Transaction = transaction;\n\n            if (preferBatching)\n            {\n                try\n                {\n                    _commandSet = new SqlCommandSet(connection);\n                    _defaultTimeout = _commandSet.BatchCommand.CommandTimeout;\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    _commandSet = null;\n                }\n            }\n        }\n\n        public DbConnection Connection { get; }\n        public DbTransaction Transaction { get; }\n\n        public int? CommandTimeout { get; set; }\n        public int? CommandBatchMaxTimeout { get; set; }\n\n        public void Dispose()\n        {\n            foreach (var command in _commandList)\n            {\n                command.Dispose();\n            }\n\n            _commandSet?.Dispose();\n        }\n\n        public void Append(DbCommand command)\n        {\n            if (_commandSet != null)\n            {\n                _commandSet.Append(command);\n            }\n            else\n            {\n                _commandList.Add(command);\n            }\n        }\n\n        public void ExecuteNonQuery()\n        {\n            if (_commandSet != null && _commandSet.CommandCount > 0)\n            {\n                _commandSet.Connection = Connection;\n                _commandSet.Transaction = Transaction;\n\n                var batchTimeout = CommandTimeout ?? _defaultTimeout;\n\n                if (batchTimeout > 0)\n                {\n                    batchTimeout = batchTimeout * _commandSet.CommandCount;\n\n                    if (CommandBatchMaxTimeout.HasValue)\n                    {\n                        batchTimeout = Math.Min(CommandBatchMaxTimeout.Value, batchTimeout);\n                    }\n                }\n\n                _commandSet.BatchCommand.CommandTimeout = batchTimeout;\n                _commandSet.ExecuteNonQuery();\n            }\n\n            foreach (var command in _commandList)\n            {\n                command.Connection = Connection;\n                command.Transaction = Transaction;\n\n                if (CommandTimeout.HasValue)\n                {\n                    command.CommandTimeout = CommandTimeout.Value;\n                }\n\n                command.ExecuteNonQuery();\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlCommandSet.cs",
    "content": "// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Data.Common;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing System.Reflection;\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class SqlCommandSet : IDisposable\n    {\n        private static readonly ConcurrentDictionary<Assembly, Type> SqlCommandSetType = new ConcurrentDictionary<Assembly, Type>();\n        private static readonly ConcurrentDictionary<Type, Action<object, DbConnection>> SetConnection = new ConcurrentDictionary<Type, Action<object, DbConnection>>();\n        private static readonly ConcurrentDictionary<Type, Action<object, DbTransaction>> SetTransaction = new ConcurrentDictionary<Type, Action<object, DbTransaction>>();\n        private static readonly ConcurrentDictionary<Type, Func<object, DbCommand>> GetBatchCommand = new ConcurrentDictionary<Type, Func<object, DbCommand>>();\n        private static readonly ConcurrentDictionary<Type, PropertyInfo> BatchCommandProperty = new ConcurrentDictionary<Type, PropertyInfo>();\n        private static readonly ConcurrentDictionary<Type, Action<object, DbCommand>> AppendMethod = new ConcurrentDictionary<Type, Action<object, DbCommand>>();\n        private static readonly ConcurrentDictionary<Type, Func<object, int>> ExecuteNonQueryMethod = new ConcurrentDictionary<Type, Func<object, int>>();\n        private static readonly ConcurrentDictionary<Type, Action<object>> DisposeMethod = new ConcurrentDictionary<Type, Action<object>>();\n        private static readonly ConcurrentDictionary<Type, Func<object>> SqlCommandSetConstructor = new  ConcurrentDictionary<Type, Func<object>>();\n\n        private readonly object _instance;\n\n        private readonly Action<object, DbConnection> _setConnection;\n        private readonly Action<object, DbTransaction> _setTransaction;\n        private readonly Func<object, DbCommand> _getBatchCommand;\n        private readonly Action<object, DbCommand> _appendMethod;\n        private readonly Func<object, int> _executeNonQueryMethod;\n        private readonly Action<object> _disposeMethod;\n        private readonly Func<object> _constructor;\n\n        public SqlCommandSet(DbConnection connection)\n        {\n            Type sqlCommandSetType;\n            try\n            {\n                sqlCommandSetType = SqlCommandSetType.GetOrAdd(connection.GetType().GetTypeInfo().Assembly, static sqlClientAssembly =>\n                {\n                    var assemblyName = sqlClientAssembly.GetName();\n                    var version = assemblyName.Version;\n\n                    if (assemblyName.Name == \"System.Data.SqlClient\" && Version.Parse(\"4.0.0.0\") < version && version < Version.Parse(\"4.6.0.0\"))\n                    {\n                        // .NET Core version of the System.Data.SqlClient package below 4.7.0 (which\n                        // has assembly version 4.6.0.0) doesn't properly implement the SqlCommandSet\n                        // class, throwing the following exception in run-time:\n                        // ArgumentException: Specified parameter name 'Parameter1' is not valid.\n                        // GitHub Issue: https://github.com/dotnet/corefx/issues/29391\n                        throw new NotSupportedException(\".NET Core version of the System.Data.SqlClient package below 4.7.0 (which has assembly version 4.6.0.0) doesn't properly implement the SqlCommandSet class.\");\n                    }\n\n                    var type = sqlClientAssembly.GetTypes().FirstOrDefault(static x => x.Name == \"SqlCommandSet\");\n                    if (type == null)\n                    {\n                        throw new TypeLoadException($\"Could not load type 'SqlCommandSet' from assembly '{sqlClientAssembly}'.\");\n                    }\n\n                    return type;\n                });\n\n                _setConnection = SetConnection.GetOrAdd(sqlCommandSetType, static type =>\n                {\n                    var p = Expression.Parameter(typeof(object));\n                    var converted = Expression.Convert(p, type);\n                    var connectionParameter = Expression.Parameter(typeof(DbConnection));\n                    var connectionProperty = type.GetProperty(\"Connection\", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException($\"Property '{type.FullName}.Connection' not found.\");\n                    return Expression.Lambda<Action<object, DbConnection>>(Expression.Assign(Expression.Property(converted, connectionProperty), Expression.Convert(connectionParameter, connectionProperty.PropertyType)), p, connectionParameter).Compile();\n                });\n                _setTransaction = SetTransaction.GetOrAdd(sqlCommandSetType, static type =>\n                {\n                    var p = Expression.Parameter(typeof(object));\n                    var converted = Expression.Convert(p, type);\n                    var transactionParameter = Expression.Parameter(typeof(DbTransaction));\n                    var transactionProperty = type.GetProperty(\"Transaction\", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException($\"Property '{type.FullName}.Transaction' not found.\");\n                    return Expression.Lambda<Action<object, DbTransaction>>(Expression.Assign(Expression.Property(converted, transactionProperty), Expression.Convert(transactionParameter, transactionProperty.PropertyType)), p, transactionParameter).Compile();\n                });\n                var batchCommandProperty = BatchCommandProperty.GetOrAdd(sqlCommandSetType, static type =>\n                {\n                    return type.GetProperty(\"BatchCommand\", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException($\"Property '{type.FullName}.BatchCommand' not found.\");\n                });\n                _getBatchCommand = GetBatchCommand.GetOrAdd(sqlCommandSetType, type =>\n                {\n                    var p = Expression.Parameter(typeof(object));\n                    var converted = Expression.Convert(p, type);\n                    return Expression.Lambda<Func<object, DbCommand>>(Expression.Property(converted, batchCommandProperty), p).Compile();\n                });\n                _appendMethod = AppendMethod.GetOrAdd(sqlCommandSetType, type =>\n                {\n                    var p = Expression.Parameter(typeof(object));\n                    var converted = Expression.Convert(p, type);\n                    var batchCommandParameter = Expression.Parameter(typeof(DbCommand));\n                    return Expression.Lambda<Action<object, DbCommand>>(Expression.Call(converted, \"Append\", null, Expression.Convert(batchCommandParameter, batchCommandProperty.PropertyType)), p, batchCommandParameter).Compile();\n                });\n                _executeNonQueryMethod = ExecuteNonQueryMethod.GetOrAdd(sqlCommandSetType, static type =>\n                {\n                    var p = Expression.Parameter(typeof(object));\n                    var converted = Expression.Convert(p, type);\n                    return Expression.Lambda<Func<object, int>>(Expression.Call(converted, \"ExecuteNonQuery\", null), p).Compile();\n                });\n                _disposeMethod = DisposeMethod.GetOrAdd(sqlCommandSetType, static type =>\n                {\n                    var p = Expression.Parameter(typeof(object));\n                    var converted = Expression.Convert(p, type);\n                    return Expression.Lambda<Action<object>>(Expression.Call(converted, \"Dispose\", null), p).Compile();\n                });\n                _constructor = SqlCommandSetConstructor.GetOrAdd(sqlCommandSetType, static type =>\n                {\n                    var ctor = Expression.New(type);\n                    return Expression.Lambda<Func<object>>(ctor).Compile();\n                });\n            }\n            catch (Exception exception) when (exception.IsCatchableExceptionType())\n            {\n                throw new NotSupportedException($\"SqlCommandSet for {connection.GetType().FullName} is not supported, use regular commands instead\", exception);\n            }\n\n            _instance = _constructor();\n        }\n\n        public DbConnection Connection\n        {\n            set => _setConnection(_instance, value);\n        }\n\n        public DbTransaction Transaction\n        {\n            set => _setTransaction(_instance, value);\n        }\n\n        public DbCommand BatchCommand => _getBatchCommand(_instance);\n        public int CommandCount { get; private set; }\n\n        public void Append(DbCommand command)\n        {\n            _appendMethod(_instance, command);\n            CommandCount++;\n        }\n\n        public int ExecuteNonQuery()\n        {\n            if (CommandCount == 0)\n            {\n                return 0;\n            }\n\n            return _executeNonQueryMethod(_instance);\n        }\n\n        public void Dispose()\n        {\n            _disposeMethod(_instance);\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerBootstrapperConfigurationExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.SqlServer\n{\n    public static class SqlServerBootstrapperConfigurationExtensions\n    {\n        /// <summary>\n        /// Tells the bootstrapper to use SQL Server as a job storage,\n        /// that can be accessed using the given connection string or \n        /// its name.\n        /// </summary>\n        /// <param name=\"configuration\">Configuration</param>\n        /// <param name=\"nameOrConnectionString\">Connection string or its name</param>\n        [Obsolete(\"Please use `GlobalConfiguration.UseSqlServerStorage` instead. Will be removed in version 2.0.0.\")]\n        public static SqlServerStorage UseSqlServerStorage(\n            this IBootstrapperConfiguration configuration,\n            string nameOrConnectionString)\n        {\n            var storage = new SqlServerStorage(nameOrConnectionString);\n            configuration.UseStorage(storage);\n\n            return storage;\n        }\n\n        /// <summary>\n        /// Tells the bootstrapper to use SQL Server as a job storage\n        /// with the given options, that can be accessed using the specified\n        /// connection string or its name.\n        /// </summary>\n        /// <param name=\"configuration\">Configuration</param>\n        /// <param name=\"nameOrConnectionString\">Connection string or its name</param>\n        /// <param name=\"options\">Advanced options</param>\n        [Obsolete(\"Please use `GlobalConfiguration.UseSqlServerStorage` instead. Will be removed in version 2.0.0.\")]\n        public static SqlServerStorage UseSqlServerStorage(\n            this IBootstrapperConfiguration configuration,\n            string nameOrConnectionString,\n            SqlServerStorageOptions options)\n        {\n            var storage = new SqlServerStorage(nameOrConnectionString, options);\n            configuration.UseStorage(storage);\n\n            return storage;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerConnection.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Globalization;\nusing System.Linq;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Hangfire.SqlServer.Entities;\nusing Hangfire.Storage;\n\n// ReSharper disable RedundantAnonymousTypePropertyName\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class SqlServerConnection : JobStorageConnection\n    {\n        private readonly SqlServerStorage _storage;\n        private readonly Dictionary<string, HashSet<Guid>> _lockedResources = new Dictionary<string, HashSet<Guid>>();\n\n        public SqlServerConnection([NotNull] SqlServerStorage storage)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            _storage = storage;\n        }\n\n        public SqlServerStorage Storage => _storage;\n        public DbConnection DedicatedConnection => _dedicatedConnection;\n\n        public override void Dispose()\n        {\n            if (_dedicatedConnection != null)\n            {\n                _dedicatedConnection.Dispose();\n                _dedicatedConnection = null;\n            }\n\n            base.Dispose();\n        }\n\n        public override IWriteOnlyTransaction CreateWriteTransaction()\n        {\n            return new SqlServerWriteOnlyTransaction(this);\n        }\n\n        public override IDisposable AcquireDistributedLock([NotNull] string resource, TimeSpan timeout)\n        {\n            if (String.IsNullOrWhiteSpace(resource)) throw new ArgumentNullException(nameof(resource));\n            return AcquireLock($\"{_storage.SchemaName}:{resource}\", timeout);\n        }\n\n        public override IFetchedJob FetchNextJob(string[] queues, CancellationToken cancellationToken)\n        {\n            if (queues == null || queues.Length == 0) throw new ArgumentNullException(nameof(queues));\n\n            var providers = queues\n                .Select(queue => _storage.QueueProviders.GetProvider(queue))\n                .Distinct()\n                .ToArray();\n\n            if (providers.Length != 1)\n            {\n                throw new InvalidOperationException(\n                    $\"Multiple provider instances registered for queues: {String.Join(\", \", queues)}. You should choose only one type of persistent queues per server instance.\");\n            }\n            \n            var persistentQueue = providers[0].GetJobQueue();\n            return persistentQueue.Dequeue(queues, cancellationToken);\n        }\n\n        public override string CreateExpiredJob(\n            Job job,\n            IDictionary<string, string> parameters, \n            DateTime createdAt,\n            TimeSpan expireIn)\n        {\n            if (job == null) throw new ArgumentNullException(nameof(job));\n            if (parameters == null) throw new ArgumentNullException(nameof(parameters));\n\n            var queryString = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].Job (InvocationData, Arguments, CreatedAt, ExpireAt)\noutput inserted.Id\nvalues (@invocationData, @arguments, @createdAt, @expireAt)\");\n\n            var invocationData = InvocationData.SerializeJob(job);\n            var payload = invocationData.SerializePayload(excludeArguments: true);\n\n            Action<DbCommand> queryParameters = cmd => cmd\n                .AddParameter(\"@invocationData\", payload, DbType.String, size: -1)\n                .AddParameter(\"@arguments\", invocationData.Arguments, DbType.String, size: -1)\n                .AddParameter(\"@createdAt\", createdAt, DbType.DateTime)\n                .AddParameter(\"@expireAt\", createdAt.Add(expireIn), DbType.DateTime);\n\n            Action<DbCommand> additionalParameters = null;\n\n            var parametersArray = parameters.ToArray();\n\n            if (parametersArray.Length <= 4)\n            {\n                if (parametersArray.Length == 1)\n                {\n                    queryString = _storage.GetQueryFromTemplate(static schemaName => $@\"\nset xact_abort on; set nocount on; declare @jobId bigint;\nbegin tran;\ninsert into [{schemaName}].Job (InvocationData, Arguments, CreatedAt, ExpireAt) values (@invocationData, @arguments, @createdAt, @expireAt);\nselect @jobId = scope_identity(); select @jobId;\ninsert into [{schemaName}].JobParameter (JobId, Name, Value) values (@jobId, @name, @value);\ncommit tran;\");\n                    additionalParameters = cmd => cmd\n                        .AddParameter(\"@name\", parametersArray[0].Key, DbType.String, size: 40)\n                        .AddParameter(\"@value\", parametersArray[0].Value, DbType.String, size: -1);\n                }\n                else if (parametersArray.Length == 2)\n                {\n                    queryString = _storage.GetQueryFromTemplate(static schemaName => $@\"\nset xact_abort on; set nocount on; declare @jobId bigint;\nbegin tran;\ninsert into [{schemaName}].Job (InvocationData, Arguments, CreatedAt, ExpireAt) values (@invocationData, @arguments, @createdAt, @expireAt);\nselect @jobId = scope_identity(); select @jobId;\ninsert into [{schemaName}].JobParameter (JobId, Name, Value) values (@jobId, @name1, @value1), (@jobId, @name2, @value2);\ncommit tran;\");\n                    additionalParameters = cmd => cmd\n                        .AddParameter(\"@name1\", parametersArray[0].Key, DbType.String, size: 40)\n                        .AddParameter(\"@value1\", parametersArray[0].Value, DbType.String, size: -1)\n                        .AddParameter(\"@name2\", parametersArray[1].Key, DbType.String, size: 40)\n                        .AddParameter(\"@value2\", parametersArray[1].Value, DbType.String, size: -1);\n                }\n                else if (parametersArray.Length == 3)\n                {\n                    queryString = _storage.GetQueryFromTemplate(static schemaName => $@\"\nset xact_abort on; set nocount on; declare @jobId bigint;\nbegin tran;\ninsert into [{schemaName}].Job (InvocationData, Arguments, CreatedAt, ExpireAt) values (@invocationData, @arguments, @createdAt, @expireAt);\nselect @jobId = scope_identity(); select @jobId;\ninsert into [{schemaName}].JobParameter (JobId, Name, Value) values (@jobId, @name1, @value1), (@jobId, @name2, @value2), (@jobId, @name3, @value3);\ncommit tran;\");\n                    additionalParameters = cmd => cmd\n                        .AddParameter(\"@name1\", parametersArray[0].Key, DbType.String, size: 40)\n                        .AddParameter(\"@value1\", parametersArray[0].Value, DbType.String, size: -1)\n                        .AddParameter(\"@name2\", parametersArray[1].Key, DbType.String, size: 40)\n                        .AddParameter(\"@value2\", parametersArray[1].Value, DbType.String, size: -1)\n                        .AddParameter(\"@name3\", parametersArray[2].Key, DbType.String, size: 40)\n                        .AddParameter(\"@value3\", parametersArray[2].Value, DbType.String, size: -1);\n                }\n                else if (parametersArray.Length == 4)\n                {\n                    queryString = _storage.GetQueryFromTemplate(static schemaName => $@\"\nset xact_abort on; set nocount on; declare @jobId bigint;\nbegin tran;\ninsert into [{schemaName}].Job (InvocationData, Arguments, CreatedAt, ExpireAt) values (@invocationData, @arguments, @createdAt, @expireAt);\nselect @jobId = scope_identity(); select @jobId;\ninsert into [{schemaName}].JobParameter (JobId, Name, Value) values (@jobId, @name1, @value1), (@jobId, @name2, @value2), (@jobId, @name3, @value3), (@jobId, @name4, @value4);\ncommit tran;\");\n                    additionalParameters = cmd => cmd\n                        .AddParameter(\"@name1\", parametersArray[0].Key, DbType.String, size: 40)\n                        .AddParameter(\"@value1\", parametersArray[0].Value, DbType.String, size: -1)\n                        .AddParameter(\"@name2\", parametersArray[1].Key, DbType.String, size: 40)\n                        .AddParameter(\"@value2\", parametersArray[1].Value, DbType.String, size: -1)\n                        .AddParameter(\"@name3\", parametersArray[2].Key, DbType.String, size: 40)\n                        .AddParameter(\"@value3\", parametersArray[2].Value, DbType.String, size: -1)\n                        .AddParameter(\"@name4\", parametersArray[3].Key, DbType.String, size: 40)\n                        .AddParameter(\"@value4\", parametersArray[3].Value, DbType.String, size: -1);\n                }\n\n                return _storage.UseConnection(_dedicatedConnection, static (storage, connection, ctx) =>\n                    {\n                        using var command = connection.Create(ctx.Key, timeout: storage.CommandTimeout);\n                        ctx.Value.Key(command);\n                        ctx.Value.Value?.Invoke(command);\n\n                        return Convert.ToInt64(command.ExecuteScalar(), CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture);\n                    },\n                    new KeyValuePair<string, KeyValuePair<Action<DbCommand>, Action<DbCommand>>>(\n                        queryString,\n                        new KeyValuePair<Action<DbCommand>, Action<DbCommand>>(queryParameters, additionalParameters)));\n            }\n\n            return _storage.UseTransaction(_dedicatedConnection, static (storage, connection, transaction, triple) =>\n            {\n                using var jobCommand = connection.Create(triple.Item1, timeout: storage.CommandTimeout);\n                triple.Item2(jobCommand);\n\n                jobCommand.Transaction = transaction;\n\n                var jobId = Convert.ToInt64(jobCommand.ExecuteScalar(), CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture);\n\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].JobParameter (JobId, Name, Value) values (@jobId, @name, @value)\");\n\n                using (var commandBatch = new SqlCommandBatch(connection, transaction, preferBatching: storage.CommandBatchMaxTimeout.HasValue))\n                {\n                    commandBatch.CommandTimeout = storage.CommandTimeout;\n                    commandBatch.CommandBatchMaxTimeout = storage.CommandBatchMaxTimeout;\n\n                    foreach (var parameter in triple.Item3)\n                    {\n                        var command = connection.Create(query)\n                            .AddParameter(\"@jobId\", long.Parse(jobId, CultureInfo.InvariantCulture), DbType.Int64)\n                            .AddParameter(\"@name\", parameter.Key, DbType.String, size: 40)\n                            .AddParameter(\"@value\", (object)parameter.Value ?? DBNull.Value, DbType.String, size: -1);\n\n                        commandBatch.Append(command);\n                    }\n\n                    commandBatch.ExecuteNonQuery();\n                }\n\n                return jobId;\n            }, CreateTriple(queryString, queryParameters, parametersArray), null);\n        }\n\n        public override JobData GetJobData(string id)\n        {\n            if (id == null) throw new ArgumentNullException(nameof(id));\n\n            if (!long.TryParse(id, out var parsedId))\n            {\n                return null;\n            }\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, parsedId) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select InvocationData, StateName, Arguments, CreatedAt from [{schemaName}].Job with (readcommittedlock, forceseek) where Id = @id\nselect Name, Value from [{schemaName}].JobParameter with (forceseek) where JobId = @id\");\n\n                using (var multi = connection.QueryMultiple(query, new { id = parsedId }, commandTimeout: storage.CommandTimeout))\n                {\n                    var jobData = multi.ReadSingleOrDefault();\n                    if (jobData == null) return null;\n\n                    var parameters = new Dictionary<string, string>();\n\n                    var jobParameters = multi.Read<JobParameter>().ToArray();\n                    for (var i = 0; i < jobParameters.Length; i++)\n                    {\n                        var jobParameter = jobParameters[i];\n                        parameters[jobParameter.Name] = jobParameter.Value;\n                    }\n\n                    // TODO: conversion exception could be thrown.\n                    var invocationData = InvocationData.DeserializePayload(jobData.InvocationData);\n\n                    if (!String.IsNullOrEmpty(jobData.Arguments))\n                    {\n                        invocationData.Arguments = jobData.Arguments;\n                    }\n\n                    Job job = null;\n                    JobLoadException loadException = null;\n\n                    try\n                    {\n                        job = invocationData.DeserializeJob();\n                    }\n                    catch (JobLoadException ex)\n                    {\n                        loadException = ex;\n                    }\n\n                    return new JobData\n                    {\n                        Job = job,\n                        InvocationData = invocationData,\n                        State = jobData.StateName,\n                        CreatedAt = jobData.CreatedAt,\n                        LoadException = loadException,\n                        ParametersSnapshot = parameters\n                    };\n                }\n            }, parsedId);\n        }\n\n        public override StateData GetStateData(string jobId)\n        {\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n\n            if (!long.TryParse(jobId, out var parsedId))\n            {\n                return null;\n            }\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, parsedId) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select s.Name, s.Reason, s.Data\nfrom [{schemaName}].State s with (readcommittedlock, forceseek)\ninner join [{schemaName}].Job j with (readcommittedlock, forceseek) on j.StateId = s.Id and j.Id = s.JobId\nwhere j.Id = @jobId\");\n\n                var sqlState = connection.QuerySingleOrDefault<SqlState>(query, new { jobId = parsedId }, commandTimeout: storage.CommandTimeout);\n                if (sqlState == null)\n                {\n                    return null;\n                }\n\n                var data = new Dictionary<string, string>(\n                    SerializationHelper.Deserialize<Dictionary<string, string>>(sqlState.Data),\n                    StringComparer.OrdinalIgnoreCase);\n\n                return new StateData\n                {\n                    Name = sqlState.Name,\n                    Reason = sqlState.Reason,\n                    Data = data\n                };\n            }, parsedId);\n        }\n\n        public override void SetJobParameter(string id, string name, string value)\n        {\n            if (id == null) throw new ArgumentNullException(nameof(id));\n            if (name == null) throw new ArgumentNullException(nameof(name));\n\n            // First updated is required for older schema versions (5 and below), where\n            // [IX_HangFire_JobParameter_JobIdAndName] index wasn't declared as unique,\n            // to make our query resistant to (no matter what schema we are using):\n            // \n            // https://github.com/HangfireIO/Hangfire/issues/1743 (deadlocks)\n            // https://github.com/HangfireIO/Hangfire/issues/1741 (duplicate entries)\n            // https://github.com/HangfireIO/Hangfire/issues/1693#issuecomment-697976133 (records aren't updated)\n\n            _storage.UseConnection(_dedicatedConnection, static (storage, connection, triple) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName => $@\"\nset xact_abort off;\nbegin try\n  update [{schemaName}].JobParameter set Value = @value where JobId = @jobId and Name = @name;\n  if @@ROWCOUNT = 0 insert into [{schemaName}].JobParameter (JobId, Name, Value) values (@jobId, @name, @value);\nend try\nbegin catch\n  declare @em nvarchar(4000), @es int, @est int;\n  select @em=error_message(),@es=error_severity(),@est=error_state();\n  IF ERROR_NUMBER() not in (2601, 2627) raiserror(@em, @es, @est);\n  update [{schemaName}].JobParameter set Value = @value where JobId = @jobId and Name = @name;\nend catch\");\n\n                return connection.Execute(\n                    query,\n                    new { jobId = triple.Item1, name = triple.Item2, value = triple.Item3 },\n                    commandTimeout: storage.CommandTimeout);\n            }, CreateTriple(long.Parse(id, CultureInfo.InvariantCulture), name, value));\n        }\n\n        public override string GetJobParameter(string id, string name)\n        {\n            if (id == null) throw new ArgumentNullException(nameof(id));\n            if (name == null) throw new ArgumentNullException(nameof(name));\n\n            if (!long.TryParse(id, out var parsedId))\n            {\n                return null;\n            }\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, pair) => connection.ExecuteScalar<string>(\n                storage.GetQueryFromTemplate(static schemaName =>\n                    $@\"select top (1) Value from [{schemaName}].JobParameter with (forceseek) where JobId = @id and Name = @name\"),\n                new { id = pair.Key, name = pair.Value },\n                commandTimeout: storage.CommandTimeout),\n                new KeyValuePair<long, string>(parsedId, name));\n        }\n\n        public override HashSet<string> GetAllItemsFromSet(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select Value from [{schemaName}].[Set] with (forceseek) where [Key] = @key\");\n\n                var result = connection.Query<string>(\n                    query,\n                    new { key = key },\n                    commandTimeout: storage.CommandTimeout);\n\n                return new HashSet<string>(result);\n            }, key);\n        }\n\n        public override string GetFirstByLowestScoreFromSet(string key, double fromScore, double toScore)\n        {\n            return GetFirstByLowestScoreFromSet(key, fromScore, toScore, 1).FirstOrDefault();\n        }\n\n        public override List<string> GetFirstByLowestScoreFromSet(string key, double fromScore, double toScore, int count)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n            if (count <= 0) throw new ArgumentException(\"The value must be a positive number\", nameof(count));\n            if (toScore < fromScore) throw new ArgumentException(\"The `toScore` value must be higher or equal to the `fromScore` value.\", nameof(toScore));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, pair) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select top (@count) Value from [{schemaName}].[Set] with (forceseek) where [Key] = @key and Score between @from and @to order by Score\");\n\n                var result = connection.Query<string>(\n                    query,\n                    new { count = pair.Value.Item3, key = pair.Key, from = pair.Value.Item1, to = pair.Value.Item2 },\n                    commandTimeout: storage.CommandTimeout);\n\n                return result.ToList();\n            }, new KeyValuePair<string, ValueTriple<double, double, int>>(key, CreateTriple(fromScore, toScore, count)));\n        }\n\n        public override void SetRangeInHash(string key, IEnumerable<KeyValuePair<string, string>> keyValuePairs)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n            if (keyValuePairs == null) throw new ArgumentNullException(nameof(keyValuePairs));\n\n            _storage.UseTransaction(_dedicatedConnection, static (storage, connection, transaction, pair) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName => $@\"\nset xact_abort off;\nbegin try\n  insert into [{schemaName}].Hash ([Key], Field, Value) values (@key, @field, @value);\n  if @@ROWCOUNT = 0 update [{schemaName}].Hash set Value = @value where [Key] = @key and Field = @field;\nend try\nbegin catch\n  declare @em nvarchar(4000), @es int, @est int;\n  select @em=error_message(),@es=error_severity(),@est=error_state();\n  IF ERROR_NUMBER() not in (2601, 2627) raiserror(@em, @es, @est);\n  update [{schemaName}].Hash set Value = @value where [Key] = @key and Field = @field;\nend catch\");\n\n                var lockResourceKey = $\"{storage.SchemaName}:Hash:Lock\";\n\n                using (var commandBatch = new SqlCommandBatch(connection, transaction, preferBatching: storage.CommandBatchMaxTimeout.HasValue))\n                {\n                    if (!storage.Options.DisableGlobalLocks)\n                    {\n                        var command = connection\n                            .Create(\"SET XACT_ABORT ON;exec sp_getapplock @Resource=@resource, @LockMode=N'Exclusive', @LockOwner=N'Transaction', @LockTimeout=-1;\")\n                            .AddParameter(\"@resource\", lockResourceKey, DbType.String, size: 255);\n                        commandBatch.Append(command);\n                    }\n\n                    foreach (var keyValuePair in pair.Value)\n                    {\n                        var command = connection.Create(query)\n                            .AddParameter(\"@key\", pair.Key, DbType.String)\n                            .AddParameter(\"@field\", keyValuePair.Key, DbType.String, size: 100)\n                            .AddParameter(\"@value\", (object)keyValuePair.Value ?? DBNull.Value, DbType.String, size: -1);\n                        commandBatch.Append(command);\n                    }\n\n                    if (!storage.Options.DisableGlobalLocks)\n                    {\n                        var command = connection\n                            .Create(\"exec sp_releaseapplock @Resource=@resource, @LockOwner=N'Transaction';\")\n                            .AddParameter(\"@resource\", lockResourceKey, DbType.String, size: 255);\n                        commandBatch.Append(command);\n                    }\n\n                    commandBatch.CommandTimeout = storage.CommandTimeout;\n                    commandBatch.CommandBatchMaxTimeout = storage.CommandBatchMaxTimeout;\n\n                    commandBatch.ExecuteNonQuery();\n                }\n            }, new KeyValuePair<string, IEnumerable<KeyValuePair<string, string>>>(key, keyValuePairs));\n        }\n\n        public override Dictionary<string, string> GetAllEntriesFromHash(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select Field, Value from [{schemaName}].Hash with (forceseek) where [Key] = @key\");\n\n                var result = connection.Query<SqlHash>(\n                    query,\n                    new { key = key },\n                    commandTimeout: storage.CommandTimeout)\n                    .ToDictionary(static x => x.Field, static x => x.Value);\n\n                return result.Count != 0 ? result : null;\n            }, key);\n        }\n\n        public override void AnnounceServer(string serverId, ServerContext context)\n        {\n            if (serverId == null) throw new ArgumentNullException(nameof(serverId));\n            if (context == null) throw new ArgumentNullException(nameof(context));\n\n            var data = new ServerData\n            {\n                WorkerCount = context.WorkerCount,\n                Queues = context.Queues,\n                StartedAt = DateTime.UtcNow,\n            };\n\n            _storage.UseConnection(_dedicatedConnection, static (storage, connection, pair) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\";merge [{schemaName}].Server with (holdlock) as Target\nusing (VALUES (@id, @data, sysutcdatetime())) as Source (Id, Data, Heartbeat)\non Target.Id = Source.Id\nwhen matched then update set Data = Source.Data, LastHeartbeat = Source.Heartbeat\nwhen not matched then insert (Id, Data, LastHeartbeat) values (Source.Id, Source.Data, Source.Heartbeat);\");\n\n                return connection.Execute(\n                    query,\n                    new { id = pair.Key, data = pair.Value },\n                    commandTimeout: storage.CommandTimeout);\n            }, new KeyValuePair<string, string>(serverId, SerializationHelper.Serialize(data)));\n        }\n\n        public override void RemoveServer(string serverId)\n        {\n            if (serverId == null) throw new ArgumentNullException(nameof(serverId));\n\n            _storage.UseConnection(_dedicatedConnection, static (storage, connection, serverId) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"delete S from [{schemaName}].Server S with (forceseek) where Id = @id\");\n\n                return connection.Execute(\n                    query,\n                    new { id = serverId },\n                    commandTimeout: storage.CommandTimeout);\n            }, serverId);\n        }\n\n        public override void Heartbeat(string serverId)\n        {\n            if (serverId == null) throw new ArgumentNullException(nameof(serverId));\n\n            _storage.UseConnection(_dedicatedConnection, static (storage, connection, serverId) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"update [{schemaName}].Server set LastHeartbeat = sysutcdatetime() where Id = @id\");\n\n                var affected = connection.Execute(\n                    query,\n                    new { id = serverId },\n                    commandTimeout: storage.CommandTimeout);\n\n                if (affected == 0)\n                {\n                    throw new BackgroundServerGoneException();\n                }\n\n                return affected;\n            }, serverId);\n        }\n\n        public override int RemoveTimedOutServers(TimeSpan timeOut)\n        {\n            if (timeOut.Duration() != timeOut)\n            {\n                throw new ArgumentException(\"The `timeOut` value must be positive.\", nameof(timeOut));\n            }\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, timeout) => connection.Execute(\n                storage.GetQueryFromTemplate(static schemaName =>\n                    $@\"delete s from [{schemaName}].Server s with (readpast, readcommitted) where LastHeartbeat < dateadd(ms, @timeoutMsNeg, sysutcdatetime())\"),\n                new { timeoutMsNeg = timeout.Negate().TotalMilliseconds },\n                commandTimeout: storage.CommandTimeout), timeOut);\n        }\n\n        public override long GetSetCount(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) => connection.ExecuteScalar<long>(\n                storage.GetQueryFromTemplate(static schemaName =>\n                    $@\"select count(*) from [{schemaName}].[Set] with (forceseek) where [Key] = @key\"),\n                new { key = key },\n                commandTimeout: storage.CommandTimeout), key);\n        }\n\n        public override long GetSetCount(IEnumerable<string> keys, int limit)\n        {\n            if (keys == null) throw new ArgumentNullException(nameof(keys));\n            if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit), \"Value must be greater or equal to 0.\");\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, pair) => connection.ExecuteScalar<long>(\n                storage.GetQueryFromTemplate(static schemaName =>\n$@\"select count(*) from (\n  select top(@limit) 1 as N from [{schemaName}].[Set] with (forceseek) where [Key] in @keys\n) a\"),\n                new { keys = pair.Key, limit = pair.Value },\n                commandTimeout: storage.CommandTimeout),\n                new KeyValuePair<IEnumerable<string>, int>(keys, limit));\n        }\n\n        public override bool GetSetContains(string key, string value)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n            if (value == null) throw new ArgumentNullException(nameof(value));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, pair) => connection.ExecuteScalar<int>(\n                storage.GetQueryFromTemplate(static schemaName =>\n                    $@\"select count(1) from [{schemaName}].[Set] with (forceseek) where [Key] = @key and [Value] = @value\"),\n                new { key = pair.Key, value = pair.Value },\n                commandTimeout: storage.CommandTimeout) == 1, new KeyValuePair<string, string>(key, value)); \n        }\n\n        public override List<string> GetRangeFromSet(string key, int startingFrom, int endingAt)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, triple) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select [Value] from (\n\tselect [Value], row_number() over (order by [Score] ASC) as row_num\n\tfrom [{schemaName}].[Set] with (forceseek)\n\twhere [Key] = @key \n) as s where s.row_num between @startingFrom and @endingAt\");\n\n                return connection\n                    .Query<string>(query, new { key = triple.Item1, startingFrom = triple.Item2 + 1, endingAt = triple.Item3 + 1 },\n                        commandTimeout: storage.CommandTimeout)\n                    .ToList();\n            }, CreateTriple(key, startingFrom, endingAt));\n        }\n\n        public override TimeSpan GetSetTtl(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select min([ExpireAt]) from [{schemaName}].[Set] with (forceseek) where [Key] = @key\");\n\n                var result = connection.ExecuteScalar<DateTime?>(query, new { key = key }, commandTimeout: storage.CommandTimeout);\n                if (!result.HasValue) return TimeSpan.FromSeconds(-1);\n\n                return result.Value - DateTime.UtcNow;\n            }, key);\n        }\n\n        public override long GetCounter(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select sum(s.[Value]) from (select sum([Value]) as [Value] from [{schemaName}].Counter with (forceseek)\nwhere [Key] = @key\nunion all\nselect [Value] from [{schemaName}].AggregatedCounter with (forceseek)\nwhere [Key] = @key) as s\");\n\n                return connection.ExecuteScalar<long?>(query, new { key = key },\n                    commandTimeout: storage.CommandTimeout) ?? 0;\n            }, key);\n        }\n\n        public override long GetHashCount(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select count(*) from [{schemaName}].Hash with (forceseek) where [Key] = @key\");\n\n                return connection.ExecuteScalar<long>(query, new { key = key },\n                    commandTimeout: storage.CommandTimeout);\n            }, key);\n        }\n\n        public override TimeSpan GetHashTtl(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select min([ExpireAt]) from [{schemaName}].Hash with (forceseek) where [Key] = @key\");\n\n                var result = connection.ExecuteScalar<DateTime?>(query, new { key = key }, commandTimeout: storage.CommandTimeout);\n                if (!result.HasValue) return TimeSpan.FromSeconds(-1);\n\n                return result.Value - DateTime.UtcNow;\n            }, key);\n        }\n\n        public override string GetValueFromHash(string key, string name)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n            if (name == null) throw new ArgumentNullException(nameof(name));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, pair) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select [Value] from [{schemaName}].Hash with (forceseek)\nwhere [Key] = @key and [Field] = @field\");\n\n                return connection.ExecuteScalar<string>(query, new { key = pair.Key, field = pair.Value },\n                    commandTimeout: storage.CommandTimeout);\n            }, new KeyValuePair<string, string>(key, name));\n        }\n\n        public override long GetListCount(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select count(*) from [{schemaName}].List with (forceseek)\nwhere [Key] = @key\");\n\n                return connection.ExecuteScalar<long>(query, new { key = key },\n                    commandTimeout: storage.CommandTimeout);\n            }, key);\n        }\n\n        public override TimeSpan GetListTtl(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select min([ExpireAt]) from [{schemaName}].List with (forceseek)\nwhere [Key] = @key\");\n\n                var result = connection.ExecuteScalar<DateTime?>(query, new { key = key }, commandTimeout: storage.CommandTimeout);\n                if (!result.HasValue) return TimeSpan.FromSeconds(-1);\n\n                return result.Value - DateTime.UtcNow;\n            }, key);\n        }\n\n        public override List<string> GetRangeFromList(string key, int startingFrom, int endingAt)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, triple) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select [Value] from (\n\tselect [Value], row_number() over (order by [Id] desc) as row_num \n\tfrom [{schemaName}].List with (forceseek)\n\twhere [Key] = @key \n) as s where s.row_num between @startingFrom and @endingAt\");\n\n                return connection\n                    .Query<string>(query, new { key = triple.Item1, startingFrom = triple.Item2 + 1, endingAt = triple.Item3 + 1 },\n                        commandTimeout: storage.CommandTimeout)\n                    .ToList();\n            }, CreateTriple(key, startingFrom, endingAt));\n        }\n\n        public override List<string> GetAllItemsFromList(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select [Value] from [{schemaName}].List with (forceseek)\nwhere [Key] = @key\norder by [Id] desc\");\n\n                return connection\n                    .Query<string>(query, new { key = key }, commandTimeout: storage.CommandTimeout)\n                    .ToList();\n            }, key);\n        }\n\n        public override DateTime GetUtcDateTime()\n        {\n            return _storage.UseConnection(_dedicatedConnection, static (_, connection) =>\n                DateTime.SpecifyKind(connection.ExecuteScalar<DateTime>(\"SELECT SYSUTCDATETIME()\"), DateTimeKind.Utc));\n        }\n\n        private DbConnection _dedicatedConnection;\n\n        internal DisposableLock AcquireLock(string resource, TimeSpan timeout)\n        {\n            if (_dedicatedConnection == null)\n            {\n                _dedicatedConnection = _storage.CreateAndOpenConnection();\n            }\n\n            var lockId = Guid.NewGuid();\n            var ownLock = false;\n\n            if (!_lockedResources.TryGetValue(resource, out var lockIds))\n            {\n                try\n                {\n                    SqlServerDistributedLock.Acquire(_dedicatedConnection, resource, timeout);\n                    ownLock = true;\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    ReleaseLock(resource, lockId, true, false);\n                    throw;\n                }\n\n                _lockedResources.Add(resource, lockIds = new HashSet<Guid>());\n            }\n\n            lockIds.Add(lockId);\n            return new DisposableLock(this, resource, lockId, ownLock);\n        }\n\n        private void ReleaseLock(string resource, Guid lockId, bool onDisposing, bool releasedExternally)\n        {\n            try\n            {\n                if (_lockedResources.TryGetValue(resource, out var lockIds))\n                {\n                    if (lockIds.Contains(lockId))\n                    {\n                        if (lockIds.Remove(lockId) &&\n                            lockIds.Count == 0 &&\n                            _lockedResources.Remove(resource) &&\n                            _dedicatedConnection.State == ConnectionState.Open)\n                        {\n                            // Session-scoped application locks are held only when connection\n                            // is open. When connection is closed or broken, for example, when\n                            // there was an error, application lock is already released by SQL\n                            // Server itself, and we shouldn't do anything.\n                            if (!releasedExternally)\n                            {\n                                SqlServerDistributedLock.Release(_dedicatedConnection, resource);\n                            }\n                        }\n                    }\n                }\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                if (!onDisposing)\n                {\n                    throw;\n                }\n            }\n            finally\n            {\n                if (_lockedResources.Count == 0)\n                {\n                    _storage.ReleaseConnection(_dedicatedConnection);\n                    _dedicatedConnection = null;\n                }\n            }\n        }\n\n        internal sealed class DisposableLock : IDisposable\n        {\n            private bool _disposed;\n            private readonly SqlServerConnection _connection;\n            private readonly string _resource;\n            private readonly Guid _lockId;\n\n            public DisposableLock(SqlServerConnection connection, string resource, Guid lockId, bool ownLock)\n            {\n                _connection = connection;\n                _resource = resource;\n                _lockId = lockId;\n                OwnLock = ownLock;\n            }\n\n            public string Resource => _resource;\n            public bool OwnLock { get; }\n            public bool ReleasedExternally { get; private set; }\n\n            public void Dispose()\n            {\n                if (_disposed) return;\n                _disposed = true;\n                _connection.ReleaseLock(_resource, _lockId, true, ReleasedExternally);\n            }\n\n            public void TryReportReleased()\n            {\n                if (OwnLock) ReleasedExternally = true;\n            }\n        }\n\n        private static ValueTriple<T1, T2, T3> CreateTriple<T1, T2, T3>(T1 item1, T2 item2, T3 item3)\n        {\n            return new ValueTriple<T1, T2, T3>(item1, item2, item3);\n        }\n\n        // .NET Framework 4.5 doesn't have ValueTuple class.\n        private readonly struct ValueTriple<T1, T2, T3>(T1 item1, T2 item2, T3 item3)\n        {\n            public T1 Item1 { get; } = item1;\n            public T2 Item2 { get; } = item2;\n            public T3 Item3 { get; } = item3;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerDistributedLock.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.Annotations;\nusing Hangfire.Storage;\n\nnamespace Hangfire.SqlServer\n{\n    public class SqlServerDistributedLock : IDisposable\n    {\n        private static readonly TimeSpan LockTimeout = TimeSpan.FromSeconds(1);\n\n        private const string LockMode = \"Exclusive\";\n        private const string LockOwner = \"Session\";\n\n        // Connections to SQL Azure Database that are idle for 30 minutes \n        // or longer will be terminated. And since we are using separate\n        // connection for a distributed lock, we'd like to prevent Resource\n        // Governor from terminating it.\n        private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1);\n\n        private static readonly IDictionary<int, string> LockErrorMessages\n            = new Dictionary<int, string>\n            {\n                { -1, \"The lock request timed out\" },\n                { -2, \"The lock request was canceled\" },\n                { -3, \"The lock request was chosen as a deadlock victim\" },\n                { -999, \"Indicates a parameter validation or other call error\" }\n            };\n\n        private static readonly ThreadLocal<Dictionary<string, int>> AcquiredLocks\n            = new ThreadLocal<Dictionary<string, int>>(static () => new Dictionary<string, int>()); \n\n        private DbConnection _connection;\n        private readonly SqlServerStorage _storage;\n        private readonly string _resource;\n        private readonly Timer _timer;\n        private readonly object _lockObject = new object();\n\n        private bool _completed;\n\n        [Obsolete(\"Don't use this class directly, use SqlServerConnection.AcquireDistributedLock instead as it provides better safety. Will be removed in 2.0.0.\")]\n        [SuppressMessage(\"Performance\", \"CA1854:Prefer the \\'IDictionary.TryGetValue(TKey, out TValue)\\' method\")]\n        public SqlServerDistributedLock([NotNull] SqlServerStorage storage, [NotNull] string resource, TimeSpan timeout)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (String.IsNullOrEmpty(resource)) throw new ArgumentNullException(nameof(resource));\n\n            _storage = storage;\n            _resource = resource;\n\n            if (!AcquiredLocks.Value.ContainsKey(_resource) || AcquiredLocks.Value[_resource] == 0)\n            {\n                _connection = storage.CreateAndOpenConnection();\n\n                try\n                {\n                    Acquire(_connection, _resource, timeout);\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    storage.ReleaseConnection(_connection);\n                    throw;\n                }\n\n                if (!_storage.IsExistingConnection(_connection))\n                {\n                    _timer = new Timer(ExecuteKeepAliveQuery, null, KeepAliveInterval, KeepAliveInterval);\n                }\n\n                AcquiredLocks.Value[_resource] = 1;\n            }\n            else\n            {\n                AcquiredLocks.Value[_resource]++;\n            }\n        }\n\n        [SuppressMessage(\"Performance\", \"CA1854:Prefer the \\'IDictionary.TryGetValue(TKey, out TValue)\\' method\")]\n        public void Dispose()\n        {\n            if (_completed) return;\n\n            _completed = true;\n\n            if (!AcquiredLocks.Value.ContainsKey(_resource)) return;\n\n            AcquiredLocks.Value[_resource]--;\n\n            if (AcquiredLocks.Value[_resource] != 0) return;\n\n            lock (_lockObject)\n            {\n                // Timer callback may be invoked after the Dispose method call,\n                // so we are using lock to avoid unsynchronized calls.\n\n                try\n                {\n                    AcquiredLocks.Value.Remove(_resource);\n\n                    _timer?.Dispose();\n\n                    if (_connection.State == ConnectionState.Open)\n                    {\n                        // Session-scoped application locks are held only when connection\n                        // is open. When connection is closed or broken, for example, when\n                        // there was an error, application lock is already released by SQL\n                        // Server itself, and we shouldn't do anything.\n                        Release(_connection, _resource);\n                    }\n                }\n                finally\n                {\n                    _storage.ReleaseConnection(_connection);\n                    _connection = null;\n                }\n            }\n\n            GC.SuppressFinalize(this);\n        }\n\n        private void ExecuteKeepAliveQuery(object obj)\n        {\n            lock (_lockObject)\n            {\n                try\n                {\n                    _connection?.Execute(\"SELECT 1;\");\n                }\n                catch\n                {\n                    // Connection is broken. This means that distributed lock\n                    // was released, and we can't guarantee the safety property\n                    // for the code that is wrapped with this block. So it was\n                    // a bad idea to have a separate connection for just\n                    // distributed lock.\n                    \n                    // OBSOLETE. This class is not used anymore by the SqlServerConnection\n                    // class. The problem above was solved there by establishing a\n                    // dedicated connection, when there is at least one acquired lock.\n                    // Since the acquisition, all the commands and transactions are routed\n                    // through that connection to ensure all the locks are still active.\n                }\n            }\n        }\n\n        internal static void Acquire(DbConnection connection, string resource, TimeSpan timeout)\n        {\n            if (connection.State != ConnectionState.Open)\n            {\n                // When we are passing a closed connection to Dapper's Execute method,\n                // it kindly opens it for us, but after command execution, it will be closed\n                // automatically, and our just-acquired application lock will immediately\n                // be released. This is not behavior we want to achieve, so let's throw an\n                // exception instead.\n                throw new InvalidOperationException(\"Connection must be open before acquiring a distributed lock.\");\n            }\n\n            var started = Stopwatch.StartNew();\n\n            // We can't pass our timeout directly to the sp_getapplock stored procedure, because\n            // high values, such as minute or more, may cause SQL Server's thread pool starvation,\n            // when the number of connections that try to acquire a lock is more than the number of \n            // available threads in SQL Server. In this case a deadlock will occur, when SQL Server \n            // tries to schedule some more work for a connection that acquired a lock, but all the \n            // available threads in a pool waiting for that lock to be released.\n            //\n            // So we are trying to acquire a lock multiple times instead, with timeout that's equal\n            // to seconds, not minutes.\n            var lockTimeout = (long) Math.Min(LockTimeout.TotalMilliseconds, timeout.TotalMilliseconds);\n\n            do\n            {\n                using var command = connection\n                    .Create(\"sp_getapplock\", CommandType.StoredProcedure, timeout: (int)(lockTimeout / 1000) + 5)\n                    .AddParameter(\"@Resource\", resource, DbType.String, size: 255)\n                    .AddParameter(\"@DbPrincipal\", \"public\", DbType.String, size: 32)\n                    .AddParameter(\"@LockMode\", LockMode, DbType.String, size: 32)\n                    .AddParameter(\"@LockOwner\", LockOwner, DbType.String, size: 32)\n                    .AddParameter(\"@LockTimeout\", lockTimeout, DbType.Int32)\n                    .AddReturnParameter(\"@Result\", out var resultParameter, DbType.Int32);\n\n                command.ExecuteNonQuery();\n\n                var lockResult = (int)resultParameter.Value;\n\n                if (lockResult >= 0)\n                {\n                    // The lock has been successfully obtained on the specified resource.\n                    return;\n                }\n\n                if (lockResult == -999 /* Indicates a parameter validation or other call error. */)\n                {\n                    throw new SqlServerDistributedLockException(\n                        $\"Could not place a lock on the resource '{resource}': {(LockErrorMessages.TryGetValue(lockResult, out var message) ? message : $\"Server returned the '{lockResult}' error.\")}.\");\n                }\n            } while (started.Elapsed < timeout);\n\n            throw new DistributedLockTimeoutException(resource);\n        }\n\n        internal static void Release(DbConnection connection, string resource)\n        {\n            using (var command = CreateReleaseCommand(connection, resource, out var resultParameter))\n            {\n                command.ExecuteNonQuery();\n\n                var releaseResult = (int)resultParameter.Value;\n\n                if (releaseResult < 0)\n                {\n                    throw new SqlServerDistributedLockException(\n                        $\"Could not release a lock on the resource '{resource}': Server returned the '{releaseResult}' error.\");\n                }\n            }\n        }\n\n        internal static DbCommand CreateReleaseCommand(\n            DbConnection connection,\n            string resource,\n            out DbParameter resultParameter)\n        {\n            return connection.Create(\"sp_releaseapplock\", CommandType.StoredProcedure)\n                .AddParameter(\"@Resource\", resource, DbType.String, size: 255)\n                .AddParameter(\"@LockOwner\", LockOwner, DbType.String, size: 32)\n                .AddReturnParameter(\"@Result\", out resultParameter, DbType.Int32);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerDistributedLockException.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Runtime.Serialization;\n\nnamespace Hangfire.SqlServer\n{\n#if !NETSTANDARD1_3\n    [Serializable]\n#endif\n    public class SqlServerDistributedLockException : Exception\n    {\n        public SqlServerDistributedLockException(string message)\n            : base(message)\n        {\n        }\n        \n#if !NETSTANDARD1_3\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"SqlServerDistributedLockException\"/> class\n        /// with serialized data.\n        /// </summary>\n        /// <param name=\"info\">The <see cref=\"SerializationInfo\"/> that holds the serialized object data about the exception being thrown.</param>\n        /// <param name=\"context\">The <see cref=\"StreamingContext\"/> that contains contextual information about the source or destination.</param>\n        protected SqlServerDistributedLockException(SerializationInfo info, StreamingContext context)\n            : base(info, context)\n        {\n        }\n#endif\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerHeartbeatProcess.cs",
    "content": "// This file is part of Hangfire. Copyright © 2021 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Threading;\nusing Hangfire.Common;\nusing Hangfire.Server;\n\nnamespace Hangfire.SqlServer\n{\n#pragma warning disable CS0618\n    internal sealed class SqlServerHeartbeatProcess : IServerComponent, IBackgroundProcess\n#pragma warning restore CS0618\n    {\n        private readonly ConcurrentDictionary<SqlServerTimeoutJob, object> _items =\n            new ConcurrentDictionary<SqlServerTimeoutJob, object>();\n\n        public void Track(SqlServerTimeoutJob item)\n        {\n            _items.TryAdd(item, null);\n        }\n\n        public void Untrack(SqlServerTimeoutJob item)\n        {\n            _items.TryRemove(item, out _);\n        }\n\n        public void Execute(BackgroundProcessContext context)\n        {\n            Execute(context.ShutdownToken);\n        }\n\n        public void Execute(CancellationToken cancellationToken)\n        {\n            foreach (var item in _items)\n            {\n                item.Key.ExecuteKeepAliveQueryIfRequired();\n            }\n\n            cancellationToken.Wait(TimeSpan.FromSeconds(1));\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerJobQueue.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Globalization;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Storage;\n\n// ReSharper disable RedundantAnonymousTypePropertyName\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class SqlServerJobQueue : IPersistentJobQueue\n    {\n        // This is an optimization that helps to overcome the polling delay, when\n        // both client and server reside in the same process. Everything is working\n        // without these events, but it helps to reduce the delays in processing.\n        internal static readonly ConcurrentDictionary<Tuple<SqlServerStorage, string>, AutoResetEvent> NewItemInQueueEvents = new();\n\n        private static readonly Func<Tuple<SqlServerStorage, string>, SemaphoreSlim> CreateSemaphoreFunc = CreateSemaphore;\n        private static readonly TimeSpan LongPollingThreshold = TimeSpan.FromSeconds(1);\n        private static readonly int PollingQuantumMs = 1000;\n        private static readonly int DefaultPollingDelayMs = 200;\n        private static readonly int MinPollingDelayMs = 100;\n        private static readonly ConcurrentDictionary<Tuple<SqlServerStorage, string>, SemaphoreSlim> Semaphores =\n            new ConcurrentDictionary<Tuple<SqlServerStorage, string>, SemaphoreSlim>();\n\n        private readonly SqlServerStorage _storage;\n        private readonly SqlServerStorageOptions _options;\n\n        public SqlServerJobQueue([NotNull] SqlServerStorage storage, SqlServerStorageOptions options)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            _storage = storage;\n            _options = options;\n        }\n\n        [NotNull]\n        public IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken)\n        {\n            if (queues == null) throw new ArgumentNullException(nameof(queues));\n            if (queues.Length == 0) throw new ArgumentException(\"Queue array must be non-empty.\", nameof(queues));\n\n            if (_options.SlidingInvisibilityTimeout.HasValue)\n            {\n                return DequeueUsingSlidingInvisibilityTimeout(queues, cancellationToken);\n            }\n\n            return DequeueUsingTransaction(queues, cancellationToken);\n        }\n\n#if FEATURE_TRANSACTIONSCOPE\n        public void Enqueue(IDbConnection connection, string queue, string jobId)\n#else\n        public void Enqueue(DbConnection connection, DbTransaction transaction, string queue, string jobId)\n#endif\n        {\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].JobQueue (JobId, Queue) values (@jobId, @queue)\");\n\n            using var command = ((DbConnection)connection).Create(query, timeout: _storage.CommandTimeout);\n            command.AddParameter(\"@jobId\", long.Parse(jobId, CultureInfo.InvariantCulture), DbType.Int64);\n            command.AddParameter(\"@queue\", queue, DbType.String);\n\n#if !FEATURE_TRANSACTIONSCOPE\n            command.Transaction = transaction;\n#endif\n\n            command.ExecuteNonQuery();\n        }\n\n        private SqlServerTimeoutJob DequeueUsingSlidingInvisibilityTimeout(string[] queues, CancellationToken cancellationToken)\n        {\n            if (queues == null) throw new ArgumentNullException(nameof(queues));\n            if (queues.Length == 0) throw new ArgumentException(\"Queue array must be non-empty.\", nameof(queues));\n            \n            cancellationToken.ThrowIfCancellationRequested();\n\n            // First we will check if our queues has any background jobs in it and\n            // return if any. In this case we don't need any additional logic like\n            // semaphores or waiting.\n            var fetchedJob = FetchJob(queues);\n            if (fetchedJob != null) return fetchedJob;\n\n            // Then we determine whether we should use the long polling feature,\n            // where only a single worker acquires a semaphore for each queue set\n            // to avoid excessive load on a database.\n            var configuredPollInterval = _options.QueuePollInterval;\n            var useLongPolling = configuredPollInterval < LongPollingThreshold;\n\n            // Then we determine a delay between attempts. For long-polling we use constrained\n            // sub-second intervals within the [MinPollingDelayMs, PollingQuantumMs] interval.\n            // For regular polling we just use the interval defined in the QueuePollInterval\n            // option.\n            var pollingDelayMs = useLongPolling\n                ? TimeSpan.FromMilliseconds(\n                    Math.Min(\n                        Math.Max(\n                            configuredPollInterval == TimeSpan.Zero ? DefaultPollingDelayMs : (int)configuredPollInterval.TotalMilliseconds,\n                            MinPollingDelayMs),\n                        PollingQuantumMs))\n                : configuredPollInterval;\n\n            var queuesString = String.Join(\"_\", queues.OrderBy(static x => x));\n            var resource = Tuple.Create(_storage, queuesString);\n\n            using var cancellationEvent = cancellationToken.GetCancellationEvent();\n            var waitArray = GetWaitArrayForQueueSignals(_storage, queues, cancellationEvent);\n\n            SemaphoreSlim semaphore = null;\n\n            try\n            {\n                semaphore = Semaphores.GetOrAdd(resource, CreateSemaphoreFunc);\n                semaphore.Wait(cancellationToken);\n\n                while (!cancellationToken.IsCancellationRequested)\n                {\n                    // For non-first attempts we just trying again and again with\n                    // the determined delay between attempts, until shutdown\n                    // request is received.\n                    fetchedJob = FetchJob(queues);\n                    if (fetchedJob != null) return fetchedJob;\n\n                    WaitHandle.WaitAny(waitArray, pollingDelayMs);\n                    cancellationToken.ThrowIfCancellationRequested();\n                }\n            }\n            finally\n            {\n                if (semaphore != null && semaphore.CurrentCount == 0)\n                {\n                    semaphore.Release();\n                }\n            }\n\n            cancellationToken.ThrowIfCancellationRequested();\n            return null;\n        }\n\n        private static SemaphoreSlim CreateSemaphore(Tuple<SqlServerStorage, string> _)\n        {\n            return new SemaphoreSlim(initialCount: 1);\n        }\n\n        private SqlServerTimeoutJob FetchJob(string[] queues)\n        {\n            return _storage.UseConnection(null, static (storage, connection, queues) =>\n            {\n                if (!storage.Options.SlidingInvisibilityTimeout.HasValue)\n                {\n                    throw new InvalidOperationException(\"This method should be called only when SlidingInvisibilityTimeout is set.\");\n                }\n\n                var invisibilityTimeout = (int)storage.Options.SlidingInvisibilityTimeout.Value.Negate().TotalSeconds;\n\n                using var command = CreateNonBlockingFetchCommand(storage, connection, queues, invisibilityTimeout);\n                using var reader = command.ExecuteReader();\n\n                if (!reader.Read()) return null;\n\n                var id = Convert.ToInt64(reader.GetValue(reader.GetOrdinal(\"Id\")), CultureInfo.InvariantCulture); // Can be Int32 in older schemas\n                var jobId = Convert.ToInt64(reader.GetValue(reader.GetOrdinal(\"JobId\")), CultureInfo.InvariantCulture); // Can be Int32 in older schemas\n                var queue = reader.GetString(reader.GetOrdinal(\"Queue\"));\n                var fetchedAt = reader.GetDateTime(reader.GetOrdinal(\"FetchedAt\"));\n\n                if (reader.Read())\n                {\n                    throw new InvalidOperationException(\"Multiple rows returned from SQL Server, while expecting single or none.\");\n                }\n\n                return new SqlServerTimeoutJob(storage, id, jobId.ToString(CultureInfo.InvariantCulture), queue, fetchedAt);\n            }, queues);\n        }\n\n        private static DbCommand CreateNonBlockingFetchCommand(\n            SqlServerStorage storage,\n            DbConnection connection,\n            string[] queues,\n            int invisibilityTimeout)\n        {\n            var template = storage.GetQueryFromTemplate(static schemaName => $@\"\nset nocount on;set xact_abort on;set tran isolation level read committed;\n\nupdate top (1) JQ\nset FetchedAt = GETUTCDATE()\noutput INSERTED.Id, INSERTED.JobId, INSERTED.Queue, INSERTED.FetchedAt\nfrom [{schemaName}].JobQueue JQ with (forceseek, readpast, updlock, rowlock)\nwhere Queue in @queues and\n(FetchedAt is null or FetchedAt < DATEADD(second, @timeoutSs, GETUTCDATE()));\");\n\n            return connection.Create(template, timeout: storage.CommandTimeout)\n                .AddParameter(\"@timeoutSs\", invisibilityTimeout, DbType.Int32)\n                .AddExpandedParameter(\"@queues\", queues, DbType.String);\n        }\n\n        private SqlServerTransactionJob DequeueUsingTransaction(string[] queues, CancellationToken cancellationToken)\n        {\n            DbTransaction transaction = null;\n\n            var pollInterval = _options.QueuePollInterval > TimeSpan.Zero\n                ? _options.QueuePollInterval\n                : TimeSpan.FromSeconds(1);\n\n            using var cancellationEvent = cancellationToken.GetCancellationEvent();\n            var waitArray = GetWaitArrayForQueueSignals(_storage, queues, cancellationEvent);\n\n            while (!cancellationToken.IsCancellationRequested)\n            {\n                var connection = _storage.CreateAndOpenConnection();\n\n                try\n                {\n                    transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);\n\n#pragma warning disable 618\n                    using var command = CreateTransactionalFetchCommand(_storage, connection, queues, (int)_options.InvisibilityTimeout.Negate().TotalSeconds);\n#pragma warning restore 618\n                    command.Transaction = transaction;\n\n                    using (var reader = command.ExecuteReader())\n                    {\n                        if (reader.Read())\n                        {\n                            var jobId = Convert.ToInt64(reader.GetValue(reader.GetOrdinal(\"JobId\")), CultureInfo.InvariantCulture); // Can be Int32 in older schemas\n                            var queue = reader.GetString(reader.GetOrdinal(\"Queue\"));\n\n                            if (reader.Read())\n                            {\n                                throw new InvalidOperationException(\n                                    \"Multiple rows returned from SQL Server, while expecting single or none.\");\n                            }\n\n                            var result = new SqlServerTransactionJob(_storage, connection, transaction,\n                                jobId.ToString(CultureInfo.InvariantCulture), queue);\n\n                            // We shouldn't dispose them, because their ownership is now related\n                            // to the SqlServerTransactionJob instance.\n                            connection = null;\n                            transaction = null;\n                            return result;\n                        }\n                    }\n\n                    // Nothing updated, just commit the empty transaction.\n                    transaction.Commit();\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    // Check connection isn't broken first, and that transaction\n                    // can be rolled back without throwing InvalidOperationException\n                    // on older System.Data.SqlClient in .NET Core.\n                    // https://github.com/HangfireIO/Hangfire/issues/1494\n                    // https://github.com/dotnet/efcore/issues/12864\n                    if (transaction?.Connection != null) transaction.Rollback();\n                    throw;\n                }\n                finally\n                {\n                    transaction?.Dispose();\n                    transaction = null;\n                    _storage.ReleaseConnection(connection);\n                }\n\n                WaitHandle.WaitAny(waitArray, pollInterval);\n            }\n\n            cancellationToken.ThrowIfCancellationRequested();\n            return null;\n        }\n\n        private static DbCommand CreateTransactionalFetchCommand(\n            SqlServerStorage storage,\n            DbConnection connection,\n            string[] queues,\n            int invisibilityTimeout)\n        {\n            var template = storage.GetQueryFromTemplate(static schemaName => \n                $@\"delete top (1) JQ\noutput DELETED.Id, DELETED.JobId, DELETED.Queue\nfrom [{schemaName}].JobQueue JQ with (readpast, updlock, rowlock, forceseek)\nwhere Queue in @queues and (FetchedAt is null or FetchedAt < DATEADD(second, @timeout, GETUTCDATE()))\");\n            \n            return connection\n                .Create(template, timeout: storage.CommandTimeout)\n                .AddParameter(\"@timeout\", invisibilityTimeout, DbType.Int32)\n                .AddExpandedParameter(\"@queues\", queues, DbType.String);\n        }\n\n        private static WaitHandle[] GetWaitArrayForQueueSignals(SqlServerStorage storage, string[] queues, CancellationTokenExtentions.CancellationEvent cancellationEvent)\n        {\n            var waitList = new List<WaitHandle>(capacity: queues.Length + 1)\n            {\n                cancellationEvent.WaitHandle\n            };\n\n            foreach (var queue in queues)\n            {\n                waitList.Add(NewItemInQueueEvents.GetOrAdd(Tuple.Create(storage, queue), static _ => new AutoResetEvent(initialState: false)));\n            }\n\n            return waitList.ToArray();\n        }\n\n        [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]\n        private sealed class FetchedJob\n        {\n            public long Id { get; set; }\n            public long JobId { get; set; }\n            public string Queue { get; set; }\n            public DateTime? FetchedAt { get; set; }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerJobQueueMonitoringApi.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing Dapper;\nusing Hangfire.Annotations;\n\n// ReSharper disable RedundantAnonymousTypePropertyName\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class SqlServerJobQueueMonitoringApi : IPersistentJobQueueMonitoringApi\n    {\n        private static readonly TimeSpan QueuesCacheTimeout = TimeSpan.FromSeconds(5);\n\n        private readonly SqlServerStorage _storage;\n        private readonly object _cacheLock = new object();\n\n        private List<string> _queuesCache = new List<string>();\n        private Stopwatch _cacheUpdated;\n\n        public SqlServerJobQueueMonitoringApi([NotNull] SqlServerStorage storage)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            _storage = storage;\n        }\n\n        public IEnumerable<string> GetQueues()\n        {\n            lock (_cacheLock)\n            {\n                if (_queuesCache.Count == 0 || _cacheUpdated.Elapsed > QueuesCacheTimeout)\n                {\n                    var result = _storage.UseConnection(null, static (storage, connection) =>\n                    {\n                        var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select distinct(Queue) from [{schemaName}].JobQueue with (nolock)\");\n\n                        return connection.Query(query, commandTimeout: storage.CommandTimeout).Select(static x => (string) x.Queue).ToList();\n                    });\n\n                    _queuesCache = result;\n                    _cacheUpdated = Stopwatch.StartNew();\n                }\n\n                return _queuesCache.ToList();\n            }  \n        }\n\n        public IEnumerable<long> GetEnqueuedJobIds(string queue, int from, int perPage)\n        {\n            return _storage.UseConnection(null, static (storage, connection, ctx) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName =>\n$@\"select r.JobId from (\n  select jq.JobId, row_number() over (order by jq.Id) as row_num \n  from [{schemaName}].JobQueue jq with (nolock, forceseek)\n  where jq.Queue = @queue and jq.FetchedAt is null\n) as r\nwhere r.row_num between @start and @end\");\n\n                return connection.Query<JobIdDto>(\n                    query,\n                    new { queue = ctx.Queue, start = ctx.From + 1, end = ctx.From + ctx.PerPage },\n                    commandTimeout: storage.CommandTimeout)\n                    .ToList()\n                    .Select(static x => x.JobId)\n                    .ToList();\n            }, new QueuePageQueryContext(queue, from, perPage));\n        }\n\n        public IEnumerable<long> GetFetchedJobIds(string queue, int from, int perPage)\n        {\n            return _storage.UseConnection(null, static (storage, connection, ctx) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName => $@\"\nselect r.JobId from (\n  select jq.JobId, jq.FetchedAt, row_number() over (order by jq.Id) as row_num \n  from [{schemaName}].JobQueue jq with (nolock, forceseek)\n  where jq.Queue = @queue and jq.FetchedAt is not null\n) as r\nwhere r.row_num between @start and @end\");\n\n                return connection.Query<JobIdDto>(\n                        query,\n                        new { queue = ctx.Queue, start = ctx.From + 1, end = ctx.From + ctx.PerPage })\n                    .ToList()\n                    .Select(static x => x.JobId)\n                    .ToList();\n            }, new QueuePageQueryContext(queue, from, perPage));\n        }\n\n        private readonly struct QueuePageQueryContext(string queue, int from, int perPage)\n        {\n            public string Queue { get; } = queue;\n            public int From { get; } = from;\n            public int PerPage { get; } = perPage;\n        }\n\n        public EnqueuedAndFetchedCountDto GetEnqueuedAndFetchedCount(string queue)\n        {\n            return _storage.UseConnection(null, static (storage, connection, q) =>\n            {\n                var query = storage.GetQueryFromTemplate(static schemaName => $@\"\nselect sum(Enqueued) as EnqueuedCount, sum(Fetched) as FetchedCount \nfrom (\n    select \n        case when FetchedAt is null then 1 else 0 end as Enqueued,\n        case when FetchedAt is not null then 1 else 0 end as Fetched\n    from [{schemaName}].JobQueue with (nolock, forceseek)\n    where Queue = @queue\n) q\");\n\n                var result = connection.QuerySingle(query, new { queue = q });\n\n                return new EnqueuedAndFetchedCountDto\n                {\n                    EnqueuedCount = result.EnqueuedCount,\n                    FetchedCount = result.FetchedCount\n                };\n            }, queue);\n        }\n\n        private sealed class JobIdDto\n        {\n            [UsedImplicitly]\n            public long JobId { get; set; }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerJobQueueProvider.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class SqlServerJobQueueProvider : IPersistentJobQueueProvider\n    {\n        private readonly IPersistentJobQueue _jobQueue;\n        private readonly IPersistentJobQueueMonitoringApi _monitoringApi;\n\n        public SqlServerJobQueueProvider([NotNull] SqlServerStorage storage, [NotNull] SqlServerStorageOptions options)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            _jobQueue = new SqlServerJobQueue(storage, options);\n            _monitoringApi = new SqlServerJobQueueMonitoringApi(storage);\n        }\n\n        public IPersistentJobQueue GetJobQueue()\n        {\n            return _jobQueue;\n        }\n\n        public IPersistentJobQueueMonitoringApi GetJobQueueMonitoringApi()\n        {\n            return _monitoringApi;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerMonitoringApi.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data.Common;\nusing System.Globalization;\nusing System.Linq;\nusing Dapper;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.SqlServer.Entities;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Hangfire.Storage.Monitoring;\n\n// ReSharper disable RedundantAnonymousTypePropertyName\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class SqlServerMonitoringApi : JobStorageMonitor\n    {\n        private readonly SqlServerStorage _storage;\n        private readonly int? _jobListLimit;\n\n        public SqlServerMonitoringApi([NotNull] SqlServerStorage storage, int? jobListLimit)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n\n            _storage = storage;\n            _jobListLimit = jobListLimit;\n        }\n\n        public override long ScheduledCount()\n        {\n            return UseConnection(connection => \n                GetNumberOfJobsByStateName(connection, ScheduledState.StateName));\n        }\n\n        public override long EnqueuedCount(string queue)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var queueApi = GetQueueApi(queue);\n            var counters = queueApi.GetEnqueuedAndFetchedCount(queue);\n\n            return counters.EnqueuedCount ?? 0;\n        }\n\n        public override long FetchedCount(string queue)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var queueApi = GetQueueApi(queue);\n            var counters = queueApi.GetEnqueuedAndFetchedCount(queue);\n\n            return counters.FetchedCount ?? 0;\n        }\n\n        public override long FailedCount()\n        {\n            return UseConnection(connection => \n                GetNumberOfJobsByStateName(connection, FailedState.StateName));\n        }\n\n        public override long ProcessingCount()\n        {\n            return UseConnection(connection => \n                GetNumberOfJobsByStateName(connection, ProcessingState.StateName));\n        }\n\n        public override JobList<ProcessingJobDto> ProcessingJobs(int @from, int count)\n        {\n            return UseConnection(connection => GetJobs(\n                connection,\n                from, count,\n                ProcessingState.StateName,\n                descending: false,\n                static (sqlJob, job, invocationData, loadException, stateData) => new ProcessingJobDto\n                {\n                    Job = job,\n                    LoadException = loadException,\n                    InvocationData = invocationData,\n                    InProcessingState = ProcessingState.StateName.Equals(sqlJob.StateName, StringComparison.OrdinalIgnoreCase),\n                    ServerId = stateData.TryGetValue(\"ServerId\", out var serverId) ? serverId : stateData[\"ServerName\"],\n                    StartedAt = sqlJob.StateChanged,\n                    StateData = stateData\n                }));\n        }\n\n        public override JobList<ScheduledJobDto> ScheduledJobs(int @from, int count)\n        {\n            return UseConnection(connection => GetJobs(\n                connection,\n                from, count,\n                ScheduledState.StateName,\n                descending: false,\n                static (sqlJob, job, invocationData, loadException, stateData) => new ScheduledJobDto\n                {\n                    Job = job,\n                    LoadException = loadException,\n                    InvocationData = invocationData,\n                    InScheduledState = ScheduledState.StateName.Equals(sqlJob.StateName, StringComparison.OrdinalIgnoreCase),\n                    EnqueueAt = JobHelper.DeserializeNullableDateTime(stateData[\"EnqueueAt\"]) ?? DateTime.MinValue,\n                    ScheduledAt = sqlJob.StateChanged,\n                    StateData = stateData\n                }));\n        }\n\n        public override IDictionary<DateTime, long> SucceededByDatesCount()\n        {\n            return UseConnection(connection => \n                GetTimelineStats(connection, \"succeeded\"));\n        }\n\n        public override IDictionary<DateTime, long> FailedByDatesCount()\n        {\n            return UseConnection(connection => \n                GetTimelineStats(connection, \"failed\"));\n        }\n\n        public override IDictionary<DateTime, long> DeletedByDatesCount()\n        {\n            return UseConnection(connection => \n                GetTimelineStats(connection, \"deleted\"));\n        }\n\n        public override IList<ServerDto> Servers()\n        {\n            return UseConnection<IList<ServerDto>>(connection =>\n            {\n                var servers = connection.Query<Entities.Server>(\n                    _storage.GetQueryFromTemplate(static schemaName =>\n                        $@\"select * from [{schemaName}].Server with (nolock)\"),\n                    commandTimeout: _storage.CommandTimeout)\n                    .ToList();\n\n                var result = new List<ServerDto>();\n\n                // ReSharper disable once LoopCanBeConvertedToQuery\n                foreach (var server in servers)\n                {\n                    var data = SerializationHelper.Deserialize<ServerData>(server.Data);\n\n                    if (data.Queues == null && data.StartedAt == null && data.WorkerCount == 0)\n                    {\n                        data = SerializationHelper.Deserialize<ServerData>(server.Data, SerializationOption.User);\n                    }\n\n                    result.Add(new ServerDto\n                    {\n                        Name = server.Id,\n                        Heartbeat = server.LastHeartbeat,\n                        Queues = data.Queues,\n                        StartedAt = data.StartedAt ?? DateTime.MinValue,\n                        WorkersCount = data.WorkerCount\n                    });\n                }\n\n                return result;\n            });\n        }\n\n        public override JobList<FailedJobDto> FailedJobs(int @from, int count)\n        {\n            return UseConnection(connection => GetJobs(\n                connection,\n                from,\n                count,\n                FailedState.StateName,\n                descending: true,\n                static (sqlJob, job, invocationData, loadException, stateData) => new FailedJobDto\n                {\n                    Job = job,\n                    LoadException = loadException,\n                    InvocationData = invocationData,\n                    InFailedState = FailedState.StateName.Equals(sqlJob.StateName, StringComparison.OrdinalIgnoreCase),\n                    Reason = sqlJob.StateReason,\n                    ExceptionDetails = stateData[\"ExceptionDetails\"],\n                    ExceptionMessage = stateData[\"ExceptionMessage\"],\n                    ExceptionType = stateData[\"ExceptionType\"],\n                    FailedAt = sqlJob.StateChanged,\n                    StateData = stateData\n                }));\n        }\n\n        public override JobList<SucceededJobDto> SucceededJobs(int @from, int count)\n        {\n            return UseConnection(connection => GetJobs(\n                connection,\n                from,\n                count,\n                SucceededState.StateName,\n                descending: true,\n                static (sqlJob, job, invocationData, loadException, stateData) => new SucceededJobDto\n                {\n                    Job = job,\n                    LoadException = loadException,\n                    InvocationData = invocationData,\n                    InSucceededState = SucceededState.StateName.Equals(sqlJob.StateName, StringComparison.OrdinalIgnoreCase),\n                    Result = stateData[\"Result\"],\n                    TotalDuration = stateData.TryGetValue(\"PerformanceDuration\", out var duration) && stateData.TryGetValue(\"Latency\", out var latency)\n                        ? (long?)long.Parse(duration, CultureInfo.InvariantCulture) + (long?)long.Parse(latency, CultureInfo.InvariantCulture)\n                        : null,\n                    SucceededAt = sqlJob.StateChanged,\n                    StateData = stateData\n                }));\n        }\n\n        public override JobList<DeletedJobDto> DeletedJobs(int @from, int count)\n        {\n            return UseConnection(connection => GetJobs(\n                connection,\n                from,\n                count,\n                DeletedState.StateName,\n                descending: true,\n                static (sqlJob, job, invocationData, loadException, stateData) => new DeletedJobDto\n                {\n                    Job = job,\n                    LoadException = loadException,\n                    InvocationData = invocationData,\n                    InDeletedState = DeletedState.StateName.Equals(sqlJob.StateName, StringComparison.OrdinalIgnoreCase),\n                    DeletedAt = sqlJob.StateChanged,\n                    StateData = stateData\n                }));\n        }\n\n        public override JobList<AwaitingJobDto> AwaitingJobs(int @from, int count)\n        {\n            var awaitingJobs = UseConnection(connection => GetJobs(\n                connection,\n                from,\n                count,\n                AwaitingState.StateName,\n                descending: false,\n                static (sqlJob, job, invocationData, loadException, stateData) => new AwaitingJobDto\n                {\n                    Job = job,\n                    LoadException = loadException,\n                    InvocationData = invocationData,\n                    InAwaitingState = AwaitingState.StateName.Equals(sqlJob.StateName, StringComparison.OrdinalIgnoreCase),\n                    AwaitingAt = sqlJob.StateChanged,\n                    StateData = stateData\n                }));\n\n            var parentIds = awaitingJobs\n                .Where(static x => x.Value != null && x.Value.InAwaitingState && x.Value.StateData.ContainsKey(\"ParentId\"))\n                .Select(static x => long.Parse(x.Value.StateData[\"ParentId\"], CultureInfo.InvariantCulture))\n                .ToArray();\n\n            var parentStates = UseConnection(connection =>\n            {\n                return connection.Query<ParentStateDto>(\n                    _storage.GetQueryFromTemplate(static schemaName =>\n                        $@\"select Id, StateName from [{schemaName}].Job with (nolock, forceseek) where Id in @ids\"),\n                    new { ids = parentIds },\n                    commandTimeout: _storage.CommandTimeout)\n                .ToDictionary(static x => x.Id, static x => x.StateName);\n            });\n\n            foreach (var awaitingJob in awaitingJobs)\n            {\n                if (awaitingJob.Value != null && awaitingJob.Value.InAwaitingState && awaitingJob.Value.StateData.TryGetValue(\"ParentId\", out var parentIdString))\n                {\n                    var parentId = long.Parse(parentIdString, CultureInfo.InvariantCulture);\n                    if (parentStates.TryGetValue(parentId, out var parentStateName))\n                    {\n                        awaitingJob.Value.ParentStateName = parentStateName;\n                    }\n                }\n            }\n\n            return awaitingJobs;\n        }\n\n        public override long AwaitingCount()\n        {\n            return UseConnection(connection => \n                GetNumberOfJobsByStateName(connection, AwaitingState.StateName));\n        }\n\n        public override IList<QueueWithTopEnqueuedJobsDto> Queues()\n        {\n            var tuples = _storage.QueueProviders\n                .Select(static x => x.GetJobQueueMonitoringApi())\n                .SelectMany(static x => x.GetQueues(), static (monitoring, queue) => new { Monitoring = monitoring, Queue = queue })\n                .OrderBy(static x => x.Queue)\n                .ToArray();\n\n            var result = new List<QueueWithTopEnqueuedJobsDto>(tuples.Length);\n\n            // ReSharper disable once LoopCanBeConvertedToQuery\n            foreach (var tuple in tuples)\n            {\n                var enqueuedJobIds = tuple.Monitoring.GetEnqueuedJobIds(tuple.Queue, 0, 5);\n                var counters = tuple.Monitoring.GetEnqueuedAndFetchedCount(tuple.Queue);\n\n                var firstJobs = UseConnection(connection => \n                    EnqueuedJobs(connection, enqueuedJobIds.ToArray()));\n\n                result.Add(new QueueWithTopEnqueuedJobsDto\n                {\n                    Name = tuple.Queue,\n                    Length = counters.EnqueuedCount ?? 0,\n                    Fetched = counters.FetchedCount,\n                    FirstJobs = firstJobs\n                });\n            }\n\n            return result;\n        }\n\n        public override JobList<EnqueuedJobDto> EnqueuedJobs(string queue, int from, int perPage)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var queueApi = GetQueueApi(queue);\n            var enqueuedJobIds = queueApi.GetEnqueuedJobIds(queue, from, perPage);\n\n            return UseConnection(connection => EnqueuedJobs(connection, enqueuedJobIds.ToArray()));\n        }\n\n        public override JobList<FetchedJobDto> FetchedJobs(string queue, int @from, int perPage)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            var queueApi = GetQueueApi(queue);\n            var fetchedJobIds = queueApi.GetFetchedJobIds(queue, from, perPage);\n\n            return UseConnection(connection => FetchedJobs(connection, fetchedJobIds.ToArray()));\n        }\n\n        public override IDictionary<DateTime, long> HourlySucceededJobs()\n        {\n            return UseConnection(connection => \n                GetHourlyTimelineStats(connection, \"succeeded\"));\n        }\n\n        public override IDictionary<DateTime, long> HourlyFailedJobs()\n        {\n            return UseConnection(connection => \n                GetHourlyTimelineStats(connection, \"failed\"));\n        }\n\n        public override IDictionary<DateTime, long> HourlyDeletedJobs()\n        {\n            return UseConnection(connection => \n                GetHourlyTimelineStats(connection, \"deleted\"));\n        }\n\n        public override JobDetailsDto JobDetails(string jobId)\n        {\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n\n            return UseConnection(connection =>\n            {\n                var query = _storage.GetQueryFromTemplate(static schemaName => $@\"\nselect * from [{schemaName}].Job with (nolock, forceseek) where Id = @id\nselect * from [{schemaName}].JobParameter with (nolock, forceseek) where JobId = @id\nselect * from [{schemaName}].State with (nolock, forceseek) where JobId = @id order by Id desc\");\n\n                using (var multi = connection.QueryMultiple(query, new { id = jobId }, commandTimeout: _storage.CommandTimeout))\n                {\n                    var job = multi.ReadSingleOrDefault<SqlJob>();\n                    if (job == null) return null;\n\n                    var parameters = multi.Read<JobParameter>()\n                        .GroupBy(static x => x.Name)\n                        .Select(static grp => grp.First())\n                        .ToDictionary(static x => x.Name, static x => x.Value);\n\n                    var deserializedJob = DeserializeJob(job.InvocationData, job.Arguments, out var payload, out var exception);\n\n                    if (deserializedJob == null)\n                    {\n                        if (payload != null)\n                        {\n                            parameters.Add(\"DBG_Type\", payload.Type);\n                            parameters.Add(\"DBG_Method\", payload.Method);\n                            parameters.Add(\"DBG_Args\", payload.Arguments);\n                        }\n                        else\n                        {\n                            parameters.Add(\"DBG_Payload\", job.InvocationData);\n                            parameters.Add(\"DBG_Args\", job.Arguments);\n                        }\n                    }\n\n                    if (exception != null)\n                    {\n                        parameters.Add(\"DBG_Exception\", (exception.InnerException ?? exception).Message);\n                    }\n\n                    var history =\n                        multi.Read<SqlState>()\n                            .ToList()\n                            .Select(static x => new StateHistoryDto\n                            {\n                                StateName = x.Name,\n                                CreatedAt = x.CreatedAt,\n                                Reason = x.Reason,\n                                Data = new SafeDictionary<string, string>(\n                                    SerializationHelper.Deserialize<Dictionary<string, string>>(x.Data),\n                                    StringComparer.OrdinalIgnoreCase),\n                            })\n                            .ToList();\n\n                    return new JobDetailsDto\n                    {\n                        CreatedAt = job.CreatedAt,\n                        ExpireAt = job.ExpireAt,\n                        Job = deserializedJob,\n                        InvocationData = payload,\n                        LoadException = exception,\n                        History = history,\n                        Properties = parameters\n                    };\n                }\n            });\n        }\n\n        public override long SucceededListCount()\n        {\n            return UseConnection(connection => \n                GetNumberOfJobsByStateName(connection, SucceededState.StateName));\n        }\n\n        public override long DeletedListCount()\n        {\n            return UseConnection(connection => \n                GetNumberOfJobsByStateName(connection, DeletedState.StateName));\n        }\n\n        public override StatisticsDto GetStatistics()\n        {\n            var query = _storage.GetQueryFromTemplate(static schemaName => $@\"\nset transaction isolation level read committed;\nselect count(Id) from [{schemaName}].Job with (nolock, forceseek) where StateName = N'Enqueued';\nselect count(Id) from [{schemaName}].Job with (nolock, forceseek) where StateName = N'Failed';\nselect count(Id) from [{schemaName}].Job with (nolock, forceseek) where StateName = N'Processing';\nselect count(Id) from [{schemaName}].Job with (nolock, forceseek) where StateName = N'Scheduled';\nselect count(Id) from [{schemaName}].Job with (nolock, forceseek) where StateName = N'Awaiting';\nselect count(Id) from [{schemaName}].Server with (nolock);\nselect sum(s.[Value]) from (\n    select sum([Value]) as [Value] from [{schemaName}].Counter with (nolock, forceseek) where [Key] = N'stats:succeeded'\n    union all\n    select [Value] from [{schemaName}].AggregatedCounter with (nolock, forceseek) where [Key] = N'stats:succeeded'\n) as s;\nselect sum(s.[Value]) from (\n    select sum([Value]) as [Value] from [{schemaName}].Counter with (nolock, forceseek) where [Key] = N'stats:deleted'\n    union all\n    select [Value] from [{schemaName}].AggregatedCounter with (nolock, forceseek) where [Key] = N'stats:deleted'\n) as s;\n\nselect count(*) from [{schemaName}].[Set] with (nolock, forceseek) where [Key] = N'recurring-jobs';\nselect count(*) from [{schemaName}].[Set] with (nolock, forceseek) where [Key] = N'retries';\n                \");\n\n            var statistics = UseConnection(connection =>\n            {\n                var stats = new StatisticsDto();\n                using (var multi = connection.QueryMultiple(query, commandTimeout: _storage.CommandTimeout))\n                {\n                    stats.Enqueued = multi.ReadSingle<int>();\n                    stats.Failed = multi.ReadSingle<int>();\n                    stats.Processing = multi.ReadSingle<int>();\n                    stats.Scheduled = multi.ReadSingle<int>();\n                    stats.Awaiting = multi.ReadSingle<int>();\n\n                    stats.Servers = multi.ReadSingle<int>();\n\n                    stats.Succeeded = multi.ReadSingleOrDefault<long?>() ?? 0;\n                    stats.Deleted = multi.ReadSingleOrDefault<long?>() ?? 0;\n\n                    stats.Recurring = multi.ReadSingle<int>();\n                    stats.Retries = multi.ReadSingle<int>();\n                }\n                return stats;\n            });\n\n            statistics.Queues = _storage.QueueProviders\n                .SelectMany(static x => x.GetJobQueueMonitoringApi().GetQueues())\n                .Count();\n\n            return statistics;\n        }\n\n        private Dictionary<DateTime, long> GetHourlyTimelineStats(DbConnection connection, string type)\n        {\n            var endDate = DateTime.UtcNow;\n            var dates = new List<DateTime>();\n            for (var i = 0; i < 24; i++)\n            {\n                dates.Add(endDate);\n                endDate = endDate.AddHours(-1);\n            }\n\n            var keyMaps = dates.ToDictionary(x => $\"stats:{type}:{x.ToString(\"yyyy-MM-dd-HH\", CultureInfo.InvariantCulture)}\", static x => x);\n\n            return GetTimelineStats(connection, keyMaps);\n        }\n\n        private Dictionary<DateTime, long> GetTimelineStats(DbConnection connection, string type)\n        {\n            var endDate = DateTime.UtcNow.Date;\n            var dates = new List<DateTime>();\n            for (var i = 0; i < 7; i++)\n            {\n                dates.Add(endDate);\n                endDate = endDate.AddDays(-1);\n            }\n\n            var keyMaps = dates.ToDictionary(x => $\"stats:{type}:{x.ToString(\"yyyy-MM-dd\", CultureInfo.InvariantCulture)}\", static x => x);\n\n            return GetTimelineStats(connection, keyMaps);\n        }\n\n        private Dictionary<DateTime, long> GetTimelineStats(\n            DbConnection connection,\n            IDictionary<string, DateTime> keyMaps)\n        {\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"select [Key], [Value] as [Count] from [{schemaName}].AggregatedCounter with (nolock, forceseek)\nwhere [Key] in @keys\");\n\n            var valuesMap = connection.Query(\n                query,\n                new { keys = keyMaps.Keys },\n                commandTimeout: _storage.CommandTimeout)\n                .ToDictionary(static x => (string)x.Key, static x => (long)x.Count);\n\n            foreach (var key in keyMaps.Keys)\n            {\n                if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0);\n            }\n\n            var result = new Dictionary<DateTime, long>();\n            for (var i = 0; i < keyMaps.Count; i++)\n            {\n                var value = valuesMap[keyMaps.ElementAt(i).Key];\n                result.Add(keyMaps.ElementAt(i).Value, value);\n            }\n\n            return result;\n        }\n\n        private IPersistentJobQueueMonitoringApi GetQueueApi(string queueName)\n        {\n            var provider = _storage.QueueProviders.GetProvider(queueName);\n            var monitoringApi = provider.GetJobQueueMonitoringApi();\n\n            return monitoringApi;\n        }\n\n        private T UseConnection<T>(Func<DbConnection, T> action)\n        {\n            return _storage.UseConnection(null, static (_, connection, action) => action(connection), action);\n        }\n\n        private JobList<EnqueuedJobDto> EnqueuedJobs(DbConnection connection, long[] jobIds)\n        {\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"select j.*, s.Reason as StateReason, s.Data as StateData, s.CreatedAt as StateChanged\nfrom [{schemaName}].Job j with (nolock, forceseek)\nleft join [{schemaName}].State s with (nolock, forceseek) on s.Id = j.StateId and s.JobId = j.Id\nwhere j.Id in @jobIds\");\n\n            var jobs = connection.Query<SqlJob>(\n                query,\n                new { jobIds = jobIds },\n                commandTimeout: _storage.CommandTimeout)\n                .ToDictionary(static x => x.Id, static x => x);\n\n            var sortedSqlJobs = jobIds\n                .Select(jobId => jobs.TryGetValue(jobId, out var job) ? job : new SqlJob { Id = jobId })\n                .ToList();\n            \n            return DeserializeJobs(\n                sortedSqlJobs,\n                static (sqlJob, job, invocationData, loadException, stateData) => new EnqueuedJobDto\n                {\n                    Job = job,\n                    LoadException = loadException,\n                    InvocationData = invocationData,\n                    State = sqlJob.StateName,\n                    InEnqueuedState = EnqueuedState.StateName.Equals(sqlJob.StateName, StringComparison.OrdinalIgnoreCase),\n                    EnqueuedAt = EnqueuedState.StateName.Equals(sqlJob.StateName, StringComparison.OrdinalIgnoreCase)\n                        ? sqlJob.StateChanged\n                        : null,\n                    StateData = stateData\n                });\n        }\n\n        private long GetNumberOfJobsByStateName(DbConnection connection, string stateName)\n        {\n            var query = _storage.GetQueryFromTemplate(_jobListLimit.HasValue\n                ? static schemaName => $@\"select count(j.Id) from (select top (@limit) Id from [{schemaName}].Job with (nolock, forceseek) where StateName = @state) as j\"\n                : static schemaName => $@\"select count(Id) from [{schemaName}].Job with (nolock, forceseek) where StateName = @state\");\n\n            var count = connection.ExecuteScalar<int>(\n                 query,\n                 new { state = stateName, limit = _jobListLimit },\n                 commandTimeout: _storage.CommandTimeout);\n\n            return count;\n        }\n\n        private static Job DeserializeJob(string invocationData, string arguments, out InvocationData data, out JobLoadException exception)\n        {\n            data = InvocationData.DeserializePayload(invocationData);\n\n            if (!String.IsNullOrEmpty(arguments))\n            {\n                data.Arguments = arguments;\n            }\n\n            try\n            {\n                exception = null;\n                return data.DeserializeJob();\n            }\n            catch (JobLoadException ex)\n            {\n                exception = ex;\n                return null;\n            }\n        }\n\n        private JobList<TDto> GetJobs<TDto>(\n            DbConnection connection,\n            int from,\n            int count,\n            string stateName,\n            bool descending,\n            Func<SqlJob, Job, InvocationData, JobLoadException, SafeDictionary<string, string>, TDto> selector)\n        {\n            var order = descending ? \"desc\" : \"asc\";\n            var query = String.Format(CultureInfo.InvariantCulture, _storage.GetQueryFromTemplate(static schemaName =>\n$@\";with cte as \n(\n  select j.Id, row_number() over (order by j.Id {{0}}) as row_num\n  from [{schemaName}].Job j with (nolock, forceseek)\n  where j.StateName = @stateName\n)\nselect j.*, s.Reason as StateReason, s.Data as StateData, s.CreatedAt as StateChanged\nfrom [{schemaName}].Job j with (nolock, forceseek)\ninner join cte on cte.Id = j.Id\nleft join [{schemaName}].State s with (nolock, forceseek) on j.StateId = s.Id and j.Id = s.JobId\nwhere cte.row_num between @start and @end\"), order);\n\n            var jobs = connection.Query<SqlJob>(\n                        query,\n                        new { stateName = stateName, start = @from + 1, end = @from + count },\n                        commandTimeout: _storage.CommandTimeout)\n                        .ToList();\n\n            return DeserializeJobs(jobs, selector);\n        }\n\n        private static JobList<TDto> DeserializeJobs<TDto>(\n            ICollection<SqlJob> jobs,\n            Func<SqlJob, Job, InvocationData, JobLoadException, SafeDictionary<string, string>, TDto> selector)\n        {\n            var result = new List<KeyValuePair<string, TDto>>(jobs.Count);\n            \n            // ReSharper disable once LoopCanBeConvertedToQuery\n            foreach (var job in jobs)\n            {\n                var dto = default(TDto);\n                \n                if (job.InvocationData != null)\n                {\n                    var deserializedData = SerializationHelper.Deserialize<Dictionary<string, string>>(job.StateData);\n                    var stateData = deserializedData != null\n                        ? new SafeDictionary<string, string>(deserializedData, StringComparer.OrdinalIgnoreCase)\n                        : null;\n\n                    dto = selector(job, DeserializeJob(job.InvocationData, job.Arguments, out var invocationData, out var loadException), invocationData, loadException, stateData);\n                }\n\n                result.Add(new KeyValuePair<string, TDto>(\n                    job.Id.ToString(CultureInfo.InvariantCulture), dto));\n            }\n\n            return new JobList<TDto>(result);\n        }\n\n        private JobList<FetchedJobDto> FetchedJobs(DbConnection connection, IEnumerable<long> jobIds)\n        { \n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"select j.*, s.Reason as StateReason, s.Data as StateData \nfrom [{schemaName}].Job j with (nolock, forceseek)\nleft join [{schemaName}].State s with (nolock, forceseek) on s.Id = j.StateId and s.JobId = j.Id\nwhere j.Id in @jobIds\");\n\n            var jobs = connection.Query<SqlJob>(\n                query,\n                new { jobIds = jobIds },\n                commandTimeout: _storage.CommandTimeout)\n                .ToList();\n\n            var result = new List<KeyValuePair<string, FetchedJobDto>>(jobs.Count);\n\n            // ReSharper disable once LoopCanBeConvertedToQuery\n            foreach (var job in jobs)\n            {\n                result.Add(new KeyValuePair<string, FetchedJobDto>(\n                    job.Id.ToString(CultureInfo.InvariantCulture),\n                    new FetchedJobDto\n                    {\n                        Job = DeserializeJob(job.InvocationData, job.Arguments, out _, out _),\n                        State = job.StateName,\n                    }));\n            }\n\n            return new JobList<FetchedJobDto>(result);\n        }\n\n        /// <summary>\n        /// Overloaded dictionary that doesn't throw if given an invalid key\n        /// Fixes issues such as https://github.com/HangfireIO/Hangfire/issues/871\n        /// </summary>\n        private sealed class SafeDictionary<TKey, TValue> : Dictionary<TKey, TValue>\n        {\n            public SafeDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) \n                : base(dictionary, comparer)\n            {\n            }\n\n            public new TValue this[TKey i]\n            {\n                get => TryGetValue(i, out var value) ? value : default(TValue);\n                set => base[i] = value;\n            }\n        }\n\n        private sealed class ParentStateDto\n        {\n            public long Id { get; set; }\n            public string StateName { get; set; }\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerObjectsInstaller.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Data.Common;\nusing System.IO;\nusing System.Reflection;\nusing Dapper;\nusing Hangfire.Logging;\n\nnamespace Hangfire.SqlServer\n{\n    public static class SqlServerObjectsInstaller\n    {\n        [Obsolete(\"This field is unused and will be removed in 2.0.0.\")]\n        public static readonly int RequiredSchemaVersion = 5;\n\n        public static readonly int LatestSchemaVersion = 9;\n\n        public static void Install(DbConnection connection)\n        {\n            Install(connection, null);\n        }\n\n        public static void Install(DbConnection connection, string schema)\n        {\n            Install(connection, schema, false);\n        }\n\n        public static void Install(DbConnection connection, string schema, bool enableHeavyMigrations)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n\n            var script = GetInstallScript(schema, enableHeavyMigrations);\n\n            connection.Execute(script, commandTimeout: 0);\n        }\n\n        public static string GetInstallScript(string schema, bool enableHeavyMigrations)\n        {\n            var script = GetStringResource(\n                typeof(SqlServerObjectsInstaller).GetTypeInfo().Assembly,\n                \"Hangfire.SqlServer.Install.sql\");\n\n            script = script.Replace(\"$(HangFireSchema)\", !string.IsNullOrWhiteSpace(schema) ? schema : Constants.DefaultSchema);\n\n            if (!enableHeavyMigrations)\n            {\n                script = script.Replace(\"--SET @DISABLE_HEAVY_MIGRATIONS = 1;\", \"SET @DISABLE_HEAVY_MIGRATIONS = 1;\");\n            }\n\n            return script;\n        }\n\n        private static string GetStringResource(Assembly assembly, string resourceName)\n        {\n            using (var stream = assembly.GetManifestResourceStream(resourceName))\n            {\n                if (stream == null) \n                {\n                    throw new InvalidOperationException(\n                        $\"Requested resource `{resourceName}` was not found in the assembly `{assembly}`.\");\n                }\n\n                using (var reader = new StreamReader(stream))\n                {\n                    return reader.ReadToEnd();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerStorage.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\n#if FEATURE_CONFIGURATIONMANAGER\nusing System.Configuration;\n#endif\n#if FEATURE_TRANSACTIONSCOPE\nusing System.Transactions;\nusing IsolationLevel = System.Transactions.IsolationLevel;\n#endif\nusing Dapper;\nusing Hangfire.Annotations;\nusing Hangfire.Dashboard;\nusing Hangfire.Logging;\nusing Hangfire.Server;\nusing Hangfire.Storage;\n\nnamespace Hangfire.SqlServer\n{\n    public class SqlServerStorage : JobStorage\n    {\n        private static readonly char[] SemicolonSeparator = new[] { ';' };\n        private static readonly char[] EqualSignSeparator = new[] { '=' };\n\n        private readonly ConcurrentDictionary<KeyValuePair<Func<string, string>, string>, string> _queryTemplateCache = new(new QueryTemplateKeyEqualityComparer());\n\n        private readonly DbConnection _existingConnection;\n        private readonly Func<DbConnection> _connectionFactory;\n        private readonly SqlServerStorageOptions _options;\n        private readonly string _connectionString;\n        private string _escapedSchemaName;\n        private SqlServerHeartbeatProcess _heartbeatProcess;\n\n        private readonly Dictionary<string, bool> _features =\n            new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase)\n            {\n                { JobStorageFeatures.ExtendedApi, true },\n                { JobStorageFeatures.JobQueueProperty, true },\n                { JobStorageFeatures.ProcessesInsteadOfComponents, true },\n                { JobStorageFeatures.Connection.BatchedGetFirstByLowest, true },\n                { JobStorageFeatures.Connection.GetUtcDateTime, true },\n                { JobStorageFeatures.Connection.GetSetContains, true },\n                { JobStorageFeatures.Connection.LimitedGetSetCount, true },\n                { JobStorageFeatures.Transaction.AcquireDistributedLock, true },\n                { JobStorageFeatures.Transaction.CreateJob, false },\n                { JobStorageFeatures.Transaction.SetJobParameter, false },\n                { JobStorageFeatures.Monitoring.DeletedStateGraphs, true },\n                { JobStorageFeatures.Monitoring.AwaitingJobs, true }\n            };\n\n        public SqlServerStorage(string nameOrConnectionString)\n            : this(nameOrConnectionString, new SqlServerStorageOptions())\n        {\n        }\n\n        /// <summary>\n        /// Initializes SqlServerStorage from the provided SqlServerStorageOptions and either the provided connection\n        /// string or the connection string with provided name pulled from the application config file.       \n        /// </summary>\n        /// <param name=\"nameOrConnectionString\">Either a SQL Server connection string or the name of \n        /// a SQL Server connection string located in the connectionStrings node in the application config</param>\n        /// <param name=\"options\"></param>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"nameOrConnectionString\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"options\"/> argument is null.</exception>\n        /// <exception cref=\"ArgumentException\"><paramref name=\"nameOrConnectionString\"/> argument is neither \n        /// a valid SQL Server connection string nor the name of a connection string in the application\n        /// config file.</exception>\n        public SqlServerStorage(string nameOrConnectionString, SqlServerStorageOptions options)\n        {\n            if (nameOrConnectionString == null) throw new ArgumentNullException(nameof(nameOrConnectionString));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            _connectionString = GetConnectionString(nameOrConnectionString);\n            _connectionFactory = DefaultConnectionFactory;\n            _options = options;\n\n            Initialize();\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"SqlServerStorage\"/> class with\n        /// explicit instance of the <see cref=\"DbConnection\"/> class that will be used\n        /// to query the data.\n        /// </summary>\n        public SqlServerStorage([NotNull] DbConnection existingConnection)\n            : this(existingConnection, new SqlServerStorageOptions())\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"SqlServerStorage\"/> class with\n        /// explicit instance of the <see cref=\"DbConnection\"/> class that will be used\n        /// to query the data, with the given options.\n        /// </summary>\n        public SqlServerStorage([NotNull] DbConnection existingConnection, [NotNull] SqlServerStorageOptions options)\n        {\n            if (existingConnection == null) throw new ArgumentNullException(nameof(existingConnection));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            _existingConnection = existingConnection;\n            _options = options;\n\n            Initialize();\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"SqlServerStorage\"/> class with\n        /// a connection factory <see cref=\"Func{DbConnection}\"/> class that will be invoked\n        /// to create new database connections for querying the data.\n        /// </summary>\n        public SqlServerStorage([NotNull] Func<DbConnection> connectionFactory)\n            : this(connectionFactory, new SqlServerStorageOptions())\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"SqlServerStorage\"/> class with\n        /// a connection factory <see cref=\"Func{DbConnection}\"/> class that will be invoked\n        /// to create new database connections for querying the data.\n        /// </summary>\n        public SqlServerStorage([NotNull] Func<DbConnection> connectionFactory, [NotNull] SqlServerStorageOptions options)\n        {\n            if (connectionFactory == null) throw new ArgumentNullException(nameof(connectionFactory));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            _connectionFactory = connectionFactory;\n            _options = options;\n\n            Initialize();\n        }\n\n        public virtual PersistentJobQueueProviderCollection QueueProviders { get; private set; }\n\n        public override bool LinearizableReads => true;\n\n        internal string SchemaName => _escapedSchemaName;\n        internal int? CommandTimeout => _options.CommandTimeout.HasValue ? (int)_options.CommandTimeout.Value.TotalSeconds : (int?)null;\n        internal int? CommandBatchMaxTimeout => _options.CommandBatchMaxTimeout.HasValue ? (int)_options.CommandBatchMaxTimeout.Value.TotalSeconds : (int?)null;\n        internal TimeSpan? SlidingInvisibilityTimeout => _options.SlidingInvisibilityTimeout;\n        internal SqlServerStorageOptions Options => _options;\n        internal SqlServerHeartbeatProcess HeartbeatProcess => _heartbeatProcess;\n\n        public override IMonitoringApi GetMonitoringApi()\n        {\n            return new SqlServerMonitoringApi(this, _options.DashboardJobListLimit);\n        }\n\n        public override IStorageConnection GetConnection()\n        {\n            return new SqlServerConnection(this);\n        }\n\n#pragma warning disable 618\n        [Obsolete($\"Please use the `{nameof(GetStorageWideProcesses)}` and/or `{nameof(GetServerRequiredProcesses)}` methods instead, and enable `{nameof(JobStorageFeatures)}.{nameof(JobStorageFeatures.ProcessesInsteadOfComponents)}`. Will be removed in 2.0.0.\")]\n        public override IEnumerable<IServerComponent> GetComponents()\n#pragma warning restore 618\n        {\n            yield return new ExpirationManager(this, _options.InactiveStateExpirationTimeout, _options.JobExpirationCheckInterval);\n            yield return new CountersAggregator(this, _options.CountersAggregateInterval);\n            yield return _heartbeatProcess;\n        }\n\n        public override IEnumerable<IBackgroundProcess> GetServerRequiredProcesses()\n        {\n            yield return _heartbeatProcess;\n        }\n\n        public override IEnumerable<IBackgroundProcess> GetStorageWideProcesses()\n        {\n            yield return new ExpirationManager(this, _options.InactiveStateExpirationTimeout, _options.JobExpirationCheckInterval);\n            yield return new CountersAggregator(this, _options.CountersAggregateInterval);\n        }\n\n        public override void WriteOptionsToLog(ILog logger)\n        {\n            logger.Info($\"Using the following options for SQL Server job storage: Queue poll interval: {_options.QueuePollInterval}.\");\n        }\n\n        public override bool HasFeature(string featureId)\n        {\n            if (featureId == null) throw new ArgumentNullException(nameof(featureId));\n\n            return _features.TryGetValue(featureId, out var isSupported) \n                ? isSupported\n                : base.HasFeature(featureId);\n        }\n\n        public override string ToString()\n        {\n            const string canNotParseMessage = \"<Connection string can not be parsed>\";\n\n            try\n            {\n                if (_connectionString == null)\n                {\n                    return \"SQL Server (custom)\";\n                }\n\n                var parts = _connectionString.Split(SemicolonSeparator, StringSplitOptions.RemoveEmptyEntries)\n                    .Select(static x => x.Split(EqualSignSeparator, StringSplitOptions.RemoveEmptyEntries))\n                    .Select(static x => new { Key = x[0].Trim(), Value = x[1].Trim() })\n                    .GroupBy(static x => x.Key, StringComparer.OrdinalIgnoreCase)\n                    .ToDictionary(static x => x.Key, static x => x.Last().Value, StringComparer.OrdinalIgnoreCase);\n\n                var builder = new StringBuilder();\n\n                foreach (var alias in new[] { \"Data Source\", \"Server\", \"Address\", \"Addr\", \"Network Address\" })\n                {\n                    if (parts.TryGetValue(alias, out var part))\n                    {\n                        builder.Append(part);\n                        break;\n                    }\n                }\n\n                if (builder.Length != 0) builder.Append('@');\n\n                foreach (var alias in new[] { \"Database\", \"Initial Catalog\" })\n                {\n                    if (parts.TryGetValue(alias, out var part))\n                    {\n                        builder.Append(part);\n                        break;\n                    }\n                }\n\n                return builder.Length != 0\n                    ? $\"SQL Server: {builder}\"\n                    : canNotParseMessage;\n            }\n            catch (Exception ex) when (ex.IsCatchableExceptionType())\n            {\n                return canNotParseMessage;\n            }\n        }\n\n        internal string GetQueryFromTemplate(Func<string, string> templateFunc)\n        {\n            return _queryTemplateCache.GetOrAdd(\n                new KeyValuePair<Func<string, string>, string>(templateFunc, SchemaName),\n                static pair => pair.Key(pair.Value));\n        }\n\n        internal void UseConnection(\n            DbConnection dedicatedConnection,\n            [InstantHandle] Action<SqlServerStorage, DbConnection> action)\n        {\n            UseConnection(dedicatedConnection, static (storage, connection, ctx) =>\n            {\n                ctx(storage, connection);\n                return true;\n            }, action);\n        }\n\n        internal TResult UseConnection<TResult>(\n            DbConnection dedicatedConnection,\n            [InstantHandle] Func<SqlServerStorage, DbConnection, TResult> action)\n        {\n            return UseConnection(dedicatedConnection, static (storage, connection, ctx) => ctx(storage, connection), action);\n        }\n\n        internal TResult UseConnection<TContext, TResult>(\n            DbConnection dedicatedConnection,\n            [InstantHandle] Func<SqlServerStorage, DbConnection, TContext, TResult> func,\n            TContext context)\n        {\n            DbConnection connection = null;\n\n            try\n            {\n                connection = dedicatedConnection ?? CreateAndOpenConnection();\n                return func(this, connection, context);\n            }\n            finally\n            {\n                if (dedicatedConnection == null)\n                {\n                    ReleaseConnection(connection);\n                }\n            }\n        }\n\n        internal void UseTransaction<TContext>(\n            DbConnection dedicatedConnection,\n            [InstantHandle] Action<SqlServerStorage, DbConnection, DbTransaction, TContext> action,\n            TContext context)\n        {\n            UseTransaction(dedicatedConnection, static (storage, connection, transaction, ctx) =>\n            {\n                ctx.Key(storage, connection, transaction, ctx.Value);\n                return true;\n            }, new KeyValuePair<Action<SqlServerStorage, DbConnection, DbTransaction, TContext>, TContext>(action, context), null);\n        }\n        \n        internal TResult UseTransaction<TContext, TResult>(\n            DbConnection dedicatedConnection,\n            [InstantHandle] Func<SqlServerStorage, DbConnection, DbTransaction, TContext, TResult> func,\n            TContext context,\n            IsolationLevel? isolationLevel)\n        {\n            isolationLevel = isolationLevel ?? (_options.UseRecommendedIsolationLevel\n                ? IsolationLevel.ReadCommitted\n#pragma warning disable 618\n                : _options.TransactionIsolationLevel);\n#pragma warning restore 618\n\n#if FEATURE_TRANSACTIONSCOPE\n            if (IsRunningOnWindows() && !_options.DisableTransactionScope)\n            {\n                using (var transaction = CreateTransaction(isolationLevel))\n                {\n                    var result = UseConnection(dedicatedConnection, static (storage, connection, ctx) =>\n                    {\n                        connection.EnlistTransaction(Transaction.Current);\n                        return ctx.Key(storage, connection, null, ctx.Value);\n                    }, new KeyValuePair<Func<SqlServerStorage, DbConnection, DbTransaction, TContext, TResult>, TContext>(func, context));\n\n                    transaction.Complete();\n\n                    return result;\n                }\n            }\n            else\n#endif\n            {\n                return UseConnection(dedicatedConnection, static (storage, connection, ctx) =>\n                {\n                    using (var transaction = connection.BeginTransaction(\n#if !FEATURE_TRANSACTIONSCOPE\n                        ctx.Value.Value ??\n#endif\n                        System.Data.IsolationLevel.ReadCommitted))\n                    {\n                        TResult result;\n\n                        try\n                        {\n                            result = ctx.Key(storage, connection, transaction, ctx.Value.Key);\n                            transaction.Commit();\n                        }\n                        catch (Exception ex) when (ex.IsCatchableExceptionType())\n                        {\n                            // It is possible that XACT_ABORT option is set, and in this\n                            // case transaction will be aborted automatically on server.\n                            // Some older SqlClient implementations throw InvalidOperationException\n                            // when trying to rollback such an aborted transaction, so we\n                            // try to handle this case.\n                            //\n                            // It's also possible that our connection is broken, so this\n                            // check is useful even when XACT_ABORT option wasn't set.\n                            if (transaction.Connection != null)\n                            {\n                                // Don't rely on implicit rollback when calling the Dispose\n                                // method, because some implementations may throw the\n                                // NullReferenceException, although it's prohibited to throw\n                                // any exception from a Dispose method, according to the\n                                // .NET Framework Design Guidelines:\n                                // https://github.com/dotnet/efcore/issues/12864\n                                // https://github.com/HangfireIO/Hangfire/issues/1494\n                                transaction.Rollback();\n                            }\n\n                            throw;\n                        }\n\n                        return result;\n                    }\n                }, new KeyValuePair<Func<SqlServerStorage, DbConnection, DbTransaction, TContext, TResult>, KeyValuePair<TContext, IsolationLevel?>>(\n                    func,\n                    new KeyValuePair<TContext, IsolationLevel?>(context, isolationLevel)));\n            }\n        }\n\n        internal DbConnection CreateAndOpenConnection()\n        {\n            using (_options.ImpersonationFunc?.Invoke())\n            {\n                DbConnection connection = null;\n\n                try\n                {\n                    connection = _existingConnection ?? _connectionFactory();\n\n                    if (connection.State == ConnectionState.Closed)\n                    {\n                        connection.Open();\n                    }\n\n                    return connection;\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    ReleaseConnection(connection);\n                    throw;\n                }\n            }\n        }\n\n        internal bool IsExistingConnection(IDbConnection connection)\n        {\n            return connection != null && ReferenceEquals(connection, _existingConnection);\n        }\n\n        internal void ReleaseConnection(IDbConnection connection)\n        {\n            if (connection != null && !IsExistingConnection(connection))\n            {\n                connection.Dispose();\n            }\n        }\n        \n        private DbConnection DefaultConnectionFactory()\n        {\n            var connection = _options.SqlClientFactory.CreateConnection() ?? throw new InvalidOperationException($\"The provider factory ({_options.SqlClientFactory}) returned a null DbConnection.\");\n            connection.ConnectionString = _connectionString;\n            return connection;\n        }\n\n        private static bool IsRunningOnWindows()\n        {\n#if !NETSTANDARD1_3\n            return Environment.OSVersion.Platform == PlatformID.Win32NT;\n#else\n            return System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows);\n#endif\n        }\n\n        private void Initialize()\n        {\n            _escapedSchemaName = _options.SchemaName.Replace(\"]\", \"]]\");\n\n            if (_options.PrepareSchemaIfNecessary)\n            {\n                var log = LogProvider.GetLogger(typeof(SqlServerObjectsInstaller));\n                const int RetryAttempts = 3;\n\n                log.Info(\"Start installing Hangfire SQL objects...\");\n\n                Exception lastException = null;\n\n                for (var i = 0; i < RetryAttempts; i++)\n                {\n                    try\n                    {\n                        UseConnection(null, static (storage, connection) =>\n                        {\n                            // TODO: Escape schema here???\n                            SqlServerObjectsInstaller.Install(connection, storage.Options.SchemaName, storage.Options.EnableHeavyMigrations);\n                        });\n\n                        lastException = null;\n                        break;\n                    }\n                    catch (DbException ex)\n                    {\n                        lastException = ex;\n                        log.WarnException(\"An exception occurred while trying to perform the migration.\" + (i < RetryAttempts - 1 ? \" Retrying...\" : \"\"), ex);\n                    }\n                }\n\n                if (lastException != null)\n                {\n                    log.WarnException(\"Was unable to perform the Hangfire schema migration due to an exception. Ignore this message unless you've just installed or upgraded Hangfire.\", lastException);\n                }\n                else\n                {\n                    log.Info(\"Hangfire SQL objects installed.\");\n                }\n            }\n\n            if (_options.TryAutoDetectSchemaDependentOptions)\n            {\n                try\n                {\n                    int? schema = UseConnection(null, static (storage, connection) =>\n                        connection.ExecuteScalar<int>($\"select top (1) [Version] from [{storage.SchemaName}].[Schema]\"));\n\n                    _options.UseRecommendedIsolationLevel = true;\n                    _options.UseIgnoreDupKeyOption = schema >= 8;\n                    _options.DisableGlobalLocks = schema >= 6;\n\n                    if (schema >= 6 && _options.DeleteExpiredBatchSize == -1)\n                    {\n                        _options.DeleteExpiredBatchSize = 10000;\n                    }\n                }\n                catch (Exception ex)\n                {\n                    var log = LogProvider.GetLogger(typeof(SqlServerStorage));\n                    log.ErrorException(\"Was unable to use the TryAutoDetectSchemaDependentOptions option due to an exception.\", ex);\n                }\n            }\n\n            InitializeQueueProviders();\n            _heartbeatProcess = new SqlServerHeartbeatProcess();\n\n            _features.Add(\n                JobStorageFeatures.Transaction.RemoveFromQueue(typeof(SqlServerTimeoutJob)),\n                _options.UseTransactionalAcknowledge);\n        }\n\n        private void InitializeQueueProviders()\n        {\n            var defaultQueueProvider = _options.DefaultQueueProvider ?? new SqlServerJobQueueProvider(this, _options);\n            QueueProviders = new PersistentJobQueueProviderCollection(defaultQueueProvider);\n        }\n\n        private static string GetConnectionString(string nameOrConnectionString)\n        {\n#if FEATURE_CONFIGURATIONMANAGER\n            if (IsConnectionString(nameOrConnectionString))\n            {\n                return nameOrConnectionString;\n            }\n\n            if (IsConnectionStringInConfiguration(nameOrConnectionString))\n            {\n                return ConfigurationManager.ConnectionStrings[nameOrConnectionString].ConnectionString;\n            }\n\n            throw new ArgumentException(\n                $\"Could not find connection string with name '{nameOrConnectionString}' in application config file\");\n#else\n            return nameOrConnectionString;\n#endif\n        }\n\n#if FEATURE_CONFIGURATIONMANAGER\n        private static bool IsConnectionString(string nameOrConnectionString)\n        {\n            return nameOrConnectionString.Contains(\";\");\n        }\n\n        private static bool IsConnectionStringInConfiguration(string connectionStringName)\n        {\n            var connectionStringSetting = ConfigurationManager.ConnectionStrings[connectionStringName];\n\n            return connectionStringSetting != null;\n        }\n#endif\n\n#if FEATURE_TRANSACTIONSCOPE\n        private TransactionScope CreateTransaction(IsolationLevel? isolationLevel)\n        {\n            return isolationLevel != null\n                ? new TransactionScope(TransactionScopeOption.Required,\n                    new TransactionOptions { IsolationLevel = isolationLevel.Value, Timeout = _options.TransactionTimeout })\n                : new TransactionScope();\n        }\n#endif\n\n        public static readonly DashboardMetric ActiveConnections = new DashboardMetric(\n            \"connections:active\",\n            \"Metrics_ActiveConnections\",\n            static page =>\n            {\n                var sqlStorage = page.Storage as SqlServerStorage;\n                if (sqlStorage == null) return new Metric(\"???\");\n\n                return sqlStorage.UseConnection(null, static (_, connection) =>\n                {\n                    var sqlQuery = @\"\nselect count(*) from sys.sysprocesses\nwhere dbid = db_id(@name) and status != 'background' and status != 'sleeping'\";\n\n                    var value = connection\n                        .QuerySingle<int>(sqlQuery, new { name = connection.Database });\n\n                    return new Metric(value);\n                });\n            });\n\n        public static readonly DashboardMetric TotalConnections = new DashboardMetric(\n            \"connections:total\",\n            \"Metrics_TotalConnections\",\n            static page =>\n            {\n                var sqlStorage = page.Storage as SqlServerStorage;\n                if (sqlStorage == null) return new Metric(\"???\");\n\n                return sqlStorage.UseConnection(null, static (_, connection) =>\n                {\n                    var sqlQuery = @\"\nselect count(*) from sys.sysprocesses\nwhere dbid = db_id(@name) and status != 'background'\";\n\n                    var value = connection\n                        .QuerySingle<int>(sqlQuery, new { name = connection.Database });\n\n                    return new Metric(value);\n                });\n            });\n\n        public static readonly DashboardMetric ActiveTransactions = new DashboardMetric(\n            \"transactions:active\",\n            \"Metrics_SQLServer_ActiveTransactions\",\n            static page =>\n            {\n                var sqlStorage = page.Storage as SqlServerStorage;\n                if (sqlStorage == null) return new Metric(\"???\");\n\n                return sqlStorage.UseConnection(null, static (_, connection) =>\n                {\n                    var sqlQuery = @\"\nselect count(*) from sys.sysprocesses\nwhere dbid = db_id(@name) and status != 'background' and open_tran = 1\";\n\n                    var value = connection\n                        .QuerySingle<int>(sqlQuery, new { name = connection.Database });\n\n                    return new Metric(value);\n                });\n            });\n\n        public static readonly DashboardMetric DataFilesSize = new DashboardMetric(\n            \"database:files:rows:size\",\n            \"Metrics_SQLServer_DataFilesSize\",\n            static page =>\n            {\n                var sqlStorage = page.Storage as SqlServerStorage;\n                if (sqlStorage == null) return new Metric(\"???\");\n\n                return sqlStorage.UseConnection(null, static (_, connection) =>\n                {\n                    var sqlQuery = @\"\nselect SUM(CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT)/128.0) as RowsSizeMB from sys.database_files\nwhere type = 0;\";\n\n                    var value = connection.QuerySingle<double>(sqlQuery);\n\n                    return new Metric(value.ToString(\"F\", CultureInfo.CurrentCulture));\n                });\n            });\n\n        public static readonly DashboardMetric LogFilesSize = new DashboardMetric(\n            \"database:files:log:size\",\n            \"Metrics_SQLServer_LogFilesSize\",\n            static page =>\n            {\n                var sqlStorage = page.Storage as SqlServerStorage;\n                if (sqlStorage == null) return new Metric(\"???\");\n\n                return sqlStorage.UseConnection(null, static (_, connection) =>\n                {\n                    var sqlQuery = @\"\nselect SUM(CAST(FILEPROPERTY(name, 'SpaceUsed') AS INT)/128.0) as LogSizeMB from sys.database_files\nwhere type = 1;\";\n\n                    var value = connection.QuerySingle<double>(sqlQuery);\n\n                    return new Metric(value.ToString(\"F\", CultureInfo.CurrentCulture));\n                });\n            });\n\n        public static readonly DashboardMetric SchemaVersion = new DashboardMetric(\n            \"sqlserver:schema\",\n            \"Metrics_SQLServer_SchemaVersion\",\n            static page =>\n            {\n                var sqlStorage = page.Storage as SqlServerStorage;\n                if (sqlStorage == null) return new Metric(\"???\");\n\n                return sqlStorage.UseConnection(null, static (storage, connection) =>\n                {\n                    var sqlQuery = $@\"select top(1) [Version] from [{storage.SchemaName}].[Schema]\";\n                    var version = connection.QuerySingleOrDefault<int?>(sqlQuery);\n\n                    if (!version.HasValue)\n                    {\n                        return new Metric(\"Unspecified\")\n                        {\n                            Style = MetricStyle.Danger,\n                        };\n                    }\n\n                    return new Metric(version.Value)\n                    {\n                        Style = version < SqlServerObjectsInstaller.LatestSchemaVersion\n                            ? MetricStyle.Warning\n                            : version == SqlServerObjectsInstaller.LatestSchemaVersion\n                                ? MetricStyle.Success\n                                : MetricStyle.Default\n                    };\n                });\n            });\n\n        public static readonly Func<string, string, string, DashboardMetric> PerformanceCounterDatabaseMetric = \n            static (string objectName, string counterName, string instanceName) => new DashboardMetric(\n            $\"sqlserver:counter:{objectName}:{counterName}:{instanceName ?? \"db\"}\",\n            counterName,\n            page =>\n            {\n                if (objectName == null) throw new ArgumentNullException(nameof(objectName));\n                if (counterName == null) throw new ArgumentNullException(nameof(counterName));\n\n                var sqlStorage = page.Storage as SqlServerStorage;\n                if (sqlStorage == null) return new Metric(\"???\");\n\n                return sqlStorage.UseConnection(null, static (_, connection, ctx) =>\n                {\n                    var sqlQuery = $@\"SELECT cntr_value FROM sys.dm_os_performance_counters where object_name = @objectName and instance_name = @instanceName and counter_name = @counterName\";\n                    long? value;\n\n                    try\n                    {\n                        value = connection.QuerySingle<long>(sqlQuery, new\n                        {\n                            objectName = ctx.Item1,\n                            instanceName = ctx.Item2 ?? connection.Database,\n                            counterName = ctx.Item3\n                        });\n                    }\n                    catch\n                    {\n                        value = null;\n                    }\n\n                    return value != null ? new Metric(value.Value) : new Metric(\"???\");\n                }, Tuple.Create(objectName, instanceName, counterName));\n            });\n\n        private sealed class\n            QueryTemplateKeyEqualityComparer : IEqualityComparer<KeyValuePair<Func<string, string>, string>>\n        {\n            public bool Equals(KeyValuePair<Func<string, string>, string> x, KeyValuePair<Func<string, string>, string> y)\n            {\n                return x.Key.Equals(y.Key) && x.Value == y.Value;\n            }\n\n            public int GetHashCode(KeyValuePair<Func<string, string>, string> obj)\n            {\n                unchecked\n                {\n                    return (obj.Key.GetHashCode() * 397) ^ obj.Value.GetHashCode();\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerStorageExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Data.Common;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Hangfire.SqlServer;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire\n{\n    public static class SqlServerStorageExtensions\n    {\n        private static int _metricsInitialized;\n\n        public static IGlobalConfiguration<SqlServerStorage> UseSqlServerStorage(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] string nameOrConnectionString)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (nameOrConnectionString == null) throw new ArgumentNullException(nameof(nameOrConnectionString));\n\n            var storage = new SqlServerStorage(nameOrConnectionString);\n            return configuration.UseStorage(storage).UseSqlServerStorageCommonMetrics();\n        }\n\n        public static IGlobalConfiguration<SqlServerStorage> UseSqlServerStorage(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] string nameOrConnectionString, \n            [NotNull] SqlServerStorageOptions options)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (nameOrConnectionString == null) throw new ArgumentNullException(nameof(nameOrConnectionString));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            var storage = new SqlServerStorage(nameOrConnectionString, options);\n            return configuration.UseStorage(storage).UseSqlServerStorageCommonMetrics();\n        }\n\n        public static IGlobalConfiguration<SqlServerStorage> UseSqlServerStorage(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] Func<DbConnection> connectionFactory)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (connectionFactory == null) throw new ArgumentNullException(nameof(connectionFactory));\n\n            var storage = new SqlServerStorage(connectionFactory);\n            return configuration.UseStorage(storage).UseSqlServerStorageCommonMetrics();\n        }\n\n        public static IGlobalConfiguration<SqlServerStorage> UseSqlServerStorage(\n            [NotNull] this IGlobalConfiguration configuration,\n            [NotNull] Func<DbConnection> connectionFactory,\n            [NotNull] SqlServerStorageOptions options)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n            if (connectionFactory == null) throw new ArgumentNullException(nameof(connectionFactory));\n            if (options == null) throw new ArgumentNullException(nameof(options));\n\n            var storage = new SqlServerStorage(connectionFactory, options);\n            return configuration.UseStorage(storage).UseSqlServerStorageCommonMetrics();\n        }\n\n        private static IGlobalConfiguration<SqlServerStorage> UseSqlServerStorageCommonMetrics(\n            [NotNull] this IGlobalConfiguration<SqlServerStorage> configuration)\n        {\n            if (configuration == null) throw new ArgumentNullException(nameof(configuration));\n\n            if (Interlocked.Exchange(ref _metricsInitialized, 1) == 0)\n            {\n                configuration.UseDashboardMetric(SqlServerStorage.SchemaVersion);\n                configuration.UseDashboardMetric(SqlServerStorage.ActiveConnections);\n                configuration.UseDashboardMetric(SqlServerStorage.TotalConnections);\n                configuration.UseDashboardMetric(SqlServerStorage.ActiveTransactions);\n                configuration.UseDashboardMetric(SqlServerStorage.DataFilesSize);\n                configuration.UseDashboardMetric(SqlServerStorage.LogFilesSize);\n            }\n\n            return configuration;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerStorageOptions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Data.Common;\nusing System.Reflection;\n#if FEATURE_TRANSACTIONSCOPE\nusing System.Transactions;\n#else\nusing System.Data;\n#endif\n\nnamespace Hangfire.SqlServer\n{\n    public class SqlServerStorageOptions\n    {\n        private TimeSpan _queuePollInterval;\n        private string _schemaName;\n        private TimeSpan _jobExpirationCheckInterval;\n        private TimeSpan? _slidingInvisibilityTimeout;\n        private DbProviderFactory _sqlClientFactory;\n\n        public SqlServerStorageOptions()\n        {\n            QueuePollInterval = TimeSpan.Zero;\n            SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5);\n#pragma warning disable 618\n            InvisibilityTimeout = TimeSpan.FromMinutes(30);\n#pragma warning restore 618\n            JobExpirationCheckInterval = TimeSpan.FromMinutes(30);\n            CountersAggregateInterval = TimeSpan.FromMinutes(5);\n            PrepareSchemaIfNecessary = true;\n            DashboardJobListLimit = 10000;\n            _schemaName = Constants.DefaultSchema;\n            TransactionTimeout = TimeSpan.FromMinutes(1);\n            DisableGlobalLocks = false;\n            DeleteExpiredBatchSize = -1;\n            UseTransactionalAcknowledge = false;\n            UseRecommendedIsolationLevel = true;\n            CommandBatchMaxTimeout = TimeSpan.FromMinutes(5);\n            TryAutoDetectSchemaDependentOptions = true;\n            _sqlClientFactory = GetDefaultSqlClientFactory();\n            InactiveStateExpirationTimeout = TimeSpan.Zero;\n        }\n\n        private static DbProviderFactory GetDefaultSqlClientFactory()\n        {\n            var dbProviderFactoryTypes = new[]\n            {\n                \"Microsoft.Data.SqlClient.SqlClientFactory, Microsoft.Data.SqlClient\",\n                // Available in the .NET Framework GAC, requires Version + Culture + PublicKeyToken to be explicitly specified\n                \"System.Data.SqlClient.SqlClientFactory, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\",\n                \"System.Data.SqlClient.SqlClientFactory, System.Data.SqlClient\",\n            };\n\n            foreach (var dbProviderFactoryType in dbProviderFactoryTypes)\n            {\n                var providerFactoryType = Type.GetType(dbProviderFactoryType, throwOnError: false);\n                if (providerFactoryType != null)\n                {\n                    var instanceField = providerFactoryType.GetField(\"Instance\");\n                    if (instanceField == null)\n                    {\n                        continue;\n                    }\n                    var instance = (DbProviderFactory)instanceField.GetValue(null);\n                    if (instance != null)\n                    {\n                        return instance;\n                    }\n                }\n            }\n\n            return null;\n        }\n\n        [Obsolete(\"TransactionIsolationLevel option is deprecated, please set UseRecommendedIsolationLevel instead. Will be removed in 2.0.0.\")]\n        public IsolationLevel? TransactionIsolationLevel { get; set; }\n\n        public TimeSpan QueuePollInterval\n        {\n            get { return _queuePollInterval; }\n            set\n            {\n                var message = $\"The QueuePollInterval property value should be positive. Given: {value}.\";\n\n                if (value != value.Duration())\n                {\n                    throw new ArgumentException(message, nameof(value));\n                }\n\n                _queuePollInterval = value;\n            }\n        }\n\n        [Obsolete(\"Does not make sense anymore. Background jobs re-queued instantly even after ungraceful shutdown now. Will be removed in 2.0.0.\")]\n        public TimeSpan InvisibilityTimeout { get; set; }\n\n        public TimeSpan? SlidingInvisibilityTimeout\n        {\n            get { return _slidingInvisibilityTimeout; }\n            set\n            {\n                if (value <= TimeSpan.Zero)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), \"Sliding timeout should be greater than zero\");\n                }\n\n                _slidingInvisibilityTimeout = value;\n            }\n        }\n\n        public bool PrepareSchemaIfNecessary { get; set; }\n\n        public TimeSpan JobExpirationCheckInterval \n        {\n            get { return _jobExpirationCheckInterval; } \n            set {\n                if (value.TotalMilliseconds > int.MaxValue)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), \"Job expiration check interval cannot be greater than int.MaxValue\");\n                }\n                _jobExpirationCheckInterval = value;\n            }\n        }\n\n        public TimeSpan CountersAggregateInterval { get; set; }\n\n        public int? DashboardJobListLimit { get; set; }\n        public TimeSpan TransactionTimeout { get; set; }\n        public TimeSpan? CommandTimeout { get; set; }\n        public TimeSpan? CommandBatchMaxTimeout { get; set; }\n        public TimeSpan InactiveStateExpirationTimeout { get; set; }\n\n        public string SchemaName\n        {\n            get { return _schemaName; }\n            set\n            {\n                if (string.IsNullOrWhiteSpace(_schemaName))\n                {\n                    throw new ArgumentException(_schemaName, nameof(value));\n                }\n                _schemaName = value;\n            }\n        }\n\n        public Func<IDisposable> ImpersonationFunc { get; set; }\n        public bool DisableGlobalLocks { get; set; }\n        \n        [Obsolete(\"This option is deprecated and doesn't change anything. You can safely remove it. Will be removed in 2.0.0.\")]\n        public bool UsePageLocksOnDequeue { get; set; }\n        public bool UseRecommendedIsolationLevel { get; set; }\n        public bool EnableHeavyMigrations { get; set; }\n        public bool UseFineGrainedLocks { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether IGNORE_DUP_KEY was applied to [Hash] and [Set] tables and so MERGE\n        /// statements can be replaced by much more efficient INSERT/UPDATE pair. This option allows\n        /// to avoid deadlocks related to SERIALIZABLE-level range locks without introducing transient\n        /// errors due to concurrency.\n        /// </summary>\n        public bool UseIgnoreDupKeyOption { get; set; }\n\n        /// <summary>\n        /// Gets or sets the number of records deleted in a single batch in expiration manager. Default\n        /// value is 1000, but it can be configured to a higher one when processing throughput is high\n        /// enough, so expiration manager becomes the bottleneck.\n        /// </summary>\n        public int DeleteExpiredBatchSize { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether to enable experimental feature of transactional acknowledge of completed\n        /// background jobs. In this case there will be less requests sent to SQL Server and better handling\n        /// of data loss when asynchronous replication is used. But additional blocking on the JobQueue table\n        /// is expected, since transaction commit requires an explicit Commit request to be sent. \n        /// </summary>\n        public bool UseTransactionalAcknowledge { get; set; }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"DbProviderFactory\"/> for creating <c>SqlConnection</c> instances.\n        /// Defaults to either <c>System.Data.SqlClient.SqlClientFactory.Instance</c> or\n        /// <c>Microsoft.Data.SqlClient.SqlClientFactory</c> depending on which package reference exists\n        /// on the consuming project.\n        /// </summary>\n        public DbProviderFactory SqlClientFactory\n        {\n            get => _sqlClientFactory ?? throw new InvalidOperationException(\"Please add a NuGet package reference to either 'Microsoft.Data.SqlClient' or 'System.Data.SqlClient' in your application project. \" +\n                                                                            \"Hangfire.SqlServer supports both providers but let the consumer decide which one should be used.\");\n            set => _sqlClientFactory = value ?? throw new ArgumentNullException(nameof(value));\n        }\n\n        /// <summary>\n        /// Gets or sets whether to try automatically query for the current schema on application start\n        /// and enable <see cref=\"UseIgnoreDupKeyOption\"/>, <see cref=\"DeleteExpiredBatchSize\"/> and\n        /// <see cref=\"DisableGlobalLocks\"/> options depending on the current schema version. When storage\n        /// is inaccessible on startup, default values will be used for those options.\n        /// </summary>\n        public bool TryAutoDetectSchemaDependentOptions { get; set; }\n\n        /// <summary>\n        /// Gets or sets a default queue provider that will be used when no special provider was\n        /// registered for a particular queue.\n        /// </summary>\n        public IPersistentJobQueueProvider DefaultQueueProvider { get; set; }\n\n#if FEATURE_TRANSACTIONSCOPE\n        /// <summary>\n        /// Disables the use of the System.Transactions namespace and <see cref=\"TransactionScope\"/> class\n        /// and switches to regular explicit transactions usage in .NET Framework version of <see cref=\"SqlServerStorage\"/>\n        /// (as in .NET Core's implementation). Potentially fixes problems with abandoned locks and exhausted\n        /// connection pool.\n        /// </summary>\n        /// <remarks>\n        /// This option only works with the default\n        /// <see cref=\"IPersistentJobQueueProvider\"/>, throwing an exception when external queue providers are used\n        /// (such as MSMQ-based or custom ones).\n        /// </remarks>\n        public bool DisableTransactionScope { get; set; }\n#endif\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerTimeoutJob.cs",
    "content": "// This file is part of Hangfire. Copyright © 2017 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.Annotations;\nusing Hangfire.Logging;\nusing Hangfire.Storage;\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class SqlServerTimeoutJob : IFetchedJob\n    {\n        private readonly ILog _logger = LogProvider.GetLogger(typeof(SqlServerTimeoutJob));\n\n        private readonly object _syncRoot = new object();\n        private readonly SqlServerStorage _storage;\n        private bool _disposed;\n        private bool _removedFromQueue;\n        private bool _requeued;\n        private SqlServerWriteOnlyTransaction _transaction;\n\n        private long _lastHeartbeat;\n        private TimeSpan _interval;\n\n        public SqlServerTimeoutJob(\n            [NotNull] SqlServerStorage storage,\n            long id,\n            [NotNull] string jobId,\n            [NotNull] string queue,\n            [NotNull] DateTime? fetchedAt)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n            if (fetchedAt == null) throw new ArgumentNullException(nameof(fetchedAt));\n\n            _storage = storage;\n\n            Id = id;\n            JobId = jobId;\n            Queue = queue;\n            FetchedAt = fetchedAt.Value;\n\n            if (storage.SlidingInvisibilityTimeout.HasValue)\n            {\n                _lastHeartbeat = TimestampHelper.GetTimestamp();\n                _interval = TimeSpan.FromSeconds(storage.SlidingInvisibilityTimeout.Value.TotalSeconds / 5);\n                storage.HeartbeatProcess.Track(this);\n            }\n        }\n\n        public long Id { get; }\n        public string JobId { get; }\n        public string Queue { get; }\n\n        internal DateTime? FetchedAt { get; private set; }\n\n        public void RemoveFromQueue()\n        {\n            lock (_syncRoot)\n            {\n                if (_transaction != null && _transaction.Committed) return;\n                if (!FetchedAt.HasValue) return;\n\n                _storage.UseConnection(\n                    null,\n                    static (storage, connection, ctx) => connection.Execute(\n                        storage.GetQueryFromTemplate(static schemaName =>\n                            $@\"delete JQ from [{schemaName}].JobQueue JQ with (forceseek, rowlock) where Queue = @queue and Id = @id and FetchedAt = @fetchedAt\"),\n                        new { queue = ctx.Queue, id = ctx.Id, fetchedAt = ctx.FetchedAt },\n                        commandTimeout: storage.CommandTimeout),\n                    this);\n\n                _removedFromQueue = true;\n            }\n        }\n\n        public void Requeue()\n        {\n            lock (_syncRoot)\n            {\n                if (_transaction != null && _transaction.Committed) return;\n\n                if (!FetchedAt.HasValue) return;\n\n                _storage.UseConnection(\n                    null,\n                    static (storage, connection, ctx) => connection.Execute(\n                        storage.GetQueryFromTemplate(static schemaName =>\n                            $@\"update JQ set FetchedAt = null from [{schemaName}].JobQueue JQ with (forceseek, rowlock) where Queue = @queue and Id = @id and FetchedAt = @fetchedAt\"),\n                         new { queue = ctx.Queue, id = ctx.Id, fetchedAt = ctx.FetchedAt },\n                         commandTimeout: storage.CommandTimeout),\n                    this);\n\n                FetchedAt = null;\n                _requeued = true;\n            }\n        }\n\n        public void Dispose()\n        {\n            if (_disposed) return;\n            _disposed = true;\n\n            DisposeTimer();\n\n            lock (_syncRoot)\n            {\n                if (!_removedFromQueue && !_requeued && (_transaction == null || !_transaction.Committed))\n                {\n                    Requeue();\n                }\n            }\n        }\n\n        internal void SetTransaction(SqlServerWriteOnlyTransaction transaction)\n        {\n            lock (_syncRoot)\n            {\n                _transaction = transaction;\n            }\n        }\n\n        internal void DisposeTimer()\n        {\n            _storage.HeartbeatProcess.Untrack(this);\n        }\n\n        internal void ExecuteKeepAliveQueryIfRequired()\n        {\n            var now = TimestampHelper.GetTimestamp();\n\n            if (TimestampHelper.Elapsed(now, Interlocked.Read(ref _lastHeartbeat)) >= _interval)\n            {\n                lock (_syncRoot)\n                {\n                    if (!FetchedAt.HasValue) return;\n\n                    if (_requeued || _removedFromQueue) return;\n                    if (_transaction != null && _transaction.Committed) return;\n\n                    try\n                    {\n                        FetchedAt = _storage.UseConnection(\n                            null,\n                            static (storage, connection, ctx) => connection.ExecuteScalar<DateTime?>(\n                                storage.GetQueryFromTemplate(static schemaName =>\n                                    $@\"update JQ set FetchedAt = getutcdate() output INSERTED.FetchedAt from [{schemaName}].JobQueue JQ with (forceseek, rowlock) where Queue = @queue and Id = @id and FetchedAt = @fetchedAt\"),\n                                new { queue = ctx.Queue, id = ctx.Id, fetchedAt = ctx.FetchedAt },\n                                commandTimeout: storage.CommandTimeout),\n                            this);\n\n                        if (!FetchedAt.HasValue)\n                        {\n                            _logger.Warn(\n                                $\"Background job identifier '{JobId}' was fetched by another worker, will not execute keep alive.\");\n                        }\n\n                        _logger.Trace($\"Keep-alive query for message {Id} sent\");\n                        Interlocked.Exchange(ref _lastHeartbeat, now);\n                    }\n                    catch (Exception ex) when (ex.IsCatchableExceptionType())\n                    {\n                        _logger.DebugException($\"Unable to execute keep-alive query for message {Id}\", ex);\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerTransactionJob.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Data;\nusing System.Threading;\nusing Dapper;\nusing Hangfire.Annotations;\nusing Hangfire.Storage;\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class SqlServerTransactionJob : IFetchedJob\n    {\n        // Connections to SQL Azure Database that are idle for 30 minutes \n        // or longer will be terminated. And since we are using separate\n        // connection for a holding a transaction during the background\n        // job processing, we'd like to prevent Resource Governor from \n        // terminating it.\n        private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1);\n\n        private readonly SqlServerStorage _storage;\n        private IDbConnection _connection;\n        private readonly IDbTransaction _transaction;\n        private readonly Timer _timer;\n        private readonly object _lockObject = new object();\n\n        public SqlServerTransactionJob(\n            [NotNull] SqlServerStorage storage,\n            [NotNull] IDbConnection connection, \n            [NotNull] IDbTransaction transaction, \n            string jobId, \n            string queue)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            if (transaction == null) throw new ArgumentNullException(nameof(transaction));\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n\n            _storage = storage;\n            _connection = connection;\n            _transaction = transaction;\n\n            JobId = jobId;\n            Queue = queue;\n\n            if (!_storage.IsExistingConnection(_connection))\n            {\n                _timer = new Timer(ExecuteKeepAliveQuery, null, KeepAliveInterval, KeepAliveInterval);\n            }\n        }\n\n        public string JobId { get; }\n        public string Queue { get; }\n\n        public void RemoveFromQueue()\n        {\n            lock (_lockObject)\n            {\n                _transaction.Commit();\n            }\n        }\n\n        public void Requeue()\n        {\n            lock (_lockObject)\n            {\n                _transaction.Rollback();\n            }\n        }\n\n        public void Dispose()\n        {\n            // Timer callback may be invoked after the Dispose method call,\n            // so we are using lock to avoid unsynchronized calls.\n            lock (_lockObject)\n            {\n                _timer?.Dispose();\n                _transaction.Dispose();\n                _storage.ReleaseConnection(_connection);\n                _connection = null;\n            }\n        }\n\n        private void ExecuteKeepAliveQuery(object obj)\n        {\n            lock (_lockObject)\n            {\n                try\n                {\n                    _connection?.Execute(\"SELECT 1;\", transaction: _transaction);\n                }\n                catch (Exception ex) when (ex.IsCatchableExceptionType())\n                {\n                    // Connection was closed. So we can't continue to send\n                    // keep-alive queries. Unlike for distributed locks,\n                    // there is no any caveats of having this issue for\n                    // queues, because Hangfire guarantees only the \"at least\n                    // once\" processing.\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer/SqlServerWriteOnlyTransaction.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Globalization;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\n\n// ReSharper disable RedundantAnonymousTypePropertyName\n\nnamespace Hangfire.SqlServer\n{\n    internal sealed class SqlServerWriteOnlyTransaction : JobStorageTransaction\n    {\n        private readonly Queue<Action<DbConnection, DbTransaction>> _queueCommandQueue = new();\n        private readonly HashSet<string> _queuesToSignal = new();\n\n        private readonly SqlServerStorage _storage;\n        private readonly SqlServerConnection _connection;\n\n        private readonly SortedDictionary<long, List<Func<DbConnection, DbCommand>>> _jobCommands = new();\n        private readonly SortedDictionary<string, List<Func<DbConnection, DbCommand>>> _counterCommands = new();\n        private readonly SortedDictionary<string, List<Func<DbConnection, DbCommand>>> _hashCommands = new();\n        private readonly SortedDictionary<string, List<Func<DbConnection, DbCommand>>> _listCommands = new();\n        private readonly SortedDictionary<string, List<Func<DbConnection, DbCommand>>> _setCommands = new();\n        private readonly SortedDictionary<string, List<Func<DbConnection, DbCommand>>> _queueCommands = new();\n        private readonly List<Tuple<DbCommand, DbParameter, string>> _lockCommands = new();\n\n        private readonly List<SqlServerConnection.DisposableLock> _acquiredLocks = new();\n\n        private readonly SortedSet<string> _lockedResources = new();\n\n        private bool _committed;\n\n        public SqlServerWriteOnlyTransaction([NotNull] SqlServerConnection connection)\n        {\n            if (connection == null) throw new ArgumentNullException(nameof(connection));\n            _connection = connection;\n            _storage = connection.Storage;\n        }\n\n        public bool Committed => _committed;\n\n        public override void Commit()\n        {\n            try\n            {\n                _storage.UseTransaction(_connection.DedicatedConnection, static (storage, connection, transaction, ctx) =>\n                {\n                    using (var commandBatch = new SqlCommandBatch(connection, transaction, preferBatching: storage.CommandBatchMaxTimeout.HasValue))\n                    {\n                        commandBatch.Append(connection.Create(\"set xact_abort on;set nocount on;\"));\n\n                        foreach (var lockedResource in ctx._lockedResources)\n                        {\n                            commandBatch.Append(connection\n                                .Create(\"exec sp_getapplock @Resource=@resource, @LockMode=N'Exclusive'\")\n                                .AddParameter(\"@resource\", lockedResource, DbType.String, size: 255));\n                        }\n\n                        AppendBatch(ctx._jobCommands, commandBatch);\n                        AppendBatch(ctx._counterCommands, commandBatch);\n                        AppendBatch(ctx._hashCommands, commandBatch);\n                        AppendBatch(ctx._listCommands, commandBatch);\n                        AppendBatch(ctx._setCommands, commandBatch);\n                        AppendBatch(ctx._queueCommands, commandBatch);\n\n                        foreach (var command in ctx._lockCommands)\n                        {\n                            commandBatch.Append(command.Item1);\n                        }\n\n                        commandBatch.CommandTimeout = storage.CommandTimeout;\n                        commandBatch.CommandBatchMaxTimeout = storage.CommandBatchMaxTimeout;\n\n                        commandBatch.ExecuteNonQuery();\n                        foreach (var acquiredLock in ctx._acquiredLocks)\n                        {\n                            acquiredLock.TryReportReleased();\n                        }\n\n                        foreach (var lockCommand in ctx._lockCommands)\n                        {\n                            var releaseResult = lockCommand.Item2.GetParameterValue<int?>();\n                            if (releaseResult.HasValue && releaseResult.Value < 0)\n                            {\n                                throw new SqlServerDistributedLockException($\"Could not release a lock on the resource '{lockCommand.Item3}': Server returned the '{releaseResult}' error.\");\n                            }\n                        }\n                        \n                        foreach (var queueCommand in ctx._queueCommandQueue)\n                        {\n                            queueCommand(connection, transaction);\n                        }\n                    }\n                }, this);\n\n                _committed = true;\n            }\n            finally\n            {\n                foreach (var acquiredLock in _acquiredLocks)\n                {\n                    acquiredLock.Dispose();\n                }\n            }\n\n            TrySignalListeningWorkers();\n        }\n\n        public override void Dispose()\n        {\n            foreach (var acquiredLock in _acquiredLocks)\n            {\n                acquiredLock.Dispose();\n            }\n\n            base.Dispose();\n        }\n\n        public override void AcquireDistributedLock(string resource, TimeSpan timeout)\n        {\n            if (String.IsNullOrWhiteSpace(resource)) throw new ArgumentNullException(nameof(resource));\n\n            var disposableLock = _connection.AcquireLock($\"{_storage.SchemaName}:{resource}\", timeout);\n            if (disposableLock.OwnLock)\n            {\n                var command = SqlServerDistributedLock.CreateReleaseCommand(\n                    _connection.DedicatedConnection,\n                    disposableLock.Resource,\n                    out var resultParameter);\n\n                _lockCommands.Add(Tuple.Create(command, resultParameter, disposableLock.Resource));\n            }\n\n            _acquiredLocks.Add(disposableLock);\n        }\n\n        public override void ExpireJob(string jobId, TimeSpan expireIn)\n        {\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"update J set ExpireAt = @expireAt from [{schemaName}].Job J with (forceseek) where Id = @id;\");\n\n            var longId = long.Parse(jobId, CultureInfo.InvariantCulture);\n\n            AddCommand(_jobCommands, longId, batch => batch.Create(query)\n                .AddParameter(\"@expireAt\", DateTime.UtcNow.Add(expireIn), DbType.DateTime)\n                .AddParameter(\"@id\", longId, DbType.Int64));\n        }\n\n        public override void PersistJob(string jobId)\n        {\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"update J set ExpireAt = NULL from [{schemaName}].Job J with (forceseek) where Id = @id;\");\n\n            var longId = long.Parse(jobId, CultureInfo.InvariantCulture);\n\n            AddCommand(_jobCommands, longId, batch => batch.Create(query)\n                .AddParameter(\"@id\", longId, DbType.Int64));\n        }\n\n        public override void SetJobState(string jobId, IState state)\n        {\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n            if (state == null) throw new ArgumentNullException(nameof(state));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].State (JobId, Name, Reason, CreatedAt, Data)\nvalues (@jobId, @name, @reason, @createdAt, @data);\nupdate [{schemaName}].Job set StateId = SCOPE_IDENTITY(), StateName = @name where Id = @jobId;\");\n\n            var longId = long.Parse(jobId, CultureInfo.InvariantCulture);\n\n            AddCommand(_jobCommands, longId, batch => batch.Create(query)\n                .AddParameter(\"@jobId\", longId, DbType.Int64)\n                .AddParameter(\"@name\", state.Name, DbType.String, size: 20)\n                .AddParameter(\"@reason\", (object)state.Reason?.Substring(0, Math.Min(99, state.Reason.Length)) ?? DBNull.Value, DbType.String, size: 100)\n                .AddParameter(\"@createdAt\", DateTime.UtcNow, DbType.DateTime)\n                .AddParameter(\"@data\", (object)SerializationHelper.Serialize(state.SerializeData()) ?? DBNull.Value, DbType.String, size: -1));\n        }\n\n        public override void AddJobState(string jobId, IState state)\n        {\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n            if (state == null) throw new ArgumentNullException(nameof(state));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].State (JobId, Name, Reason, CreatedAt, Data)\nvalues (@jobId, @name, @reason, @createdAt, @data)\");\n\n            var longId = long.Parse(jobId, CultureInfo.InvariantCulture);\n\n            AddCommand(_jobCommands, longId, batch => batch.Create(query)\n                .AddParameter(\"@jobId\", longId, DbType.Int64)\n                .AddParameter(\"@name\", state.Name, DbType.String, size: 20)\n                .AddParameter(\"@reason\", (object)state.Reason?.Substring(0, Math.Min(99, state.Reason.Length)) ?? DBNull.Value, DbType.String, size: 100)\n                .AddParameter(\"@createdAt\", DateTime.UtcNow, DbType.DateTime)\n                .AddParameter(\"@data\", (object)SerializationHelper.Serialize(state.SerializeData()) ?? DBNull.Value, DbType.String, size: -1));\n        }\n\n        public override void AddToQueue(string queue, string jobId)\n        {\n            if (queue == null) throw new ArgumentNullException(nameof(queue));\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n\n            var provider = _storage.QueueProviders.GetProvider(queue);\n            var persistentQueue = provider.GetJobQueue();\n\n            if (persistentQueue.GetType() == typeof(SqlServerJobQueue))\n            {\n                var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].JobQueue (JobId, Queue) values (@jobId, @queue)\");\n\n                AddCommand(_queueCommands, queue, batch => batch.Create(query)\n                    .AddParameter(\"@jobId\", long.Parse(jobId, CultureInfo.InvariantCulture), DbType.Int64)\n                    .AddParameter(\"@queue\", queue, DbType.String));\n\n                _queuesToSignal.Add(queue);\n            }\n            else\n            {\n#if FEATURE_TRANSACTIONSCOPE\n                if (_storage.Options.DisableTransactionScope)\n                {\n                    throw new NotSupportedException($\"`{nameof(SqlServerStorageOptions.DisableTransactionScope)}` option does not support external queue providers\");\n                }\n#endif\n                _queueCommandQueue.Enqueue((connection, transaction) => persistentQueue.Enqueue(\n                    connection,\n#if !FEATURE_TRANSACTIONSCOPE\n                    transaction,\n#endif\n                    queue,\n                    jobId));\n            }\n        }\n\n        public override void IncrementCounter(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].Counter ([Key], [Value]) values (@key, @value)\");\n\n            AddCommand(_counterCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@value\", value: +1, DbType.Int32));\n        }\n\n        public override void IncrementCounter(string key, TimeSpan expireIn)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].Counter ([Key], [Value], [ExpireAt]) values (@key, @value, @expireAt)\");\n\n            AddCommand(_counterCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@value\", value: +1, DbType.Int32)\n                .AddParameter(\"@expireAt\", DateTime.UtcNow.Add(expireIn), DbType.DateTime));\n        }\n\n        public override void DecrementCounter(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].Counter ([Key], [Value]) values (@key, @value)\");\n\n            AddCommand(_counterCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@value\", value: -1, DbType.Int32));\n        }\n\n        public override void DecrementCounter(string key, TimeSpan expireIn)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].Counter ([Key], [Value], [ExpireAt]) values (@key, @value, @expireAt)\");\n\n            AddCommand(_counterCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@value\", value: -1, DbType.Int32)\n                .AddParameter(\"@expireAt\", DateTime.UtcNow.Add(expireIn), DbType.DateTime));\n        }\n\n        public override void AddToSet(string key, string value)\n        {\n            AddToSet(key, value, 0.0);\n        }\n\n        public override void AddToSet(string key, string value, double score)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n            if (value == null) throw new ArgumentNullException(nameof(value));\n\n            var query = _storage.GetQueryFromTemplate(_storage.Options.UseIgnoreDupKeyOption\n                ? static schemaName => $@\"insert into [{schemaName}].[Set] ([Key], Value, Score) values (@key, @value, @score);\nif @@ROWCOUNT = 0 update [{schemaName}].[Set] set Score = @score where [Key] = @key and Value = @value;\"\n\n                : static schemaName => $@\";merge [{schemaName}].[Set] with (xlock) as Target\nusing (VALUES (@key, @value, @score)) as Source ([Key], Value, Score)\non Target.[Key] = Source.[Key] and Target.Value = Source.Value\nwhen matched then update set Score = Source.Score\nwhen not matched then insert ([Key], Value, Score) values (Source.[Key], Source.Value, Source.Score);\");\n\n            AcquireSetLock(key);\n            AddCommand(_setCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@value\", value, DbType.String, 256)\n                .AddParameter(\"@score\", score, DbType.Double, size: 53));\n        }\n\n        public override void RemoveFromSet(string key, string value)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n            if (value == null) throw new ArgumentNullException(nameof(value));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"delete S from [{schemaName}].[Set] S with (forceseek) where [Key] = @key and Value = @value\");\n\n            AcquireSetLock(key);\n            AddCommand(_setCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@value\", value, DbType.String, size: 256));\n        }\n\n        public override void InsertToList(string key, string value)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n            if (value == null) throw new ArgumentNullException(nameof(value));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName => $@\"\nselect [Key] from [{schemaName}].List with (xlock, forceseek)\nwhere [Key] = @key;\ninsert into [{schemaName}].List ([Key], Value) values (@key, @value);\");\n\n            AcquireListLock(key);\n            AddCommand(_listCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@value\", value, DbType.String, size: -1));\n        }\n\n        public override void RemoveFromList(string key, string value)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n            if (value == null) throw new ArgumentNullException(nameof(value));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"delete L from [{schemaName}].List L with (forceseek) where [Key] = @key and Value = @value\");\n\n            AcquireListLock(key);\n            AddCommand(_listCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@value\", value, DbType.String, size: -1));\n        }\n\n        public override void TrimList(string key, int keepStartingFrom, int keepEndingAt)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\";with cte as (\n    select row_number() over (order by Id desc) as row_num\n    from [{schemaName}].List with (xlock, forceseek)\n    where [Key] = @key)\ndelete from cte where row_num not between @start and @end\");\n\n            AcquireListLock(key);\n\n            AddCommand(_listCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@start\", keepStartingFrom + 1, DbType.Int32)\n                .AddParameter(\"@end\", keepEndingAt + 1, DbType.Int32));\n        }\n\n        public override void SetRangeInHash(string key, IEnumerable<KeyValuePair<string, string>> keyValuePairs)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n            if (keyValuePairs == null) throw new ArgumentNullException(nameof(keyValuePairs));\n\n            var query = _storage.GetQueryFromTemplate(_storage.Options.UseIgnoreDupKeyOption\n                ? static schemaName => $@\"insert into [{schemaName}].Hash ([Key], Field, Value) values (@key, @field, @value);\nif @@ROWCOUNT = 0 update [{schemaName}].Hash set Value = @value where [Key] = @key and Field = @field;\"\n\n                : static schemaName => $@\";merge [{schemaName}].Hash with (xlock) as Target\nusing (VALUES (@key, @field, @value)) as Source ([Key], Field, Value)\non Target.[Key] = Source.[Key] and Target.Field = Source.Field\nwhen matched then update set Value = Source.Value\nwhen not matched then insert ([Key], Field, Value) values (Source.[Key], Source.Field, Source.Value);\");\n\n            AcquireHashLock(key);\n\n            foreach (var pair in keyValuePairs)\n            {\n                AddCommand(_hashCommands, key, batch => batch.Create(query)\n                    .AddParameter(\"@key\", key, DbType.String)\n                    .AddParameter(\"@field\", pair.Key, DbType.String, size: 100)\n                    .AddParameter(\"@value\", (object)pair.Value ?? DBNull.Value, DbType.String, size: -1));\n            }\n        }\n\n        public override void RemoveHash(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"delete H from [{schemaName}].Hash H with (forceseek) where [Key] = @key\");\n\n            AcquireHashLock(key);\n            AddCommand(_hashCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String));\n        }\n\n        public override void AddRangeToSet(string key, IList<string> items)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n            if (items == null) throw new ArgumentNullException(nameof(items));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].[Set] ([Key], Value, Score) values (@key, @value, 0.0)\");\n\n            AcquireSetLock(key);\n\n            foreach (var item in items)\n            {\n                AddCommand(_setCommands, key, batch => batch.Create(query)\n                    .AddParameter(\"@key\", key, DbType.String)\n                    .AddParameter(\"@value\", item, DbType.String, size: 256));\n            }\n        }\n\n        public override void RemoveSet(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"delete S from [{schemaName}].[Set] S with (forceseek) where [Key] = @key\");\n\n            AcquireSetLock(key);\n            AddCommand(_setCommands, key, batch => batch.Create(query).AddParameter(\"@key\", key, DbType.String));\n        }\n\n        public override void ExpireHash(string key, TimeSpan expireIn)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n             var query = _storage.GetQueryFromTemplate(static schemaName => $@\"\nupdate [{schemaName}].[Hash] set ExpireAt = @expireAt where [Key] = @key\");\n\n            AcquireHashLock(key);\n            AddCommand(_hashCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@expireAt\", DateTime.UtcNow.Add(expireIn), DbType.DateTime));\n        }\n\n        public override void ExpireSet(string key, TimeSpan expireIn)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName => $@\"\nupdate [{schemaName}].[Set] set ExpireAt = @expireAt where [Key] = @key\");\n\n            AcquireSetLock(key);\n            AddCommand(_setCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@expireAt\", DateTime.UtcNow.Add(expireIn), DbType.DateTime));\n        }\n\n        public override void ExpireList(string key, TimeSpan expireIn)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName => $@\"\nupdate [{schemaName}].[List] set ExpireAt = @expireAt where [Key] = @key\");\n\n            AcquireListLock(key);\n            AddCommand(_listCommands, key, batch => batch.Create(query)\n                .AddParameter(\"@key\", key, DbType.String)\n                .AddParameter(\"@expireAt\", DateTime.UtcNow.Add(expireIn), DbType.DateTime));\n        }\n\n        public override void PersistHash(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName => $@\"\nupdate [{schemaName}].Hash set ExpireAt = null where [Key] = @key\");\n\n            AcquireHashLock(key);\n            AddCommand(_hashCommands, key, batch => batch.Create(query).AddParameter(\"@key\", key, DbType.String));\n        }\n\n        public override void PersistSet(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName => $@\"\nupdate [{schemaName}].[Set] set ExpireAt = null where [Key] = @key\");\n\n            AcquireSetLock(key);\n            AddCommand(_setCommands, key, batch => batch.Create(query).AddParameter(\"@key\", key, DbType.String));\n        }\n\n        public override void PersistList(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName => $@\"\nupdate [{schemaName}].[List] set ExpireAt = null where [Key] = @key\");\n\n            AcquireListLock(key);\n            AddCommand(_listCommands, key, batch => batch.Create(query).AddParameter(\"@key\", key, DbType.String));\n        }\n\n        public override void RemoveFromQueue(IFetchedJob fetchedJob)\n        {\n            if (fetchedJob == null) throw new ArgumentNullException(nameof(fetchedJob));\n\n            if (fetchedJob is SqlServerTimeoutJob timeoutJob)\n            {\n                var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"delete JQ from [{schemaName}].JobQueue JQ with (forceseek, rowlock) where Queue = @queue and Id = @id and FetchedAt = @fetchedAt\");\n\n                AddCommand(_queueCommands, timeoutJob.Queue, batch => batch.Create(query)\n                    .AddParameter(\"@queue\", timeoutJob.Queue, DbType.String)\n                    .AddParameter(\"@id\", timeoutJob.Id, DbType.Int64)\n                    .AddParameter(\"@fetchedAt\", timeoutJob.FetchedAt, DbType.DateTime));\n\n                timeoutJob.SetTransaction(this);\n            }\n            else\n            {\n                throw new NotSupportedException(\n                    \"Only '\" + nameof(SqlServerTimeoutJob) + \"' type supports transactional acknowledge, '\" + fetchedJob.GetType().Name + \"' given.\");\n            }\n        }\n\n        private static void AppendBatch<TKey>(\n            SortedDictionary<TKey, List<Func<DbConnection, DbCommand>>> collection,\n            SqlCommandBatch batch)\n        {\n            foreach (var pair in collection)\n            {\n                foreach (var command in pair.Value)\n                {\n                    var dbCommand = command(batch.Connection);\n                    batch.Append(dbCommand);\n                }\n            }\n        }\n\n        private static void AddCommand<TKey>(\n            SortedDictionary<TKey, List<Func<DbConnection, DbCommand>>> collection,\n            TKey key,\n            Func<DbConnection, DbCommand> command)\n        {\n            if (!collection.TryGetValue(key, out var commands))\n            {\n                commands = new List<Func<DbConnection, DbCommand>>();\n                collection.Add(key, commands);\n            }\n\n            commands.Add(command);\n        }\n\n        private void AcquireListLock(string key)\n        {\n            AcquireLock(_storage.Options.DisableGlobalLocks ? $\"List:{key}\" : \"List\");\n        }\n\n        private void AcquireSetLock(string key)\n        {\n            AcquireLock(_storage.Options.DisableGlobalLocks ? $\"Set:{key}\" : \"Set\");\n        }\n\n        private void AcquireHashLock(string key)\n        {\n            AcquireLock(_storage.Options.DisableGlobalLocks ? $\"Hash:{key}\" : \"Hash\");\n        }\n\n        private void AcquireLock(string resource)\n        {\n            if (!_storage.Options.DisableGlobalLocks || _storage.Options.UseFineGrainedLocks)\n            {\n                _lockedResources.Add($\"{_storage.SchemaName}:{resource}:Lock\");\n            }\n        }\n\n        private void TrySignalListeningWorkers()\n        {\n            foreach (var queue in _queuesToSignal)\n            {\n                if (SqlServerJobQueue.NewItemInQueueEvents.TryGetValue(Tuple.Create(_storage, queue), out var signal))\n                {\n                    signal.Set();\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/TimestampHelper.cs",
    "content": "// This file is part of Hangfire. Copyright © 2021 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\n\nnamespace Hangfire.SqlServer\n{\n    internal static class TimestampHelper\n    {\n        public static long GetTimestamp()\n        {\n#if NETCOREAPP3_0\n            return Environment.TickCount64;\n#else\n            return Environment.TickCount;\n#endif\n        }\n\n        public static TimeSpan Elapsed(long timestamp)\n        {\n            var now = GetTimestamp();\n            return Elapsed(now, timestamp);\n        }\n\n        public static TimeSpan Elapsed(long now, long timestamp)\n        {\n#if NETCOREAPP3_0\n            return TimeSpan.FromMilliseconds(now - timestamp);\n#else\n            return TimeSpan.FromMilliseconds(unchecked((int)now - (int)timestamp));\n#endif\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \".NETFramework,Version=v4.5.1\": {\n      \"Dapper\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.60.6, )\",\n        \"resolved\": \"1.60.6\",\n        \"contentHash\": \"mmnJNhKMeF2KhvVXDoVQlFxre8aJAo71YBJrKqFlvuqzYC2QiXUq94/GCDBJzU7paq4GqpkV2glw3308TcGibw==\"\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net451\": \"1.0.3\"\n        }\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net451\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vVPinxdLrwoX81ApbNIHDBI6qymQEy8eSOxDNBgKJtc2+cifnF0oT1U2d3EFx+V5O68yaqna2myZJNsgKCpVkA==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      }\n    },\n    \".NETStandard,Version=v1.3\": {\n      \"Dapper\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.60.6, )\",\n        \"resolved\": \"1.60.6\",\n        \"contentHash\": \"mmnJNhKMeF2KhvVXDoVQlFxre8aJAo71YBJrKqFlvuqzYC2QiXUq94/GCDBJzU7paq4GqpkV2glw3308TcGibw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Collections.NonGeneric\": \"4.3.0\",\n          \"System.Data.SqlClient\": \"4.4.0\",\n          \"System.Dynamic.Runtime\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.4.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Xml.XmlDocument\": \"4.3.0\"\n        }\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.6.1, )\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.0.1\",\n        \"contentHash\": \"17h8b5mXa87XYKrrVqdgZ38JefSUqLChUQpXgSnpzsM0nDOhE40FTeNWOJ/YmySGV6tG6T8+hjz6vxbknHJr6A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Dynamic.Runtime\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Extensions\": \"4.0.1\",\n          \"System.Reflection.Primitives\": \"4.0.1\",\n          \"System.Reflection.TypeExtensions\": \"4.1.0\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.InteropServices\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"9.0.1\",\n        \"contentHash\": \"U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==\",\n        \"dependencies\": {\n          \"Microsoft.CSharp\": \"4.0.1\",\n          \"System.Collections\": \"4.0.11\",\n          \"System.Diagnostics.Debug\": \"4.0.11\",\n          \"System.Dynamic.Runtime\": \"4.0.11\",\n          \"System.Globalization\": \"4.0.11\",\n          \"System.IO\": \"4.1.0\",\n          \"System.Linq\": \"4.1.0\",\n          \"System.Linq.Expressions\": \"4.1.0\",\n          \"System.ObjectModel\": \"4.0.12\",\n          \"System.Reflection\": \"4.1.0\",\n          \"System.Reflection.Extensions\": \"4.0.1\",\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\",\n          \"System.Runtime.Extensions\": \"4.1.0\",\n          \"System.Runtime.Serialization.Primitives\": \"4.1.1\",\n          \"System.Text.Encoding\": \"4.0.11\",\n          \"System.Text.Encoding.Extensions\": \"4.0.11\",\n          \"System.Text.RegularExpressions\": \"4.1.0\",\n          \"System.Threading\": \"4.0.11\",\n          \"System.Threading.Tasks\": \"4.0.11\",\n          \"System.Xml.ReaderWriter\": \"4.0.11\",\n          \"System.Xml.XDocument\": \"4.0.11\"\n        }\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"A8v6PGmk+UGbfWo5Ixup0lPM4swuSwOiayJExZwKIOjTlFFQIsu3QnDXECosBEyrWSPryxBVrdqtJyhK3BaupQ==\",\n        \"dependencies\": {\n          \"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni\": \"4.4.0\",\n          \"runtime.win-x64.runtime.native.System.Data.SqlClient.sni\": \"4.4.0\",\n          \"runtime.win-x86.runtime.native.System.Data.SqlClient.sni\": \"4.4.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==\"\n      },\n      \"runtime.win-x64.runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==\"\n      },\n      \"runtime.win-x86.runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==\"\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.NonGeneric\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Data.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"lm6E3T5u7BOuEH0u18JpbJHxBfOJPuCyl4Kg1RH10ktYLp5uEEE1xKrHW56/We4SnZpGAuCc9N0MJpSDhTHZGQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Data.SqlClient\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"fxb9ghn1k1Ua7FFdlvtiBOD4/PsQvD/fk2KnhS+FK7VC6OggEx6P+lP1P0+KMb5V2dqS1+FbR7HCenoqzJMNIA==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\",\n          \"System.Data.Common\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.4.0\",\n          \"System.IO.Pipes\": \"4.3.0\",\n          \"System.Net.NameResolution\": \"4.3.0\",\n          \"System.Net.Security\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Security.Principal\": \"4.3.0\",\n          \"System.Security.Principal.Windows\": \"4.4.0\",\n          \"System.Text.Encoding.CodePages\": \"4.4.0\",\n          \"System.Threading.Thread\": \"4.3.0\",\n          \"System.Threading.ThreadPool\": \"4.3.0\",\n          \"runtime.native.System.Data.SqlClient.sni\": \"4.4.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"2SMt95w+TKf2fth4mSlqn1AVNSmFDQkdTVmUe6D/oP1atzVU0vxTb+iDP+IHNQB1qSbYkWNoPN55SaMsGUe68A==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Dynamic.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Pipes\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"wpGJuACA6r8+KRckXoI6ghGTwgPRiICI6T7kgHI/m7S5eMqV/8jH37fzAUhTwIe9RwlH/j1sWwm2Q2zyXwZGHw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Principal\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Overlapped\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Net.NameResolution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"AFYl08R7MrsrEjqpQWTZWBadqXyTzNDaWpMqyxhb0d6sGhV6xMDKueuBXlLL30gz+DIRY6MpdgnHWlCh5wmq9w==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Principal.Windows\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Security\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"IgJKNfALqw7JRWp5LMQ5SWHNKvXVz094U6wNE3c1i8bOkMQvgBL+MMQuNt3xl9Qg9iWpj3lFxPZEY6XHmROjMQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Claims\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Security.Principal\": \"4.3.0\",\n          \"System.Security.Principal.Windows\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.ThreadPool\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"dkmh/ySlwnXJp/1qYP9uyKkCK1CXR/REFzl7abHcArxBcV91mY2CgrrzSRA5Z/X4MevJWwXsklGRdR3A7K9zbg==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Serialization.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.1.1\",\n        \"contentHash\": \"HZ6Du5QrTG8MNJbf4e4qMO3JRAkIboGT5Fk804uZtg3Gq516S7hAqTm2UZKUHa7/6HUGdVy3AqMQKbns06G/cg==\",\n        \"dependencies\": {\n          \"System.Resources.ResourceManager\": \"4.0.1\",\n          \"System.Runtime\": \"4.1.0\"\n        }\n      },\n      \"System.Security.Claims\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"P/+BR/2lnc4PNDHt/TPBAWHVMLMRHsyYZbU1NphW4HIWzCggz8mJbTQQ3MKljFE7LS3WagmVFuBgoLcFzYXlkA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Security.Principal\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Principal\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Principal.Windows\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"pP+AOzt1o3jESOuLmf52YQTF7H3Ng9hTnrOESQiqsnl2IbBh1HInsAMHYtoh75iUYV0OIkHmjvveraYB6zM97w==\",\n        \"dependencies\": {\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Claims\": \"4.3.0\",\n          \"System.Security.Principal\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.CodePages\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"6JX7ZdaceBiLKLkYt8zJcp4xTJd1uYyXXEkPw6mnlUIjh1gZPIVKPtRXPmY5kLf6DwZmf5YLwR3QUrRonl7l0A==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Overlapped\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m3HQ2dPiX/DSTpf+yJt8B0c+SRvzfqAJKx+QDWi+VLhz8svLT23MVjEOHPF/KiSLeArKU/iHescrbLd3yVgyNg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Thread\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OHmbT+Zz065NKII/ZHcH9XO1dEuLGI1L2k7uYss+9C1jLxTC9kTZZuzUOyXHayRk+dft9CiDf3I/QZ0t8JKyBQ==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.ThreadPool\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"k/+g4b7vjdd4aix83sTgC9VG6oXYKAktSfNIJUNGxPEj7ryEOfzHHhfnmsZvjxawwcD9HyWXKCXmPjX8U4zeSw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XmlDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"NETStandard.Library\": \"[1.6.1, )\",\n          \"Newtonsoft.Json\": \"[9.0.1, )\",\n          \"System.Threading.Thread\": \"[4.0.0, )\",\n          \"System.Threading.ThreadPool\": \"[4.0.10, )\"\n        }\n      }\n    },\n    \".NETStandard,Version=v2.0\": {\n      \"Dapper\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.1.28, )\",\n        \"resolved\": \"2.1.28\",\n        \"contentHash\": \"ha49pzOEDmCPkMxwfPSR/wxa/6RD3r42TESIgpzpi7FXq/gNVUuJVEO+wtUzntNRhtmq3BKCl0s0aAlSZLkBUA==\",\n        \"dependencies\": {\n          \"System.Reflection.Emit.Lightweight\": \"4.7.0\"\n        }\n      },\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.0.3, )\",\n        \"resolved\": \"2.0.3\",\n        \"contentHash\": \"st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"vvVR/B08YVghQ4jHEloxqw2ZWzEGE1AOA5E0DioUM3ujbXz6FD3AfB/0Jl2ohJPd0nXYGwmPe1En6HTsSriq1A==\"\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"11.0.1\",\n        \"contentHash\": \"pNN4l+J6LlpIvHOeNdXlwxv39NPJ2B5klz+Rd2UQZIx30Squ5oND1Yy3wEAUoKn0GPUj6Yxt9lxlYWQqfZcvKg==\"\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.7.0\",\n        \"contentHash\": \"AucBYo3DSI0IDxdUjKksBcQJXPHyoPyrCXYURW1WDsLI4M65Ar/goSHjdnHOAY9MiYDNKqDlIgaYm+zL2hA1KA==\"\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.7.0\",\n        \"contentHash\": \"a4OLB4IITxAXJeV74MDx49Oq2+PsF6Sml54XAFv+2RyWwtDBcabzoxiiJRhdhx+gaohLh4hEGCLQyBozXoQPqA==\",\n        \"dependencies\": {\n          \"System.Reflection.Emit.ILGeneration\": \"4.7.0\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.CSharp\": \"[4.4.0, )\",\n          \"Newtonsoft.Json\": \"[11.0.1, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/Hangfire.SqlServer.Msmq.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n  <PropertyGroup>\r\n    <TargetFramework>net451</TargetFramework>\r\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\r\n    <RootNamespace>Hangfire.SqlServer.Msmq</RootNamespace>\r\n  </PropertyGroup>\r\n  \r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\Hangfire.Core\\Hangfire.Core.csproj\" />\r\n    <ProjectReference Include=\"..\\Hangfire.SqlServer\\Hangfire.SqlServer.csproj\" />\r\n    <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies\" Version=\"1.0.3\" PrivateAssets=\"all\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <Reference Include=\"System.Messaging\" />\r\n    <Reference Include=\"System.Transactions\" />\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/IMsmqTransaction.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Messaging;\n\nnamespace Hangfire.SqlServer.Msmq\n{\n    internal interface IMsmqTransaction : IDisposable\n    {\n        Message Receive(MessageQueue queue, TimeSpan timeout);\n\n        void Commit();\n        void Abort();\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/MessageQueueExtensions.cs",
    "content": "﻿// The MIT License (MIT)\n// \n// Copyright (c) 2014 Philip Hoppe\n// \n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n\nusing System;\nusing System.ComponentModel;\nusing System.Messaging;\nusing System.Runtime.InteropServices;\nusing System.Text.RegularExpressions;\n\n// ReSharper disable InconsistentNaming\n// ReSharper disable UnusedMember.Local\n// ReSharper disable FieldCanBeMadeReadOnly.Local\n// ReSharper disable MemberCanBePrivate.Local\n// ReSharper disable RedundantCast\n// ReSharper disable CheckNamespace\n\nnamespace MQTools\n{\n    internal static class MessageQueueExtensions\n    {\n        #region P/Invoke stuff\n\n        [DllImport(\"mqrt.dll\")]\n        private static extern int MQMgmtGetInfo(\n            [MarshalAs(UnmanagedType.BStr)]string computerName,\n            [MarshalAs(UnmanagedType.BStr)]string objectName,\n            ref MQMGMTPROPS mgmtProps);\n\n        private const byte VT_NULL = 1;\n        private const byte VT_UI4 = 19;\n        private const int PROPID_MGMT_QUEUE_MESSAGE_COUNT = 7;\n\n        //size must be 16\n        [StructLayout(LayoutKind.Sequential)]\n        private struct MQPROPVariant\n        {\n            public byte vt;       //0\n            public byte spacer;   //1\n            public short spacer2; //2\n            public int spacer3;   //4\n            public uint ulVal;    //8\n            public int spacer4;   //12\n        }\n\n        //size must be 16 in x86 and 28 in x64\n        [StructLayout(LayoutKind.Sequential)]\n        private struct MQMGMTPROPS\n        {\n            public uint cProp;\n            public IntPtr aPropID;\n            public IntPtr aPropVar;\n            public IntPtr status;\n        }\n\n        #endregion\n\n        private const int MQ_ERROR = unchecked((int)0xC00E0001); // A non-specific Message Queuing error was generated. For example, information about a queue that is currently not the active queue was requested.\n        private const int MQ_ERROR_ACCESS_DENIED = unchecked((int)0xC00E0025); // The access rights for retrieving information about the applicable msmq (MSMQ-Configuration) or queue object are not allowed for the calling process.\n        private const int MQ_ERROR_ILLEGAL_FORMATNAME = unchecked((int)0xC00E001E); // The specified format name in pObjectName is illegal.\n        private const int MQ_ERROR_ILLEGAL_PROPERTY_VT = unchecked((int)0xC00E0019); // An invalid type indicator was supplied for one of the properties specified in pMgmtProps.\n        private const int MQ_ERROR_QUEUE_NOT_ACTIVE = unchecked((int)0xC00E0004); // The queue is not open or may not exist.\n        private const int MQ_ERROR_SERVICE_NOT_AVAILABLE = unchecked((int)0xC00E000B); // The Message Queuing service is not available.\n        // ReSharper disable once RedundantOverflowCheckingContext\n        private const int MQ_INFORMATION_UNSUPPORTED_PROPERTY = unchecked((int)0x400E0004); // An unsupported property identifier was specified in pMgmtProps\n\n        const string QueueRegex = @\"^(?:(.*\\:)|)((?<computerName>[^\\\\]*)|\\.)(?:\\\\(?<queueType>.*)|)\\\\(?<queue>.*)$\";\n        private static readonly Regex regex = new Regex(QueueRegex, RegexOptions.Compiled | RegexOptions.IgnoreCase, TimeSpan.FromSeconds(5));\n\n        public static long GetCount(this MessageQueue messageQueue)\n        {\n            var match = GetQueuePathMatch(messageQueue.Path);\n\n            var computerName = match.Groups[\"computerName\"].Value;\n            var queueType = match.Groups[\"queueType\"].Value;\n            var queue = match.Groups[\"queue\"].Value;\n\n            if (computerName == \".\")\n                computerName = null;\n\n            return GetQueueCount(computerName, queueType, queue);\n        }\n\n        internal static Match GetQueuePathMatch(string queuePath)\n        {\n            var matches = regex.Matches(queuePath);\n            if (matches.Count != 1)\n            {\n                throw new InvalidOperationException($\"Unable to parse queue path '{queuePath}'\");\n            }\n\n            return matches[0];\n        }\n\n        private static long GetQueueCount(string computerName, string queueType, string queue)\n        {\n            if (string.IsNullOrEmpty(computerName)) computerName = null;\n            string queuePath = $\"queue=Direct=OS:{computerName ?? \".\"}\";\n\n            if (!String.IsNullOrEmpty(queueType))\n            {\n                queuePath += $\"\\\\{queueType}\";\n            }\n\n            queuePath += $\"\\\\{queue}\";\n\n            return GetCount(computerName, queuePath);\n        }\n\n        private static long GetCount(string computerName, string queuePath)\n        {\n            var props = new MQMGMTPROPS\n            {\n                cProp = 1,\n                aPropID = Marshal.AllocHGlobal(sizeof(int)),\n                aPropVar = Marshal.AllocHGlobal(Marshal.SizeOf<MQPROPVariant>()),\n                status = Marshal.AllocHGlobal(sizeof(int))\n            };\n\n            Marshal.WriteInt32(props.aPropID, PROPID_MGMT_QUEUE_MESSAGE_COUNT);\n            Marshal.StructureToPtr(new MQPROPVariant { vt = VT_NULL }, props.aPropVar, false);\n            Marshal.WriteInt32(props.status, 0);\n\n            try\n            {\n                int result = MQMgmtGetInfo(computerName, queuePath, ref props);\n                //Console.WriteLine(\"{0} {1} Result:{2:X}\", computerName, queuePath, result);\n                switch (result)\n                {\n                    case 0:\n                        break;\n                    case MQ_ERROR_QUEUE_NOT_ACTIVE:\n                        return 0;\n                    default:\n                        throw new Win32Exception(result);\n                }\n\n                if (Marshal.ReadInt32(props.status) != 0)\n                    return -1;\n\n                var variant = Marshal.PtrToStructure<MQPROPVariant>(props.aPropVar);\n                if (variant.vt != VT_UI4)\n                    return -2;\n\n                return variant.ulVal;\n            }\n            finally\n            {\n                Marshal.FreeHGlobal(props.aPropID);\n                Marshal.FreeHGlobal(props.aPropVar);\n                Marshal.FreeHGlobal(props.status);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/MsmqDtcTransaction.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Messaging;\nusing System.Transactions;\n\nnamespace Hangfire.SqlServer.Msmq\n{\n    internal sealed class MsmqDtcTransaction : IMsmqTransaction\n    {\n        private readonly TransactionScope _scope;\n        private TransactionScope _suppressedScope;\n\n        public MsmqDtcTransaction()\n        {\n            _scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.Zero);\n        }\n\n        public void Dispose()\n        {\n            if (_suppressedScope != null)\n            {\n                _suppressedScope.Complete();\n                _suppressedScope.Dispose();\n            }\n\n            _scope.Dispose();\n        }\n\n        public Message Receive(MessageQueue queue, TimeSpan timeout)\n        {\n            var message = queue.Receive(timeout, MessageQueueTransactionType.Automatic);\n            _suppressedScope = new TransactionScope(TransactionScopeOption.Suppress, TimeSpan.Zero);\n\n            return message;\n        }\n\n        public void Commit()\n        {\n            _scope.Complete();\n        }\n\n        public void Abort()\n        {\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/MsmqExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing Hangfire.SqlServer;\nusing Hangfire.SqlServer.Msmq;\nusing Hangfire.States;\n\n// ReSharper disable once CheckNamespace\nnamespace Hangfire\n{\n    public static class MsmqExtensions\n    {\n        public static IGlobalConfiguration<SqlServerStorage> UseMsmqQueues(\n            this IGlobalConfiguration<SqlServerStorage> configuration,\n            string pathPattern, \n            params string[] queues)\n        {\n            return UseMsmqQueues(configuration, MsmqTransactionType.Internal, pathPattern, queues);\n        }\n\n        public static IGlobalConfiguration<SqlServerStorage> UseMsmqQueues(\n            this IGlobalConfiguration<SqlServerStorage> configuration,\n            MsmqTransactionType transactionType,\n            string pathPattern,\n            params string[] queues)\n        {\n            if (queues.Length == 0)\n            {\n                queues = new[] { EnqueuedState.DefaultQueue };\n            }\n\n            var provider = new MsmqJobQueueProvider(pathPattern, queues, transactionType);\n            configuration.Entry.QueueProviders.Add(provider, queues);\n\n            return configuration;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/MsmqFetchedJob.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Hangfire.Storage;\n\nnamespace Hangfire.SqlServer.Msmq\n{\n    internal sealed class MsmqFetchedJob : IFetchedJob\n    {\n        private readonly IMsmqTransaction _transaction;\n\n        public MsmqFetchedJob([NotNull] IMsmqTransaction transaction, [NotNull] string jobId)\n        {\n            if (transaction == null) throw new ArgumentNullException(nameof(transaction));\n            if (jobId == null) throw new ArgumentNullException(nameof(jobId));\n\n            _transaction = transaction;\n\n            JobId = jobId;\n        }\n\n        public string JobId { get; }\n\n        public void RemoveFromQueue()\n        {\n            _transaction.Commit();\n        }\n\n        public void Requeue()\n        {\n            _transaction.Abort();\n        }\n\n        public void Dispose()\n        {\n            _transaction.Dispose();\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/MsmqInternalTransaction.cs",
    "content": "// This file is part of Hangfire. Copyright © 2015 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Messaging;\n\nnamespace Hangfire.SqlServer.Msmq\n{\n    internal sealed class MsmqInternalTransaction : IMsmqTransaction\n    {\n        private readonly MessageQueueTransaction _transaction;\n\n        public MsmqInternalTransaction()\n        {\n            _transaction = new MessageQueueTransaction();\n        }\n\n        public void Dispose()\n        {\n            _transaction.Dispose();\n        }\n\n        public Message Receive(MessageQueue queue, TimeSpan timeout)\n        {\n            _transaction.Begin();\n            return queue.Receive(timeout, _transaction);\n        }\n\n        public void Commit()\n        {\n            _transaction.Commit();\n        }\n\n        public void Abort()\n        {\n            _transaction.Abort();\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/MsmqJobQueue.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Data;\nusing System.Globalization;\nusing System.Messaging;\nusing System.Threading;\nusing Hangfire.Storage;\n\nnamespace Hangfire.SqlServer.Msmq\n{\n    internal sealed class MsmqJobQueue : IPersistentJobQueue\n    {\n        private static readonly TimeSpan SyncReceiveTimeout = TimeSpan.FromSeconds(5);\n\n        private readonly string _pathPattern;\n        private readonly MsmqTransactionType _transactionType;\n\n        public MsmqJobQueue(string pathPattern, MsmqTransactionType transactionType)\n        {\n            if (pathPattern == null) throw new ArgumentNullException(nameof(pathPattern));\n\n            _pathPattern = pathPattern;\n            _transactionType = transactionType;\n        }\n\n        public IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken)\n        {\n            string jobId = null;\n            var queueIndex = 0;\n\n            while (!cancellationToken.IsCancellationRequested)\n            {\n                var transaction = CreateTransaction();\n                \n                try\n                {\n                    using (var messageQueue = GetMessageQueue(queues[queueIndex]))\n                    {\n                        var message = queueIndex == queues.Length - 1\n                            ? transaction.Receive(messageQueue, SyncReceiveTimeout)\n                            : transaction.Receive(messageQueue, new TimeSpan(1));\n\n                        jobId = message.Label;\n\n                        return new MsmqFetchedJob(transaction, jobId);\n                    }\n                }\n                catch (MessageQueueException ex) when (ex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)\n                {\n                    // Receive timeout occurred, we should just switch to the next queue\n                }\n                finally\n                {\n                    if (jobId == null)\n                    {\n                        transaction.Dispose();\n                    }\n                }\n\n                queueIndex = (queueIndex + 1) % queues.Length;\n            }\n\n            cancellationToken.ThrowIfCancellationRequested();\n            return null;\n        }\n\n        public void Enqueue(IDbConnection connection, string queue, string jobId)\n        {\n            using (var messageQueue = GetMessageQueue(queue))\n            using (var message = new Message { Label = jobId })\n            using (var transaction = new MessageQueueTransaction())\n            {\n                transaction.Begin();\n                messageQueue.Send(message, transaction);\n                transaction.Commit();\n            }\n        }\n\n        private IMsmqTransaction CreateTransaction()\n        {\n            switch (_transactionType)\n            {\n                case MsmqTransactionType.Internal:\n                    return new MsmqInternalTransaction();\n                case MsmqTransactionType.Dtc:\n                    return new MsmqDtcTransaction();\n            }\n\n            throw new InvalidOperationException(\"Unknown MSMQ transaction type: \" + _transactionType);\n        }\n\n        private MessageQueue GetMessageQueue(string queue)\n        {\n            return new MessageQueue(String.Format(CultureInfo.InvariantCulture, _pathPattern, queue));\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/MsmqJobQueueMonitoringApi.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Messaging;\nusing MQTools;\n\nnamespace Hangfire.SqlServer.Msmq\n{\n    internal sealed class MsmqJobQueueMonitoringApi : IPersistentJobQueueMonitoringApi\n    {\n        private readonly string _pathPattern;\n        private readonly IEnumerable<string> _queues;\n\n        public MsmqJobQueueMonitoringApi(string pathPattern, IEnumerable<string> queues)\n        {\n            if (pathPattern == null) throw new ArgumentNullException(nameof(pathPattern));\n            if (queues == null) throw new ArgumentNullException(nameof(queues));\n\n            _pathPattern = pathPattern;\n            _queues = queues;\n        }\n\n        public IEnumerable<string> GetQueues()\n        {\n            return _queues;\n        }\n\n        public IEnumerable<long> GetEnqueuedJobIds(string queue, int @from, int perPage)\n        {\n            var result = new List<long>();\n\n            using (var messageQueue = new MessageQueue(String.Format(CultureInfo.InvariantCulture, _pathPattern, queue)))\n            {\n                var current = 0;\n                var end = from + perPage;\n                var enumerator = messageQueue.GetMessageEnumerator2();\n\n                while (enumerator.MoveNext())\n                {\n                    if (current >= from && current < end)\n                    {\n                        var message = enumerator.Current;\n                        if (message == null) continue;\n\n                        result.Add(long.Parse(message.Label, CultureInfo.InvariantCulture));\n                    }\n\n                    if (current >= end) break;\n\n                    current++;\n                }\n            }\n\n            return result;\n        }\n\n        public IEnumerable<long> GetFetchedJobIds(string queue, int @from, int perPage)\n        {\n            return Enumerable.Empty<long>();\n        }\n\n        public EnqueuedAndFetchedCountDto GetEnqueuedAndFetchedCount(string queue)\n        {\n            using (var messageQueue = new MessageQueue(String.Format(CultureInfo.InvariantCulture, _pathPattern, queue)))\n            {                \n                return new EnqueuedAndFetchedCountDto\n                {\n                    EnqueuedCount = (int?)messageQueue.GetCount()\n                };\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/MsmqJobQueueProvider.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System.Collections.Generic;\n\nnamespace Hangfire.SqlServer.Msmq\n{\n    internal sealed class MsmqJobQueueProvider : IPersistentJobQueueProvider\n    {\n        private readonly MsmqJobQueue _jobQueue;\n        private readonly MsmqJobQueueMonitoringApi _monitoringApi;\n\n        public MsmqJobQueueProvider(string pathPattern, IEnumerable<string> queues, MsmqTransactionType transactionType)\n        {\n            _jobQueue = new MsmqJobQueue(pathPattern, transactionType);\n            _monitoringApi = new MsmqJobQueueMonitoringApi(pathPattern, queues);\n        }\n\n        public IPersistentJobQueue GetJobQueue()\n        {\n            return _jobQueue;\n        }\n\n        public IPersistentJobQueueMonitoringApi GetJobQueueMonitoringApi()\n        {\n            return _monitoringApi;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/MsmqSqlServerStorageExtensions.cs",
    "content": "﻿// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nusing System;\nusing Hangfire.Annotations;\nusing Hangfire.States;\n\nnamespace Hangfire.SqlServer.Msmq\n{\n    public static class MsmqSqlServerStorageExtensions\n    {\n        public static SqlServerStorage UseMsmqQueues(\n            [NotNull] this SqlServerStorage storage,\n            [NotNull] string pathPattern)\n        {\n            return UseMsmqQueues(storage, pathPattern, EnqueuedState.DefaultQueue);\n        }\n\n        public static SqlServerStorage UseMsmqQueues(\n            [NotNull] this SqlServerStorage storage,\n            [NotNull] string pathPattern,\n            params string[] queues)\n        {\n            return UseMsmqQueues(storage, MsmqTransactionType.Internal, pathPattern, queues);\n        }\n\n        public static SqlServerStorage UseMsmqQueues(\n            [NotNull] this SqlServerStorage storage, \n            MsmqTransactionType transactionType,\n            [NotNull] string pathPattern, \n            params string[] queues)\n        {\n            if (storage == null) throw new ArgumentNullException(nameof(storage));\n\n            if (queues.Length == 0)\n            {\n                queues = new[] { EnqueuedState.DefaultQueue }; \n            }\n\n            var provider = new MsmqJobQueueProvider(pathPattern, queues, transactionType);\n            storage.QueueProviders.Add(provider, queues);\n\n            return storage;\n        }\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/MsmqTransactionType.cs",
    "content": "// This file is part of Hangfire. Copyright © 2013-2014 Hangfire OÜ.\n// \n// Hangfire is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Lesser General Public License as \n// published by the Free Software Foundation, either version 3 \n// of the License, or any later version.\n// \n// Hangfire is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU Lesser General Public License for more details.\n// \n// You should have received a copy of the GNU Lesser General Public \n// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.\n\nnamespace Hangfire.SqlServer.Msmq\n{\n    public enum MsmqTransactionType\n    {\n        /// <summary>\n        /// Internal (MSMQ) transaction will be used to fetch pending background\n        /// jobs, does not support remote queues.\n        /// </summary>\n        Internal,\n\n        /// <summary>\n        /// External (DTC) transaction will be used to fetch pending background\n        /// jobs. Supports remote queues, but requires running MSDTC Service.\n        /// </summary>\n        Dtc\n    }\n}"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/Properties/AssemblyInfo.cs",
    "content": "﻿using System;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n[assembly: AssemblyTitle(\"Hangfire.SqlServer.MSMQ\")]\n[assembly: AssemblyDescription(\"Hangfire MSMQ job queue for SQL Server storage implementation\")]\n[assembly: Guid(\"03092b5c-0dfc-4c6c-8422-556bd1cb291e\")]\n[assembly: CLSCompliant(true)]\n\n[assembly: InternalsVisibleTo(\"Hangfire.SqlServer.Msmq.Tests\")]\n// Allow the generation of mocks for internal types\n[assembly: InternalsVisibleTo(\"DynamicProxyGenAssembly2\")]"
  },
  {
    "path": "src/Hangfire.SqlServer.Msmq/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \".NETFramework,Version=v4.5.1\": {\n      \"Microsoft.CodeAnalysis.NetAnalyzers\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.0, )\",\n        \"resolved\": \"9.0.0\",\n        \"contentHash\": \"JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net451\": \"1.0.3\"\n        }\n      },\n      \"Microsoft.SourceLink.GitHub\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[8.0.0, )\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==\",\n        \"dependencies\": {\n          \"Microsoft.Build.Tasks.Git\": \"8.0.0\",\n          \"Microsoft.SourceLink.Common\": \"8.0.0\"\n        }\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Dapper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.60.6\",\n        \"contentHash\": \"mmnJNhKMeF2KhvVXDoVQlFxre8aJAo71YBJrKqFlvuqzYC2QiXUq94/GCDBJzU7paq4GqpkV2glw3308TcGibw==\"\n      },\n      \"Microsoft.Build.Tasks.Git\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net451\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vVPinxdLrwoX81ApbNIHDBI6qymQEy8eSOxDNBgKJtc2+cifnF0oT1U2d3EFx+V5O68yaqna2myZJNsgKCpVkA==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Microsoft.SourceLink.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==\"\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      },\n      \"hangfire.sqlserver\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Dapper\": \"[1.60.6, )\",\n          \"Hangfire.Core\": \"[1.0.0, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/SharedAssemblyInfo.cs",
    "content": "using System.Reflection;\nusing System.Runtime.InteropServices;\n\n[assembly: AssemblyProduct(\"Hangfire\")]\n[assembly: AssemblyCompany(\"Hangfire OÜ\")]\n[assembly: AssemblyCopyright(\"Copyright © 2013-2026 Hangfire OÜ\")]\n[assembly: AssemblyCulture(\"\")]\n\n[assembly: ComVisible(false)]\n\n// Please don't edit it manually, use the `build.bat version` command instead.\n[assembly: AssemblyVersion(\"1.8.23\")]\n"
  },
  {
    "path": "tests/Directory.Build.props",
    "content": "<Project>\n  <Import Project=\"$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))\" />\n  <PropertyGroup>\n    <NuGetAudit>false</NuGetAudit><!-- We are testing against these packages, not including them into the product -->\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.8.0\" />\n    <PackageReference Include=\"xunit\" Version=\"2.4.0\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.0\" />\n\n    <!-- Don't upgrade to 4.20 or higher, please refer to https://github.com/HangfireIO/Hangfire/issues/2266 -->\n    <PackageReference Include=\"Moq\" Version=\"4.10.0\" />\n  </ItemGroup>\n  \n  <ItemGroup>\n    <Service Include=\"{82a7f48d-3b50-4b1e-b82e-3ada8210c358}\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "tests/Hangfire.Core.Tests/BackgroundJobClientExtensionsFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests\n{\n    public class BackgroundJobClientExtensionsFacts\n    {\n        private const string JobId = \"job-id\";\n\n        private readonly Mock<IBackgroundJobClient> _client;\n        private readonly Mock<IState> _state;\n\n        public BackgroundJobClientExtensionsFacts()\n        {\n            _client = new Mock<IBackgroundJobClient>();\n            _state = new Mock<IState>();\n        }\n\n        [Fact]\n        public void StaticCreate_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.Create(\n                    null, () => StaticMethod(), _state.Object));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void StaticCreate_ShouldCreateAJobInTheGivenState()\n        {\n            _client.Object.Create(() => StaticMethod(), _state.Object);\n            \n            _client.Verify(x => x.Create(It.IsNotNull<Job>(), _state.Object));\n        }\n\n        [Fact]\n        public void InstanceCreate_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.Create<BackgroundJobClientExtensionsFacts>(\n                    null, x => x.InstanceMethod(), _state.Object));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void InstanceCreate_ShouldCreateAJobInTheGivenState()\n        {\n            _client.Object.Create<BackgroundJobClientExtensionsFacts>(x => x.InstanceMethod(), _state.Object);\n\n            _client.Verify(x => x.Create(It.IsNotNull<Job>(), _state.Object));\n        }\n\n        [Fact]\n        public void StaticEnqueue_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.Enqueue(\n                    null, () => StaticMethod()));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void StaticEnqueue_ShouldCreateAJobInTheEnqueueState()\n        {\n            _client.Object.Enqueue(() => StaticMethod());\n\n            _client.Verify(x => x.Create(It.IsNotNull<Job>(), It.IsAny<EnqueuedState>()));\n        }\n\n        [Fact]\n        public void InstanceEnqueue_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.Enqueue<BackgroundJobClientExtensionsFacts>(\n                    null, x => x.InstanceMethod()));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void InstanceEnqueue_ShouldCreateAJobInTheEnqueuedState()\n        {\n            _client.Object.Enqueue<BackgroundJobClientExtensionsFacts>(x => x.InstanceMethod());\n\n            _client.Verify(x => x.Create(It.IsNotNull<Job>(), It.IsAny<EnqueuedState>()));\n        }\n\n        [Fact]\n        public void StaticSchedule_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.Schedule(\n                    null, () => StaticMethod(), TimeSpan.FromDays(1)));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void StaticSchedule_ShouldCreateAJobInTheScheduledState()\n        {\n            _client.Object.Schedule(() => StaticMethod(), TimeSpan.FromDays(1));\n\n            _client.Verify(x => x.Create(\n                It.IsNotNull<Job>(),\n                It.Is<ScheduledState>(state => state.EnqueueAt > DateTime.UtcNow)));\n        }\n\n        [Fact]\n        public void StaticSchedule_WithDateTimeOffset_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.Schedule(\n                    null, () => StaticMethod(), DateTimeOffset.UtcNow));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void StaticSchedule_WithDateTimeOffset_ShouldCreateAJob_InTheScheduledState()\n        {\n            var now = DateTimeOffset.Now;\n\n            _client.Object.Schedule(() => StaticMethod(), now);\n\n            _client.Verify(x => x.Create(\n                It.IsNotNull<Job>(),\n                It.Is<ScheduledState>(state => state.EnqueueAt == now.UtcDateTime)));\n        }\n\n        [Fact]\n        public void InstanceSchedule_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.Schedule<BackgroundJobClientExtensionsFacts>(\n                    null, x => x.InstanceMethod(), TimeSpan.FromDays(1)));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void InstanceSchedule_ShouldCreateAJobInTheScheduledState()\n        {\n            _client.Object.Schedule<BackgroundJobClientExtensionsFacts>(\n                x => x.InstanceMethod(), TimeSpan.FromDays(1));\n\n            _client.Verify(x => x.Create(\n                It.IsNotNull<Job>(),\n                It.Is<ScheduledState>(state => state.EnqueueAt > DateTime.UtcNow)));\n        }\n\n        [Fact]\n        public void InstanceSchedule_WithDateTimeOffset_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.Schedule<BackgroundJobClientExtensionsFacts>(\n                    null, x => x.InstanceMethod(), DateTimeOffset.UtcNow));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void InstanceSchedule_WithDateTimeOffset_ShouldCreateAJobInTheScheduledState()\n        {\n            var now = DateTimeOffset.Now;\n\n            _client.Object.Schedule<BackgroundJobClientExtensionsFacts>(\n                x => x.InstanceMethod(),\n                now);\n\n            _client.Verify(x => x.Create(\n                It.IsNotNull<Job>(),\n                It.Is<ScheduledState>(state => state.EnqueueAt == now.UtcDateTime)));\n        }\n\n        [Fact]\n        public void ChangeState_WithoutFromState_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.ChangeState(null, \"job-id\", _state.Object));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void ChangeState_WithoutFromState_CallsItsOverload()\n        {\n            _client.Object.ChangeState(\"job-id\", _state.Object);\n\n            _client.Verify(x => x.ChangeState(\"job-id\", _state.Object, null));\n        }\n\n        [Fact]\n        public void Delete_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.Delete(null, JobId));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Delete_ChangesTheStateOfAJob_ToDeleted()\n        {\n            _client.Object.Delete(JobId);\n\n            _client.Verify(x => x.ChangeState(\n                JobId,\n                It.IsAny<DeletedState>(),\n                null));\n        }\n\n        [Fact]\n        public void Delete_WithFromState_ChangesTheStateOfAJob_ToDeletedWithFromStateValue()\n        {\n            _client.Object.Delete(JobId, FailedState.StateName);\n\n            _client.Verify(x => x.ChangeState(\n                JobId,\n                It.IsAny<DeletedState>(),\n                FailedState.StateName));\n        }\n\n        [Fact]\n        public void Requeue_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => BackgroundJobClientExtensions.Requeue(null, JobId, FailedState.StateName));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Requeue_ChangesTheStateOfAJob_ToEnqueued()\n        {\n            _client.Object.Requeue(JobId);\n\n            _client.Verify(x => x.ChangeState(JobId, It.IsAny<EnqueuedState>(), null));\n        }\n\n        [Fact]\n        public void Requeue_WithFromState_ChangesTheStateOfAJob_ToEnqueued_FromTheGivenState()\n        {\n            _client.Object.Requeue(JobId, FailedState.StateName);\n\n            _client.Verify(x => x.ChangeState(JobId, It.IsAny<EnqueuedState>(), FailedState.StateName));\n        }\n\n        [Fact]\n        public void Reschedule_ThrowsAnException_WhenClientIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n              () => BackgroundJobClientExtensions.Reschedule(null, JobId, TimeSpan.FromDays(1), FailedState.StateName));\n\n            Assert.Equal(\"client\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Reschedule_ChangesTheStateOfAJob_ToScheduled()\n        {\n            _client.Object.Reschedule(JobId, TimeSpan.FromDays(1));\n\n            _client.Verify(x => x.ChangeState(JobId, It.Is<ScheduledState>(state => state.EnqueueAt > DateTime.UtcNow), null));\n        }\n\n        [Fact]\n        public void Reschedule_WithFromState_ChangesTheStateOfAJob_ToScheduled_FromTheGivenState()\n        {\n            _client.Object.Reschedule(JobId, TimeSpan.FromDays(1), FailedState.StateName);\n\n            _client.Verify(x => x.ChangeState(JobId,  It.Is<ScheduledState>(state => state.EnqueueAt > DateTime.UtcNow), FailedState.StateName));\n        }\n\n        [Fact]\n        public void Reschedule_WithDateTimeOffset_ChangesTheStateOfAJob_ToScheduled()\n        {\n            var now = DateTimeOffset.Now;\n\n            _client.Object.Reschedule(JobId, now);\n\n            _client.Verify(x => x.ChangeState(JobId, It.Is<ScheduledState>(state => state.EnqueueAt == now.UtcDateTime), null));\n        }\n\n        [Fact]\n        public void Reschedule_WithDateTimeOffset_WithFromState_ChangesTheStateOfAJob_ToScheduled_FromTheGivenState()\n        {\n            var now = DateTimeOffset.Now;\n\n            _client.Object.Reschedule(JobId, now, FailedState.StateName);\n\n            _client.Verify(x => x.ChangeState(JobId,  It.Is<ScheduledState>(state => state.EnqueueAt == now.UtcDateTime), FailedState.StateName));\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void StaticMethod()\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        public void InstanceMethod()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/BackgroundJobClientFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n#pragma warning disable 618\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests\n{\n    public class BackgroundJobClientFacts\n    {\n        private readonly Mock<JobStorage> _storage;\n        private readonly Mock<IBackgroundJobFactory> _factory;\n        private readonly Mock<IState> _state;\n        private readonly Job _job;\n        private readonly Mock<IBackgroundJobStateChanger> _stateChanger;\n\n        public BackgroundJobClientFacts()\n        {\n            var connection = new Mock<IStorageConnection>();\n            _storage = new Mock<JobStorage>();\n            _storage.Setup(x => x.GetConnection()).Returns(connection.Object);\n\n            _stateChanger = new Mock<IBackgroundJobStateChanger>();\n            \n            _state = new Mock<IState>();\n            _state.Setup(x => x.Name).Returns(\"Mock\");\n            _job = Job.FromExpression(() => Method());\n\n            _factory = new Mock<IBackgroundJobFactory>();\n            _factory.Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .Returns(new BackgroundJob(\"some-job\", _job, DateTime.UtcNow));\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobClient(null, _factory.Object, _stateChanger.Object));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenFactoryIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobClient(_storage.Object, null, _stateChanger.Object));\n\n            Assert.Equal(\"factory\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStateChangerIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobClient(_storage.Object, _factory.Object, null));\n\n            Assert.Equal(\"stateChanger\", exception.ParamName);\n        }\n\n        [Fact, GlobalLock(Reason = \"Needs JobStorage.Current instance\")]\n        public void Ctor_UsesCurrent_JobStorageInstance_ByDefault()\n        {\n            JobStorage.Current = new Mock<JobStorage>().Object;\n            // ReSharper disable once ObjectCreationAsStatement\n            // Does not throw\n            new BackgroundJobClient();\n        }\n\n        [Fact]\n        public void CreateJob_ThrowsAnException_WhenJobIsNull()\n        {\n            var client = CreateClient();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => client.Create(null, _state.Object));\n\n            Assert.Equal(\"job\", exception.ParamName);\n        }\n\n        [Fact]\n        public void CreateJob_ThrowsAnException_WhenStateIsNull()\n        {\n            var client = CreateClient();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => client.Create(_job, null));\n\n            Assert.Equal(\"state\", exception.ParamName);\n        }\n\n        [Fact]\n        public void CreateJob_DelegatesBackgroundJobCreation_ToFactory()\n        {\n            var client = CreateClient();\n\n            client.Create(_job, _state.Object);\n\n            _factory.Verify(x => x.Create(It.IsNotNull<CreateContext>()));\n        }\n\n        [Fact]\n        public void CreateJob_ReturnsJobIdentifier()\n        {\n            var client = CreateClient();\n\n            var id = client.Create(_job, _state.Object);\n\n            Assert.Equal(\"some-job\", id);\n        }\n\n        [Fact]\n        public void CreateJob_WrapsOccurringExceptions_IntoItsOwnException()\n        {\n            var client = CreateClient();\n            _factory.Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .Throws<InvalidOperationException>();\n\n            var exception = Assert.Throws<BackgroundJobClientException>(\n                () => client.Create(_job, _state.Object));\n\n            Assert.NotNull(exception.InnerException);\n            Assert.IsType<InvalidOperationException>(exception.InnerException);\n        }\n\n        [Fact]\n        public void ChangeState_ThrowsAnException_WhenJobIdIsNull()\n        {\n            var client = CreateClient();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => client.ChangeState(null, _state.Object, null));\n\n            Assert.Equal(\"jobId\", exception.ParamName);\n        }\n\n        [Fact]\n        public void ChangeState_ThrowsAnException_WhenStateIsNull()\n        {\n            var client = CreateClient();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => client.ChangeState(\"jobId\", null, null));\n\n            Assert.Equal(\"state\", exception.ParamName);\n        }\n\n        [Fact]\n        public void ChangeState_ChangesTheStateOfAJob_ToTheGivenOne()\n        {\n            var client = CreateClient();\n\n            client.ChangeState(\"job-id\", _state.Object, null);\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.BackgroundJobId == \"job-id\" &&\n                ctx.NewState == _state.Object &&\n                ctx.ExpectedStates == null)));\n        }\n\n        [Fact]\n        public void ChangeState_WithFromState_ChangesTheStateOfAJob_WithFromStateValue()\n        {\n            var client = CreateClient();\n\n            client.ChangeState(\"job-id\", _state.Object, \"State\");\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.BackgroundJobId == \"job-id\" &&\n                ctx.NewState == _state.Object &&\n                ctx.ExpectedStates.SequenceEqual(new[] { \"State\" }))));\n        }\n\n        [Fact]\n        public void ChangeState_ReturnsTheResult_OfStateChangerInvocation()\n        {\n            _stateChanger.Setup(x => x.ChangeState(It.IsAny<StateChangeContext>()))\n                .Returns(_state.Object);\n            var client = CreateClient();\n\n            var result = client.ChangeState(\"job-id\", _state.Object, null);\n\n            Assert.True(result);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Method()\n        {\n        }\n\n        private BackgroundJobClient CreateClient()\n        {\n            return new BackgroundJobClient(_storage.Object, _factory.Object, _stateChanger.Object);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/BackgroundJobFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable PossibleNullReferenceException\n\nnamespace Hangfire.Core.Tests\n{\n    public class BackgroundJobFacts\n    {\n        private readonly Mock<IBackgroundJobClient> _client;\n\n        public BackgroundJobFacts()\n        {\n            _client = new Mock<IBackgroundJobClient>();\n        }\n        \n        [Fact, GlobalLock(Reason = \"Access BackgroundJob.ClientFactory member\")]\n        public void Enqueue_CreatesAJobInEnqueuedState()\n        {\n            Initialize();\n\n            BackgroundJob.Enqueue(() => Method());\n\n            _client.Verify(x => x.Create(It.IsNotNull<Job>(), It.IsAny<EnqueuedState>()));\n        }\n\n        [Fact, GlobalLock(Reason = \"Access BackgroundJob.ClientFactory member\")]\n        public void EnqueueGeneric_CreatesAJobInEnqueuedState()\n        {\n            Initialize();\n\n            BackgroundJob.Enqueue<BackgroundJobFacts>(x => x.Method());\n\n            _client.Verify(x => x.Create(It.IsNotNull<Job>(), It.IsAny<EnqueuedState>()));\n        }\n\n        [Fact, GlobalLock(Reason = \"Access BackgroundJob.ClientFactory member\")]\n        public void Schedule_WithTimeSpan_CreatesAJobInScheduledState()\n        {\n            Initialize();\n\n            BackgroundJob.Schedule(() => Method(), TimeSpan.FromDays(1));\n\n            _client.Verify(x => x.Create(\n                It.IsNotNull<Job>(),\n                It.Is<ScheduledState>(state => state.EnqueueAt > DateTime.UtcNow)));\n        }\n\n        [Fact, GlobalLock(Reason = \"Access BackgroundJob.ClientFactory member\")]\n        public void Schedule_WithDateTimeOffset_CreatesAJobInScheduledState()\n        {\n            Initialize();\n\n            BackgroundJob.Schedule(() => Method(), DateTimeOffset.Now);\n\n            _client.Verify(x => x.Create(\n                It.IsNotNull<Job>(),\n                It.IsNotNull<ScheduledState>()));\n        }\n\n        [Fact, GlobalLock(Reason = \"Access BackgroundJob.ClientFactory member\")]\n        public void ScheduleGeneric_WithTimeSpan_CreatesAJobInScheduledState()\n        {\n            Initialize();\n\n            BackgroundJob.Schedule<BackgroundJobFacts>(x => Method(), TimeSpan.FromDays(1));\n\n            _client.Verify(x => x.Create(\n                It.IsNotNull<Job>(),\n                It.Is<ScheduledState>(state => state.EnqueueAt > DateTime.UtcNow)));\n        }\n\n        [Fact, GlobalLock(Reason = \"Access BackgroundJob.ClientFactory member\")]\n        public void ScheduleGeneric_WithDateTimeOffset_CreatesAJobInScheduledState()\n        {\n            Initialize();\n\n            BackgroundJob.Schedule<BackgroundJobFacts>(x => x.Method(), DateTimeOffset.Now);\n\n            _client.Verify(x => x.Create(\n                It.IsNotNull<Job>(),\n                It.IsNotNull<ScheduledState>()));\n        }\n\n        [Fact, GlobalLock]\n        public void Delete_ChangesStateOfAJobToDeleted()\n        {\n            Initialize();\n\n            BackgroundJob.Delete(\"job-id\");\n\n            _client.Verify(x => x.ChangeState(\n                \"job-id\",\n                It.IsAny<DeletedState>(),\n                null));\n        }\n\n        [Fact, GlobalLock]\n        public void Delete_WithFromState_ChangesStateOfAJobToDeleted_WithFromState()\n        {\n            Initialize();\n\n            BackgroundJob.Delete(\"job-id\", FailedState.StateName);\n\n            _client.Verify(x => x.ChangeState(\n                \"job-id\",\n                It.IsAny<DeletedState>(),\n                FailedState.StateName));\n        }\n\n        [Fact, GlobalLock]\n        public void Requeue_ChangesStateOfAJobToEnqueued()\n        {\n            Initialize();\n\n            BackgroundJob.Requeue(\"job-id\");\n\n            _client.Verify(x => x.ChangeState(\n                \"job-id\",\n                It.IsAny<EnqueuedState>(),\n                null));\n        }\n\n        [Fact, GlobalLock]\n        public void Requeue_WithFromState_ChangesStateOfAJobToEnqueued_WithFromState()\n        {\n            Initialize();\n\n            BackgroundJob.Requeue(\"job-id\", FailedState.StateName);\n\n            _client.Verify(x => x.ChangeState(\n                \"job-id\",\n                It.IsAny<EnqueuedState>(),\n                FailedState.StateName));\n        }\n\n        [Fact, GlobalLock]\n        public void Reschedule_WithTimeSpan_ChangesStateOfAJobToScheduled()\n        {\n            Initialize();\n\n            BackgroundJob.Reschedule(\"job-id\", TimeSpan.FromDays(1));\n\n            _client.Verify(x => x.ChangeState(\n              \"job-id\",\n              It.Is<ScheduledState>(state => state.EnqueueAt > DateTime.UtcNow),\n              null));\n        }\n\n        [Fact, GlobalLock]\n        public void Reschedule_WithTimeSpan_WithFromState_ChangesStateOfAJobToScheduled_WithFromState()\n        {\n            Initialize();\n\n            BackgroundJob.Reschedule(\"job-id\", TimeSpan.FromDays(1), FailedState.StateName);\n\n            _client.Verify(x => x.ChangeState(\n              \"job-id\",\n              It.Is<ScheduledState>(state => state.EnqueueAt > DateTime.UtcNow),\n              FailedState.StateName));\n        }\n\n        [Fact, GlobalLock]\n        public void Reschedule_WithDateTimeOffset_ChangesStateOfAJobToScheduled()\n        {\n            var now = DateTimeOffset.Now;\n\n            Initialize();\n\n            BackgroundJob.Reschedule(\"job-id\", now);\n\n            _client.Verify(x => x.ChangeState(\n              \"job-id\",\n              It.Is<ScheduledState>(state => state.EnqueueAt == now.UtcDateTime),\n              null));\n        }\n\n        [Fact, GlobalLock]\n        public void Reschedule_WithDateTimeOffset_WithFromState_ChangesStateOfAJobToScheduled_WithFromState()\n        {\n            var now = DateTimeOffset.Now;\n\n            Initialize();\n\n            BackgroundJob.Reschedule(\"job-id\", now, FailedState.StateName);\n\n            _client.Verify(x => x.ChangeState(\n              \"job-id\",\n              It.Is<ScheduledState>(state => state.EnqueueAt == now.UtcDateTime),\n              FailedState.StateName));\n        }\n\n        [Fact, GlobalLock(Reason = \"Accesses to BJ.ClientFactory, JS.Current\")]\n        public void ClientFactory_HasDefaultValue_ThatReturns()\n        {\n            BackgroundJob.ClientFactory = null;\n            JobStorage.Current = new Mock<JobStorage>().Object;\n\n            var client = BackgroundJob.ClientFactory();\n            Assert.NotNull(client);\n        }\n\n        private void Initialize()\n        {\n            BackgroundJob.ClientFactory = () => _client.Object;\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public void Method() { }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/BackgroundJobServerFacts.cs",
    "content": "﻿namespace Hangfire.Core.Tests\n{\n    public class BackgroundJobServerFacts\n    {\n        /*private readonly Mock<JobStorage> _storage;\n        private readonly Mock<IServerSupervisor> _supervisor;\n        private readonly Mock<BackgroundJobServer> _serverMock;\n        private readonly BackgroundJobServerOptions _options;\n\n        public BackgroundJobServerFacts()\n        {\n            _storage = new Mock<JobStorage>();\n            _options = new BackgroundJobServerOptions();\n\n            _supervisor = new Mock<IServerSupervisor>();\n            _serverMock = new Mock<BackgroundJobServer>(_options, _storage.Object)\n            {\n                CallBase = true\n            };\n            _serverMock.Setup(x => x.GetBootstrapTask()).Returns(_supervisor.Object);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenOptionsValueIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobServer(null, _storage.Object));\n\n            Assert.Equal(\"options\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobServer(_options, null));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact, GlobalLock(Reason = \"Uses JobStorage.Current instance\")]\n        public void Ctor_HasDefaultValue_ForStorage()\n        {\n            JobStorage.Current = new Mock<JobStorage>().Object;\n            Assert.DoesNotThrow(() => StartServer(\n                () => new BackgroundJobServer(_options)));\n        }\n\n        [Fact]\n        public void Ctor_HasDefaultValue_ForOptions()\n        {\n            Assert.DoesNotThrow(() => StartServer(\n                () => new BackgroundJobServer(_storage.Object)));\n        }\n\n        [Fact, GlobalLock(Reason = \"Uses JobStorage.Current instance\")]\n        public void Ctor_HasDefaultValues_ForAllParameters()\n        {\n            JobStorage.Current = new Mock<JobStorage>().Object;\n            Assert.DoesNotThrow(() => StartServer(\n                () => new BackgroundJobServer()));\n        }\n\n        [Fact]\n        public void Ctor_StartsTheBootstrapSupervisor()\n        {\n            var instance = _serverMock.Object;\n            _supervisor.Verify(x => x.Start());\n        }\n\n        [Fact]\n        public void Dispose_DisposesBootstrapSupervisor()\n        {\n            _serverMock.Object.Dispose();\n\n            _supervisor.Verify(x => x.Dispose());\n        }\n\n        [Fact]\n        public void GetBootstrapSupervisor_ReturnsBootstrapper_WrappedWithAutomaticRetry()\n        {\n            // Arrange\n            var server = CreateServer();\n\n            // Act\n            var supervisor = server.GetBootstrapTask();\n\n            // Assert\n            Assert.NotNull(supervisor);\n\n            var wrapper = ((ServerSupervisor) supervisor).Component;\n\n            Assert.IsType<AutomaticRetryServerComponentWrapper>(wrapper);\n            Assert.IsType<ServerBootstrapper>(((AutomaticRetryServerComponentWrapper)wrapper).InnerComponent);\n        }\n\n        [Fact]\n        public void GetSupervisors_ContainsDefaultComponents_WrappedTo_AutomaticRetryServerComponentWrapper()\n        {\n            // Arrange\n            var server = CreateServer();\n\n            // Act\n            var supervisors = server.GetSupervisors();\n\n            // Assert\n            var componentTypes = supervisors.OfType<ServerSupervisor>()\n                .Select(x => x.Component)\n                .Cast<AutomaticRetryServerComponentWrapper>()\n                .Select(x => x.InnerComponent)\n                .Select(x => x.GetType())\n                .ToArray();\n\n            Assert.Contains(typeof(Worker), componentTypes);\n            Assert.Contains(typeof(ServerHeartbeat), componentTypes);\n            Assert.Contains(typeof(ServerWatchdog), componentTypes);\n            Assert.Contains(typeof(DelayedJobSchedulerFacts), componentTypes);\n        }\n\n        [Fact]\n        public void GetSupervisors_ContainsStorageComponents_WrappedTo_AutomaticRetryServerComponentWrapper()\n        {\n            // Arrange\n            var storageComponent = new Mock<IServerComponent>();\n            _storage.Setup(x => x.GetComponents()).Returns(new[] { storageComponent.Object });\n\n            var server = CreateServer();\n\n            // Act\n            var supervisors = server.GetSupervisors();\n\n            // Assert\n            var components = supervisors.OfType<ServerSupervisor>()\n                .Select(x => x.Component)\n                .Cast<AutomaticRetryServerComponentWrapper>()\n                .Select(x => x.InnerComponent)\n                .ToArray();\n\n            Assert.Contains(storageComponent.Object, components);\n        }\n\n        private BackgroundJobServer CreateServer()\n        {\n            return new BackgroundJobServer(_options, _storage.Object);\n        }\n\n        private void StartServer(Func<BackgroundJobServer> createFunc)\n        {\n            using (createFunc()) { }\n        }*/\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/CaptureCultureAttributeFacts.cs",
    "content": "using System.Globalization;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class CaptureCultureAttributeFacts\n    {\n        private readonly CreateContextMock _context;\n        private readonly PerformContextMock _perform;\n        private readonly CultureInfo _culture;\n        private readonly CultureInfo _uiCulture;\n\n        public CaptureCultureAttributeFacts()\n        {\n            _context = new CreateContextMock();\n            _perform = new PerformContextMock();\n            _culture = new CultureInfo(\"th-TH\");\n            _uiCulture = new CultureInfo(\"vi-VN\");\n\n            SetCurrentCulture(CultureInfo.InvariantCulture);\n            SetCurrentUICulture(CultureInfo.InvariantCulture);\n\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentCulture\"))\n                .Returns($\"\\\"{_culture.Name}\\\"\");\n\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentUICulture\"))\n                .Returns($\"\\\"{_uiCulture.Name}\\\"\");\n        }\n\n        [Fact]\n        public void Ctor_SetsDefaultCulturesToNull_ByDefault()\n        {\n            var attribute = new CaptureCultureAttribute();\n\n            Assert.Null(attribute.DefaultCultureName);\n            Assert.Null(attribute.DefaultUICultureName);\n        }\n\n        [Fact]\n        public void Ctor_AllowsToUseNulls_AsDefaultCultureValues()\n        {\n            var attribute = new CaptureCultureAttribute(null, null);\n\n            Assert.Null(attribute.DefaultCultureName);\n            Assert.Null(attribute.DefaultUICultureName);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void OnCreating_SetsCultureRelated_Parameters()\n        {\n            // Arrange\n            var attribute = new CaptureCultureAttribute();\n            SetCurrentCulture(_culture);\n            SetCurrentUICulture(_uiCulture);\n\n            // Act\n            attribute.OnCreating(_context.GetCreatingContext());\n\n            // Assert\n            Assert.Equal(_context.Object.Parameters[\"CurrentCulture\"], _culture.Name);\n            Assert.Equal(_context.Object.Parameters[\"CurrentUICulture\"], _uiCulture.Name);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_180)]\n        public void OnCreating_ExplicitlySetsUICultureName_EvenWhenItIsEqualToTheCultureNameOne_WithCompatibilityVersion170AndBelow()\n        {\n            // Arrange\n            var attribute = new CaptureCultureAttribute(null, null, captureDefault: false);\n            SetCurrentCulture(_culture);\n            SetCurrentUICulture(_culture);\n\n            // Act\n            attribute.OnCreating(_context.GetCreatingContext());\n\n            // Assert\n            Assert.Equal(_context.Object.Parameters[\"CurrentCulture\"], _culture.Name);\n            Assert.Equal(_context.Object.Parameters[\"CurrentUICulture\"], _culture.Name);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_180)]\n        public void OnCreating_DoesNotSetUICultureName_WhenItIsEqualToTheCultureNameOne_WithCompatibilityVersion180AndAbove()\n        {\n            // Arrange\n            var attribute = new CaptureCultureAttribute(null, null, captureDefault: true);\n            SetCurrentCulture(_culture);\n            SetCurrentUICulture(_culture);\n\n            // Act\n            attribute.OnCreating(_context.GetCreatingContext());\n\n            // Assert\n            Assert.Equal(_context.Object.Parameters[\"CurrentCulture\"], _culture.Name);\n            Assert.False(_context.Object.Parameters.ContainsKey(\"CurrentUICulture\"));\n        }\n\n        [DataCompatibilityRangeFact]\n        public void OnCreating_SetsCultureNames_EvenWhenTheyEqualToTheDefaultOnes_WhenCaptureDefaultEnabled()\n        {\n            // Arrange\n            SetCurrentCulture(_culture);\n            SetCurrentUICulture(_uiCulture);\n\n            var attribute = new CaptureCultureAttribute(_culture.Name, _uiCulture.Name, captureDefault: true);\n\n            // Act\n            attribute.OnCreating(_context.GetCreatingContext());\n\n            // Assert\n            Assert.Equal(_context.Object.Parameters[\"CurrentCulture\"], _culture.Name);\n            Assert.Equal(_context.Object.Parameters[\"CurrentUICulture\"], _uiCulture.Name);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void OnCreating_DoesNotSetCultureNames_WhenTheyEqualToTheDefaultOnes_WhenCaptureDefaultDisabled()\n        {\n            // Arrange\n            SetCurrentCulture(_culture);\n            SetCurrentUICulture(_uiCulture);\n\n            var attribute = new CaptureCultureAttribute(_culture.Name, _uiCulture.Name, captureDefault: false);\n\n            // Act\n            attribute.OnCreating(_context.GetCreatingContext());\n\n            // Assert\n            Assert.False(_context.Object.Parameters.ContainsKey(\"CurrentCulture\"));\n            Assert.False(_context.Object.Parameters.ContainsKey(\"CurrentUICulture\"));\n        }\n\n        [Fact]\n        public void OnCreated_DoesNotThrow_NotImplementedException()\n        {\n            // Arrange\n            var attribute = new CaptureCultureAttribute();\n\n            // Act\n            attribute.OnCreated(_context.GetCreatedContext(\"id\"));\n\n            // Assert – does not throw\n        }\n\n        [Fact]\n        public void OnPerforming_ReadsCorrespondingJobParameters_AndSetCurrentCultures()\n        {\n            // Arrange\n            var attribute = new CaptureCultureAttribute();\n\n            // Act\n            attribute.OnPerforming(_perform.GetPerformingContext());\n\n            // Assert\n            Assert.Equal(_culture, CultureInfo.CurrentCulture);\n            Assert.Equal(_uiCulture, CultureInfo.CurrentUICulture);\n        }\n\n        [Fact]\n        public void OnPerforming_UsesTheSameCultureForUI_WhenCultureIsSetButUICultureIsMissing()\n        {\n            // Arrange\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentUICulture\"))\n                .Returns((string)null);\n\n            var attribute = new CaptureCultureAttribute();\n\n            // Act\n            attribute.OnPerforming(_perform.GetPerformingContext());\n\n            // Assert\n            Assert.Equal(_culture, CultureInfo.CurrentCulture);\n            Assert.Equal(_culture, CultureInfo.CurrentUICulture);\n        }\n\n        [Fact]\n        public void OnPerforming_UsesDefaultCultures_WhenCorrespondingJobParametersAreMissing()\n        {\n            // Arrange\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentCulture\"))\n                .Returns((string)null);\n\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentUICulture\"))\n                .Returns((string)null);\n\n            var attribute = new CaptureCultureAttribute(\"en-US\", \"en-GB\");\n\n            // Act\n            attribute.OnPerforming(_perform.GetPerformingContext());\n\n            // Assert\n            Assert.Equal(\"en-US\", CultureInfo.CurrentCulture.Name);\n            Assert.Equal(\"en-GB\", CultureInfo.CurrentUICulture.Name);\n        }\n\n        [Fact]\n        public void OnPerforming_UsesTheSameDefaultCultures_WhenCorrespondingJobParametersAreMissing_AndOnlyOneIsSpecified()\n        {\n            // Arrange\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentCulture\"))\n                .Returns((string)null);\n\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentUICulture\"))\n                .Returns((string)null);\n\n            var attribute = new CaptureCultureAttribute(\"en-US\");\n\n            // Act\n            attribute.OnPerforming(_perform.GetPerformingContext());\n\n            // Assert\n            Assert.Equal(\"en-US\", CultureInfo.CurrentCulture.Name);\n            Assert.Equal(\"en-US\", CultureInfo.CurrentUICulture.Name);\n        }\n\n        [Fact]\n        public void OnPerforming_DoesNotUseDefaultCultureAsDefaultUICulture_WhenCorrespondingJobParametersAreMissing_AndExplicitNullValueIsUsed()\n        {\n            // Arrange\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentCulture\"))\n                .Returns((string)null);\n\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentUICulture\"))\n                .Returns((string)null);\n\n            var attribute = new CaptureCultureAttribute(\"en-US\", null);\n\n            // Act\n            attribute.OnPerforming(_perform.GetPerformingContext());\n\n            // Assert\n            Assert.Equal(CultureInfo.InvariantCulture, CultureInfo.CurrentUICulture);\n        }\n\n        [Fact]\n        public void OnPerforming_DoesNotSetAnything_WhenBothJobParametersMissing_AndDefaultCulturesNotSet()\n        {\n            // Arrange\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentCulture\"))\n                .Returns((string)null);\n\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentUICulture\"))\n                .Returns((string)null);\n\n            var attribute = new CaptureCultureAttribute();\n\n            // Act\n            attribute.OnPerforming(_perform.GetPerformingContext());\n\n            // Assert\n            Assert.Equal(CultureInfo.InvariantCulture, CultureInfo.CurrentCulture);\n            Assert.Equal(CultureInfo.InvariantCulture, CultureInfo.CurrentUICulture);\n        }\n\n        [Fact]\n        public void OnPerforming_DoesNotSetAnything_InCaseOfCultureNotFoundException()\n        {\n            // Arrange\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentCulture\"))\n                .Returns(\"\\\"xx-XX\\\"\");\n\n            _perform.Connection\n                .Setup(x => x.GetJobParameter(_perform.BackgroundJob.Id, \"CurrentUICulture\"))\n                .Returns(\"\\\"yy-YY\\\"\");\n\n            var attribute = new CaptureCultureAttribute();\n\n            // Act\n            attribute.OnPerforming(_perform.GetPerformingContext());\n\n            // Assert\n            if (CultureInfo.CurrentCulture.Name != \"xx-XX\")\n                Assert.Equal(CultureInfo.InvariantCulture, CultureInfo.CurrentCulture);\n            if (CultureInfo.CurrentUICulture.Name != \"yy-YY\")\n                Assert.Equal(CultureInfo.InvariantCulture, CultureInfo.CurrentUICulture);\n        }\n\n        [Fact]\n        public void OnPerformed_ResetsCurrentCultures_ToTheirOriginalValues()\n        {\n            // Arrange\n            var attribute = new CaptureCultureAttribute();\n            attribute.OnPerforming(_perform.GetPerformingContext());\n\n            // Act\n            attribute.OnPerformed(_perform.GetPerformedContext());\n\n            // Assert\n            Assert.Equal(CultureInfo.InvariantCulture, CultureInfo.CurrentCulture);\n            Assert.Equal(CultureInfo.InvariantCulture, CultureInfo.CurrentUICulture);\n        }\n\n        [Fact]\n        public void OnPerformed_DoesNotThrow_WhenCanNotRestoreOriginalCultures()\n        {\n            // Arrange\n            var attribute = new CaptureCultureAttribute();\n\n            // Act\n            attribute.OnPerformed(_perform.GetPerformedContext());\n\n            // Assert – does not throw\n        }\n\n        private static void SetCurrentCulture(CultureInfo value)\n        {\n#if !NETCOREAPP1_0\n            System.Threading.Thread.CurrentThread.CurrentCulture = value;\n#else\n            CultureInfo.CurrentCulture = value;\n#endif\n        }\n\n        // ReSharper disable once InconsistentNaming\n        private static void SetCurrentUICulture(CultureInfo value)\n        {\n#if !NETCOREAPP1_0\n            System.Threading.Thread.CurrentThread.CurrentUICulture = value;\n#else\n            CultureInfo.CurrentUICulture = value;\n#endif\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Client/BackgroundJobFactoryFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Moq.Sequences;\nusing Xunit;\n\n// ReSharper disable PossibleNullReferenceException\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Client\n{\n    public class BackgroundJobFactoryFacts\n    {\n        private readonly Mock<CreateContext> _context;\n        private readonly IList<object> _filters;\n        private readonly Mock<IJobFilterProvider> _filterProvider;\n        private readonly Mock<IBackgroundJobFactory> _innerFactory;\n        private readonly BackgroundJobMock _backgroundJob;\n\n        public BackgroundJobFactoryFacts()\n        {\n            var storage = new Mock<JobStorage>();\n            var connection = new Mock<IStorageConnection>();\n            var state = new Mock<IState>();\n            _backgroundJob = new BackgroundJobMock();\n\n            _context = new Mock<CreateContext>(storage.Object, connection.Object, _backgroundJob.Job, state.Object)\n            {\n                CallBase = true\n            };\n            \n            _filters = new List<object>();\n            _filterProvider = new Mock<IJobFilterProvider>();\n            _filterProvider.Setup(x => x.GetFilters(It.IsNotNull<Job>())).Returns(\n                _filters.Select(f => new JobFilter(f, JobFilterScope.Type, null)));\n            \n            _innerFactory = new Mock<IBackgroundJobFactory>();\n            _innerFactory.Setup(x => x.Create(_context.Object)).Returns(_backgroundJob.Object);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenFilterProviderIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobFactory(null, _innerFactory.Object));\n\n            Assert.Equal(\"filterProvider\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenInnerFactoryIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobFactory(_filterProvider.Object, null));\n\n            Assert.Equal(\"innerFactory\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Run_ThrowsAnException_WhenContextIsNull()\n        {\n            var factory = CreateFactory();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => factory.Create(null));\n\n            Assert.Equal(\"context\", exception.ParamName);\n        }\n        \n        [Fact]\n        public void Run_CallsInnerFactory_ToCreateAJob()\n        {\n            var factory = CreateFactory();\n\n            factory.Create(_context.Object);\n\n            _innerFactory.Verify(\n                x => x.Create(_context.Object), \n                Times.Once);\n        }\n\n        [Fact]\n        public void Run_ReturnsJobIdentifier()\n        {\n            var factory = CreateFactory();\n\n            var result = factory.Create(_context.Object);\n\n            Assert.Equal(_backgroundJob.Id, result.Id);\n        }\n\n        [Fact]\n        public void Run_DoesNotCatchExceptions()\n        {\n            _innerFactory.Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .Throws<InvalidOperationException>();\n\n            var factory = CreateFactory();\n\n            Assert.Throws<InvalidOperationException>(() => factory.Create(_context.Object));\n        }\n\n        [Fact]\n        public void Run_CallsExceptionFilter_OnException()\n        {\n            // Arrange\n            var filter = new Mock<IClientExceptionFilter>();\n            _filters.Add(filter.Object);\n\n            _innerFactory.Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .Throws<InvalidOperationException>();\n\n            var factory = CreateFactory();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(\n                () => factory.Create(_context.Object));\n            \n            // Assert\n            filter.Verify(x => x.OnClientException(\n                It.IsNotNull<ClientExceptionContext>()));\n        }\n\n        [Fact, Sequence]\n        public void Run_CallsExceptionFilters_InReverseOrder()\n        {\n            // Arrange\n            var filter1 = new Mock<IClientExceptionFilter>();\n            var filter2 = new Mock<IClientExceptionFilter>();\n\n            filter2.Setup(x => x.OnClientException(It.IsAny<ClientExceptionContext>())).InSequence();\n            filter1.Setup(x => x.OnClientException(It.IsAny<ClientExceptionContext>())).InSequence();\n\n            _filters.Add(filter1.Object);\n            _filters.Add(filter2.Object);\n\n            _innerFactory\n                .Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .Throws<InvalidOperationException>();\n\n            var factory = CreateFactory();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(\n                () => factory.Create(_context.Object));\n\n            // Assert - see the `SequenceAttribute` class.\n        }\n\n        [Fact]\n        public void Run_EatsException_WhenItWasHandlerByFilter_AndReturnsNullJobIdentifier()\n        {\n            // Arrange\n            _innerFactory.Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .Throws<InvalidOperationException>();\n\n            var filter = new Mock<IClientExceptionFilter>();\n            filter.Setup(x => x.OnClientException(It.IsAny<ClientExceptionContext>()))\n                .Callback((ClientExceptionContext x) => x.ExceptionHandled = true);\n\n            _filters.Add(filter.Object);\n\n            var factory = CreateFactory();\n\n            // Act\n            var jobId = factory.Create(_context.Object);\n\n            Assert.Null(jobId);\n        }\n\n        [Fact, Sequence]\n        public void Run_CallsClientFilters_BeforeAndAfterTheCreationOfAJob()\n        {\n            // Arrange\n            var filter = new Mock<IClientFilter>();\n            _filters.Add(filter.Object);\n\n            filter.Setup(x => x.OnCreating(It.IsNotNull<CreatingContext>())).InSequence();\n\n            _innerFactory.Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .InSequence();\n\n            filter.Setup(x => x.OnCreated(It.IsNotNull<CreatedContext>())).InSequence();\n\n            var factory = CreateFactory();\n\n            // Act\n            factory.Create(_context.Object);\n\n            // Assert - see the `SequenceAttribute` class.\n        }\n\n        [Fact, Sequence]\n        public void Run_WrapsFilterCalls_OneIntoAnother()\n        {\n            // Arrange\n            var outerFilter = new Mock<IClientFilter>();\n            var innerFilter = new Mock<IClientFilter>();\n\n            _filters.Add(outerFilter.Object);\n            _filters.Add(innerFilter.Object);\n\n            outerFilter.Setup(x => x.OnCreating(It.IsAny<CreatingContext>())).InSequence();\n            innerFilter.Setup(x => x.OnCreating(It.IsAny<CreatingContext>())).InSequence();\n            innerFilter.Setup(x => x.OnCreated(It.IsAny<CreatedContext>())).InSequence();\n            outerFilter.Setup(x => x.OnCreated(It.IsAny<CreatedContext>())).InSequence();\n\n            var factory = CreateFactory();\n\n            // Act\n            factory.Create(_context.Object);\n\n            // Assert - see the `SequenceAttribute` class.\n        }\n\n        [Fact]\n        public void Run_DoesNotCallBoth_CreateJob_And_OnCreated_WhenFilterCancelsThis_AndReturnsNullJobIdentifier()\n        {\n            // Arrange\n            var filter = new Mock<IClientFilter>();\n            _filters.Add(filter.Object);\n\n            filter.Setup(x => x.OnCreating(It.IsAny<CreatingContext>()))\n                .Callback((CreatingContext x) => x.Canceled = true);\n            \n            var factory = CreateFactory();\n\n            // Act\n            var jobId = factory.Create(_context.Object);\n\n            // Assert\n            Assert.Null(jobId);\n\n            _innerFactory.Verify(\n                x => x.Create(It.IsAny<CreateContext>()), \n                Times.Never);\n\n            filter.Verify(x => x.OnCreated(It.IsAny<CreatedContext>()), Times.Never);\n        }\n\n        [Fact]\n        public void Run_TellsOuterFilter_AboutTheCancellationOfCreation()\n        {\n            // Arrange\n            var outerFilter = new Mock<IClientFilter>();\n            var innerFilter = new Mock<IClientFilter>();\n\n            _filters.Add(outerFilter.Object);\n            _filters.Add(innerFilter.Object);\n\n            innerFilter.Setup(x => x.OnCreating(It.IsAny<CreatingContext>()))\n                .Callback((CreatingContext context) => context.Canceled = true);\n\n            var factory = CreateFactory();\n\n            // Act\n            factory.Create(_context.Object);\n\n            // Assert\n            outerFilter.Verify(x => x.OnCreated(It.Is<CreatedContext>(context => context.Canceled)));\n        }\n\n        [Fact]\n        public void Run_DoesNotCall_CreateJob_And_OnCreated_WhenExceptionOccured_DuringCreatingPhase()\n        {\n            // Arrange\n            var filter = new Mock<IClientFilter>();\n            _filters.Add(filter.Object);\n\n            filter.Setup(x => x.OnCreating(It.IsAny<CreatingContext>()))\n                .Throws<InvalidOperationException>();\n\n            var factory = CreateFactory();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(\n                () => factory.Create(_context.Object));\n\n            // Assert\n            _innerFactory.Verify(\n                x => x.Create(It.IsAny<CreateContext>()), \n                Times.Never);\n\n            filter.Verify(x => x.OnCreated(It.IsAny<CreatedContext>()), Times.Never);\n        }\n\n        [Fact]\n        public void Run_TellsFiltersAboutException_WhenItIsOccured_DuringTheCreationOfAJob()\n        {\n            // Arrange\n            var filter = new Mock<IClientFilter>();\n            _filters.Add(filter.Object);\n\n            var exception = new InvalidOperationException();\n            _innerFactory.Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .Throws(exception);\n\n            var factory = CreateFactory();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(\n                () => factory.Create(_context.Object));\n\n            // Assert\n            filter.Verify(x => x.OnCreated(It.Is<CreatedContext>(\n                context => context.Exception == exception)));\n        }\n\n        [Fact]\n        public void Run_TellsOuterFilters_AboutAllExceptions()\n        {\n            // Arrange\n            var outerFilter = new Mock<IClientFilter>();\n            var innerFilter = new Mock<IClientFilter>();\n\n            _filters.Add(outerFilter.Object);\n            _filters.Add(innerFilter.Object);\n\n            var exception = new InvalidOperationException();\n            _innerFactory.Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .Throws(exception);\n\n            var factory = CreateFactory();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(\n                () => factory.Create(_context.Object));\n\n            outerFilter.Verify(x => x.OnCreated(It.Is<CreatedContext>(context => context.Exception == exception)));\n        }\n\n        [Fact]\n        public void Run_DoesNotThrow_HandledExceptions_AndReturnsNullJobIdentifier()\n        {\n            // Arrange\n            var filter = new Mock<IClientFilter>();\n            _filters.Add(filter.Object);\n\n            var exception = new InvalidOperationException();\n            _innerFactory.Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .Throws(exception);\n\n            filter.Setup(x => x.OnCreated(It.Is<CreatedContext>(context => context.Exception == exception)))\n                .Callback((CreatedContext x) => x.ExceptionHandled = true);\n\n            var factory = CreateFactory();\n\n            // Act\n            var jobId = factory.Create(_context.Object);\n\n            // Assert\n            Assert.Null(jobId);\n        }\n\n        [Fact]\n        public void Run_TellsOuterFilter_EvenAboutHandledException()\n        {\n            // Arrange\n            var outerFilter = new Mock<IClientFilter>();\n            var innerFilter = new Mock<IClientFilter>();\n\n            _filters.Add(outerFilter.Object);\n            _filters.Add(innerFilter.Object);\n\n            _innerFactory.Setup(x => x.Create(It.IsAny<CreateContext>()))\n                .Throws<InvalidOperationException>();\n\n            innerFilter.Setup(x => x.OnCreated(It.IsAny<CreatedContext>()))\n                .Callback((CreatedContext x) => x.ExceptionHandled = true);\n\n            var factory = CreateFactory();\n\n            // Act\n            factory.Create(_context.Object);\n\n            // Assert\n            outerFilter.Verify(x => x.OnCreated(It.Is<CreatedContext>(context => context.Exception != null)));\n        }\n\n        [UsedImplicitly]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        public void TestMethod()\n        {\n        }\n\n        private BackgroundJobFactory CreateFactory()\n        {\n            return new BackgroundJobFactory(_filterProvider.Object, _innerFactory.Object);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Client/ClientExceptionContextFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Client\n{\n    public class ClientExceptionContextFacts\n    {\n        private readonly CreateContext _createContext;\n\n        public ClientExceptionContextFacts()\n        {\n            var storage = new Mock<JobStorage>();\n            var connection = new Mock<IStorageConnection>();\n            var job = Job.FromExpression(() => TestMethod());\n            var state = new Mock<IState>();\n\n            _createContext = new CreateContext(\n                storage.Object, connection.Object, job, state.Object);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenCreateContextIsNull()\n        {\n            Assert.Throws<NullReferenceException>(\n                () => new ClientExceptionContext(null, new Exception()));\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenExceptionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new ClientExceptionContext(_createContext, null));\n\n            Assert.Equal(\"exception\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_CorrectlySets_AllProperties()\n        {\n            var exception = new Exception();\n            var context = new ClientExceptionContext(_createContext, exception);\n\n            Assert.Same(exception, context.Exception);\n            Assert.False(context.ExceptionHandled);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void TestMethod()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Client/CoreBackgroundJobFactoryFacts.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable PossibleNullReferenceException\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Client\n{\n    public class CoreBackgroundJobFactoryFacts\n    {\n        private const string JobId = \"jobId\";\n        private readonly Mock<IStateMachine> _stateMachine;\n        private readonly CreateContextMock _context;\n        private readonly Mock<JobStorageTransaction> _transaction;\n\n        public CoreBackgroundJobFactoryFacts()\n        {\n            _stateMachine = new Mock<IStateMachine>();\n            _context = new CreateContextMock();\n            _transaction = new Mock<JobStorageTransaction>();\n\n            _transaction.Setup(x => x.CreateJob(\n                It.IsNotNull<Job>(),\n                It.IsNotNull<IDictionary<string, string>>(),\n                It.IsAny<DateTime>(),\n                It.IsAny<TimeSpan>())).Returns(JobId);\n\n            _context.Connection.Setup(x => x.CreateExpiredJob(\n                It.IsAny<Job>(),\n                It.IsAny<IDictionary<string, string>>(),\n                It.IsAny<DateTime>(),\n                It.IsAny<TimeSpan>())).Returns(JobId);\n            _context.Connection.Setup(x => x.GetJobData(JobId)).Returns(new JobData());\n            _context.Connection.Setup(x => x.CreateWriteTransaction())\n                .Returns(_transaction.Object);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStateMachineIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new CoreBackgroundJobFactory(null));\n\n            Assert.Equal(\"stateMachine\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Create_ThrowsAnException_WhenContextIsNull()\n        {\n            var factory = CreateFactory();\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => factory.Create(null));\n\n            Assert.Equal(\"context\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Create_ThrowsAnException_WhenJobQueueIsSet_ButStorageDoesNotSupportIt()\n        {\n            // Arrange\n            _context.Storage.Setup(x => x.HasFeature(JobStorageFeatures.JobQueueProperty)).Returns(false);\n            _context.Job = Job.FromExpression(() => Method(), \"some-queue\");\n\n            var factory = CreateFactory();\n\n            // Act & Assert\n            Assert.Throws<NotSupportedException>(() => factory.Create(_context.Object));\n        }\n\n        [Fact]\n        public void Create_ReturnsNull_WhenCreateExpiredJobReturnedNull()\n        {\n            _context.Connection\n                .Setup(x => x.CreateExpiredJob(It.IsAny<Job>(), It.IsAny<IDictionary<string, string>>(), It.IsAny<DateTime>(), It.IsAny<TimeSpan>()))\n                .Returns<string>(null);\n\n            var factory = CreateFactory();\n            var result = factory.Create(_context.Object);\n\n            Assert.Null(result);\n        }\n        \n        [Fact]\n        public void CreateJob_CreatesExpiredJob()\n        {\n            _context.Object.Parameters.Add(\"Name\", \"Value\");\n\n            var factory = CreateFactory();\n\n            factory.Create(_context.Object);\n\n            _context.Connection.Verify(x => x.CreateExpiredJob(\n                _context.Job,\n                It.Is<Dictionary<string, string>>(d => d[\"Name\"] == \"\\\"Value\\\"\"),\n                It.IsAny<DateTime>(),\n                It.IsAny<TimeSpan>()));\n        }\n\n        [Fact]\n        public void CreateJob_ChangesTheStateOfACreatedJob()\n        {\n            var factory = CreateFactory();\n\n            factory.Create(_context.Object);\n\n            _stateMachine.Verify(x => x.ApplyState(\n                It.Is<ApplyStateContext>(\n                    sc => sc.BackgroundJob.Id == JobId && sc.BackgroundJob.Job == _context.Job\n                    && sc.NewState == _context.InitialState.Object && sc.OldStateName == null)));\n\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void CreateJob_ReturnsNewJobId()\n        {\n            var factory = CreateFactory();\n            Assert.Equal(JobId, factory.Create(_context.Object).Id);\n        }\n\n        [Fact]\n        public void Create_DoesNotRetryCreateExpiredJobMethod_ByDefault_AndThrowsAnException()\n        {\n            // Arrange\n            _context.Connection\n                .Setup(x => x.CreateExpiredJob(It.IsAny<Job>(), It.IsAny<IDictionary<string, string>>(),\n                    It.IsAny<DateTime>(), It.IsAny<TimeSpan>()))\n                .Throws<InvalidOperationException>();\n\n            var factory = CreateFactory();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(() => factory.Create(_context.Object));\n\n            // Assert\n            _context.Connection.Verify(\n                x => x.CreateExpiredJob(It.IsAny<Job>(), It.IsAny<IDictionary<string, string>>(), It.IsAny<DateTime>(), It.IsAny<TimeSpan>()),\n                Times.Once);\n        }\n\n        [Fact]\n        public void Create_DoesNotRetryStateTransaction_ByDefault_AndThrowsAnException()\n        {\n            // Arrange\n            _stateMachine.Setup(x => x.ApplyState(It.IsAny<ApplyStateContext>())).Throws<InvalidOperationException>();\n\n            var factory = CreateFactory();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(() => factory.Create(_context.Object));\n\n            // Assert\n            _context.Connection.Verify(\n                x => x.CreateExpiredJob(It.IsAny<Job>(), It.IsAny<IDictionary<string, string>>(), It.IsAny<DateTime>(), It.IsAny<TimeSpan>()),\n                Times.Once);\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()), Times.Once);\n            _context.Connection.Verify(x => x.GetJobData(It.IsAny<string>()), Times.Never);\n            _transaction.Verify(x => x.Commit(), Times.Never);\n        }\n\n        [Fact]\n        public void Create_IsResilientToASingleCreateExpiredJobFault_WhenRetriesEnabled()\n        {\n            // Arrange\n            _context.Connection\n                .SetupSequence(x => x.CreateExpiredJob(It.IsAny<Job>(), It.IsAny<IDictionary<string, string>>(),\n                    It.IsAny<DateTime>(), It.IsAny<TimeSpan>()))\n                .Throws<InvalidOperationException>()\n                .Returns(JobId);\n\n            var factory = CreateFactory(retries: 1);\n\n            // Act\n            factory.Create(_context.Object);\n\n            // Assert\n            _context.Connection.Verify(\n                x => x.CreateExpiredJob(It.IsNotNull<Job>(), It.IsNotNull<IDictionary<string, string>>(), It.IsAny<DateTime>(), It.IsAny<TimeSpan>()),\n                Times.Exactly(2));\n            _stateMachine.Verify(x => x.ApplyState(It.IsNotNull<ApplyStateContext>()), Times.Once);\n            _context.Connection.Verify(x => x.GetJobData(It.IsAny<string>()), Times.Never);\n            _transaction.Verify(x => x.Commit(), Times.Once);\n        }\n\n        [Fact]\n        public void Create_IsResilientToASingleStateMachineFault_WhenRetriesEnabled()\n        {\n            // Arrange\n            _stateMachine.SetupSequence(x => x.ApplyState(It.IsAny<ApplyStateContext>()))\n                .Throws<InvalidOperationException>()\n                .Returns(_context.InitialState.Object);\n\n            var factory = CreateFactory(retries: 1);\n\n            // Act\n            factory.Create(_context.Object);\n\n            // Assert\n            _context.Connection.Verify(\n                x => x.CreateExpiredJob(It.IsNotNull<Job>(), It.IsNotNull<IDictionary<string, string>>(), It.IsAny<DateTime>(), It.IsAny<TimeSpan>()),\n                Times.Once);\n            _stateMachine.Verify(x => x.ApplyState(It.IsNotNull<ApplyStateContext>()), Times.Exactly(2));\n            _context.Connection.Verify(x => x.GetJobData(JobId), Times.Once);\n            _transaction.Verify(x => x.Commit(), Times.Once);\n        }\n\n        [Fact]\n        public void Create_DoesNotInitializeJobTwice_WhenTransactionFaulted_WhenRetriesEnabled()\n        {\n            // Arrange\n            _transaction.SetupSequence(x => x.Commit())\n                .Throws<TimeoutException>()\n                .Pass();\n\n            _context.Connection.Setup(x => x.GetJobData(JobId)).Returns(new JobData\n            {\n                State = EnqueuedState.StateName\n            });\n\n            var factory = CreateFactory(retries: 1);\n\n            // Act\n            factory.Create(_context.Object);\n\n            // Assert\n            _context.Connection.Verify(\n                x => x.CreateExpiredJob(It.IsNotNull<Job>(), It.IsNotNull<IDictionary<string, string>>(), It.IsAny<DateTime>(), It.IsAny<TimeSpan>()),\n                Times.Once);\n            _stateMachine.Verify(x => x.ApplyState(It.IsNotNull<ApplyStateContext>()), Times.Once);\n            _context.Connection.Verify(x => x.GetJobData(JobId), Times.Once);\n            _transaction.Verify(x => x.Commit(), Times.Once);\n        }\n\n        [Fact]\n        public void Create_ThrowsAnException_WhenJobDataReturnsNull_OnStateTransactionRetry()\n        {\n            // Arrange\n            _transaction.SetupSequence(x => x.Commit())\n                .Throws<TimeoutException>()\n                .Pass();\n\n            _context.Connection.Setup(x => x.GetJobData(It.IsAny<string>())).Returns((JobData)null);\n\n            var factory = CreateFactory(retries: 1);\n\n            // Act\n            var exception = Assert.Throws<AggregateException>(() => factory.Create(_context.Object));\n\n            // Assert\n            Assert.IsType<TimeoutException>(exception.InnerExceptions[0]);\n            Assert.IsType<InvalidOperationException>(exception.InnerExceptions[1]);\n\n            _context.Connection.Verify(\n                x => x.CreateExpiredJob(It.IsNotNull<Job>(), It.IsNotNull<IDictionary<string, string>>(), It.IsAny<DateTime>(), It.IsAny<TimeSpan>()),\n                Times.Once);\n            _stateMachine.Verify(x => x.ApplyState(It.IsNotNull<ApplyStateContext>()), Times.Once);\n            _transaction.Verify(x => x.Commit(), Times.Once);\n        }\n\n        [Fact]\n        public void Create_ThrowsAnException_AndLeavesJobUninitialized_WhenAllRetryAttemptsExhausted_WhenCallingCreateExpiredJob()\n        {\n            // Arrange\n            _context.Connection\n                .SetupSequence(x => x.CreateExpiredJob(It.IsAny<Job>(), It.IsAny<IDictionary<string, string>>(),\n                    It.IsAny<DateTime>(), It.IsAny<TimeSpan>()))\n                .Throws<NotSupportedException>()\n                .Throws<TimeoutException>();\n\n            var factory = CreateFactory(retries: 1);\n\n            // Act\n            var exception = Assert.Throws<AggregateException>(() => factory.Create(_context.Object));\n\n            // Assert\n            Assert.IsType<NotSupportedException>(exception.InnerExceptions[0]);\n            Assert.IsType<TimeoutException>(exception.InnerExceptions[1]);\n\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()), Times.Never);\n            _transaction.Verify(x => x.Commit(), Times.Never);\n        }\n\n        [Fact]\n        public void Create_ThrowsAnException_AndLeavesJobAsIs_WhenAllRetryAttemptsExhausted_WithFaultyStateTransaction()\n        {\n            // Arrange\n            _stateMachine.SetupSequence(x => x.ApplyState(It.IsAny<ApplyStateContext>()))\n                .Throws<NotSupportedException>()\n                .Returns(_context.InitialState.Object);\n\n            _transaction.SetupSequence(x => x.Commit())\n                .Throws<TimeoutException>()\n                .Pass();\n\n            var factory = CreateFactory(retries: 1);\n\n            // Act\n            var exception = Assert.Throws<AggregateException>(() => factory.Create(_context.Object));\n\n            // Assert\n            Assert.IsType<NotSupportedException>(exception.InnerExceptions[0]);\n            Assert.IsType<TimeoutException>(exception.InnerExceptions[1]);\n\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()), Times.Exactly(2));\n            _transaction.Verify(x => x.Commit(), Times.Once);\n        }\n\n        private CoreBackgroundJobFactory CreateFactory(int? retries = null)\n        {\n            var factory = new CoreBackgroundJobFactory(_stateMachine.Object);\n            if (retries.HasValue) factory.RetryAttempts = retries.Value;\n\n            return factory;\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Method()\n        {\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Client/CreateContextFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable ObjectCreationAsStatement\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Client\n{\n    public class CreateContextFacts\n    {\n        private readonly Job _job;\n        private readonly Mock<IState> _state;\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly Mock<JobStorage> _storage;\n\n        public CreateContextFacts()\n        {\n            _job = Job.FromExpression(() => Method());\n            _state = new Mock<IState>();\n            _connection = new Mock<IStorageConnection>();\n            _storage = new Mock<JobStorage>();\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new CreateContext(null, _connection.Object, _job, _state.Object));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenConnectionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new CreateContext(_storage.Object, null, _job, _state.Object));\n\n            Assert.Equal(\"connection\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenJobIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new CreateContext(_storage.Object, _connection.Object, null, _state.Object));\n\n            Assert.Equal(\"job\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_DoesNotThrowAnException_WhenStateIsNull()\n        {\n            // Does not throw\n            new CreateContext(_storage.Object, _connection.Object, _job, null);\n        }\n\n        [Fact]\n        public void Ctor_CorrectlyInitializes_AllProperties()\n        {\n            var context = CreateContext();\n\n            Assert.Same(_storage.Object, context.Storage);\n            Assert.Same(_connection.Object, context.Connection);\n            Assert.Same(_job, context.Job);\n            Assert.Same(_state.Object, context.InitialState);\n\n            Assert.NotNull(context.Items);\n            Assert.NotNull(context.Parameters);\n        }\n\n        [Fact]\n        public void CopyCtor_CopiesItemsDictionary_FromTheGivenContext()\n        {\n            var context = CreateContext();\n            var contextCopy = new CreateContext(context);\n\n            Assert.Same(context.Items, contextCopy.Items);\n            Assert.Same(context.Parameters, contextCopy.Parameters);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Method()\n        {\n        }\n\n        private CreateContext CreateContext()\n        {\n            return new CreateContext(_storage.Object, _connection.Object, _job, _state.Object);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Client/CreatedContextFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\n#pragma warning disable 618\n\nnamespace Hangfire.Core.Tests.Client\n{\n    public class CreatedContextFacts\n    {\n        private readonly Exception _exception;\n        private readonly BackgroundJobMock _backgroundJob;\n\n        public CreatedContextFacts()\n        {\n            _exception = new Exception();\n            _backgroundJob = new BackgroundJobMock();\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenCreateContextIsNull()\n        {\n            Assert.Throws<NullReferenceException>(\n                () => new CreatedContext(null, _backgroundJob.Object, false, null));\n        }\n\n        [Fact]\n        public void Ctor_CorrectlySetsAllProperties()\n        {\n            var context = CreateContext();\n\n            Assert.True(context.Canceled);\n            Assert.Same(_exception, context.Exception);\n            Assert.Equal(_backgroundJob.Id, context.JobId);\n        }\n\n        [Fact]\n        public void SetJobParameter_ThrowsAnException_WhenParameterNameIsNull()\n        {\n            var context = CreateContext();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => context.SetJobParameter(null, null));\n\n            Assert.Equal(\"name\", exception.ParamName);\n        }\n\n        [Fact]\n        public void SetJobParameter_ThrowsAnException_AfterCreateJobWasCalled()\n        {\n            // TODO: incorrect test.\n\n            var context = CreateContext();\n\n            Assert.Throws<InvalidOperationException>(\n                () => context.SetJobParameter(\"name\", \"value\"));\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void TestMethod() { }\n\n        private CreatedContext CreateContext()\n        {\n            var storage = new Mock<JobStorage>();\n            var connection = new Mock<IStorageConnection>();\n            var job = Job.FromExpression(() => TestMethod());\n            var state = new Mock<IState>();\n            \n            var createContext = new CreateContext(storage.Object, connection.Object, job, state.Object);\n            return new CreatedContext(createContext, _backgroundJob.Object, true, _exception);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Client/CreatingContextFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Client\n{\n    public class CreatingContextFacts\n    {\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenContextIsNull()\n        {\n            Assert.Throws<NullReferenceException>(\n                () => new CreatingContext(null));\n        }\n\n        [Fact]\n        public void Ctor_CanceledProperty_IsFalseByDefault()\n        {\n            var context = CreateContext();\n\n            Assert.False(context.Canceled);\n        }\n\n        [Fact]\n        public void SetJobParameter_ThrowsAnException_WhenParameterNameIsNull()\n        {\n            var context = CreateContext();\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => context.SetJobParameter(null, null));\n\n            Assert.Equal(\"name\", exception.ParamName);\n        }\n\n        [Fact]\n        public void SetJobParameter_AcceptsNullValues()\n        {\n            var context = CreateContext();\n\n            // Does noto throw\n            context.SetJobParameter(\"name\", null);\n        }\n\n        [Fact]\n        public void SetJobParameter_CanBeCalledTwice_WithTheSameName()\n        {\n            var context = CreateContext();\n            context.SetJobParameter(\"name\", null);\n\n            // Does not throw\n            context.SetJobParameter(\"name\", null);\n        }\n\n        [Fact]\n        public void GetJobParameter_ThrowsAnException_WhenParameterNameIsNull()\n        {\n            var context = CreateContext();\n            Assert.Throws<ArgumentNullException>(\n                () => context.GetJobParameter<int>(null));\n        }\n\n        [Fact]\n        public void GetJobParameter_ReturnsDefaultValue_IfParameterDoesNotExists()\n        {\n            var context = CreateContext();\n\n            Assert.Equal(default(int), context.GetJobParameter<int>(\"one\"));\n            Assert.Equal(default(string), context.GetJobParameter<string>(\"two\"));\n        }\n\n        [Fact]\n        public void GetJobParameter_ReturnsTheValue_ThatWasSetByTheCorrespondingMethod()\n        {\n            var context = CreateContext();\n            context.SetJobParameter(\"name\", \"value\");\n\n            Assert.Equal(\"value\", context.GetJobParameter<string>(\"name\"));\n        }\n\n        [Fact]\n        public void GetJobParameter_ReturnsTheValue_OfTheSpecifiedParameterNameOnly()\n        {\n            var context = CreateContext();\n\n            context.SetJobParameter(\"name1\", \"value1\");\n            context.SetJobParameter(\"name2\", \"value2\");\n\n            Assert.Equal(\"value1\", context.GetJobParameter<string>(\"name1\"));\n        }\n\n        [Fact]\n        public void GetJobParameter_ReturnsTheFreshestValue_WhenTwoSetOperationsPerformed()\n        {\n            var context = CreateContext();\n\n            context.SetJobParameter(\"name\", \"oldValue\");\n            context.SetJobParameter(\"name\", \"newValue\");\n\n            Assert.Equal(\"newValue\", context.GetJobParameter<string>(\"name\"));\n        }\n\n        [Fact]\n        public void GetJobParameter_ThrowsAnException_WhenParameterCouldNotBeDeserialized()\n        {\n            var context = CreateContext();\n\n            context.SetJobParameter(\"name\", \"value\");\n\n            Assert.Throws<InvalidOperationException>(\n                () => context.GetJobParameter<int>(\"name\"));\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void TestMethod() { }\n\n        private static CreatingContext CreateContext()\n        {\n            var storage = new Mock<JobStorage>();\n            var connection = new Mock<IStorageConnection>();\n            var job = Job.FromExpression(() => TestMethod());\n            var state = new Mock<IState>();\n\n            var createContext = new CreateContext(storage.Object, connection.Object, job, state.Object);\n            return new CreatingContext(createContext);            \n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/CancellationTokenExtentionsFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Threading;\nusing Hangfire.Common;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class CancellationTokenExtentionsFacts\n    {\n        private readonly CancellationTokenSource _cts = new CancellationTokenSource();\n\n        [Fact]\n        public void GetCancellationEvent_ReturnsSomething()\n        {\n            var cancellationEvent = _cts.Token.GetCancellationEvent();\n\n            Assert.NotNull(cancellationEvent);\n            Assert.NotNull(cancellationEvent.WaitHandle);\n        }\n\n        [Fact]\n        public void Wait_PerformsWait_NotLessThanTheSpecifiedTime()\n        {\n            var stopwatch = Stopwatch.StartNew();\n            var result = _cts.Token.Wait(TimeSpan.FromSeconds(1));\n            stopwatch.Stop();\n\n            Assert.False(result);\n            Assert.True(stopwatch.Elapsed >= TimeSpan.FromMilliseconds(900), $\"Elapsed: {stopwatch.Elapsed}\");\n        }\n\n        [Fact]\n        public void Wait_DoesNotPerformWait_WhenTokenIsCanceled()\n        {\n            _cts.Cancel();\n            var stopwatch = Stopwatch.StartNew();\n            var result = _cts.Token.Wait(TimeSpan.FromSeconds(1));\n            stopwatch.Stop();\n\n            Assert.True(result);\n            Assert.True(stopwatch.Elapsed < TimeSpan.FromMilliseconds(900), $\"Elapsed: {stopwatch.Elapsed}\");\n        }\n\n        [Fact]\n        public void WaitOrThrow_DoesNotThrow_WhenTokenIsNotCanceled()\n        {\n            _cts.Token.WaitOrThrow(TimeSpan.Zero);\n            Assert.False(_cts.Token.IsCancellationRequested);\n        }\n\n        [Fact]\n        public void WaitOrThrow_ThrowsAnException_WhenTokenIsCanceled()\n        {\n            _cts.Cancel();\n            var exception = Assert.Throws<OperationCanceledException>(\n                () => _cts.Token.WaitOrThrow(TimeSpan.FromSeconds(1)));\n\n            Assert.Equal(_cts.Token, exception.CancellationToken);\n            Assert.True(exception.CancellationToken.IsCancellationRequested);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/JobArgumentFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Linq;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Storage;\nusing Moq;\nusing Newtonsoft.Json;\nusing Xunit;\n\n#pragma warning disable 618\n\nnamespace Hangfire.Core.Tests.Common\n{\n\tpublic class JobArgumentFacts\n\t{\n\t\tprivate readonly Mock<JobActivator> _activator;\n\t\tprivate readonly Mock<IJobCancellationToken> _token;\n\n\t\tpublic JobArgumentFacts()\n\t\t{\n\t\t\t_activator = new Mock<JobActivator>();\n\t\t\t_activator.Setup(x => x.ActivateJob(It.IsAny<Type>()))\n\t\t\t\t.Returns(() => new JobArgumentFacts());\n\n\t\t\t_token = new Mock<IJobCancellationToken>();\n\t\t}\n\n\t\tprivate const Boolean BooleanValue = true;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Boolean value) { Assert.Equal(BooleanValue, value); }\n\n\t\t[Fact]\n\t\tpublic void BooleanArguments_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(BooleanValue);\n\t\t}\n\n\t\tprivate const Byte ByteValue = 142;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Byte value) { Assert.Equal(ByteValue, value); }\n\n\t\t[Fact]\n\t\tpublic void ByteValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(ByteValue);\n\t\t}\n\n\t\tprivate const SByte SByteValue = -111;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(SByte value) { Assert.Equal(SByteValue, value); }\n\n\t\t[Fact]\n\t\tpublic void SByteValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(SByteValue);\n\t\t}\n\n\t\tprivate const Char CharValue = Char.MaxValue;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Char value) { Assert.Equal(CharValue, value); }\n\n\t\t[Fact]\n\t\tpublic void CharValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(CharValue);\n\t\t}\n\n\t\tprivate const Decimal DecimalValue = Decimal.MaxValue;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Decimal value) { Assert.Equal(DecimalValue, value); }\n\n\t\t[Fact]\n\t\tpublic void DecimalValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(DecimalValue);\n\t\t}\n\n\t\tprivate const Double DoubleValue = 3.14159265359D;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Double value) { Assert.Equal(DoubleValue, value); }\n\n\t\t[Fact]\n\t\tpublic void DoubleValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(DoubleValue);\n\t\t}\n\n\t\tprivate const Single SingleValue = 3.1415F;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Single value) { Assert.Equal(SingleValue, value); }\n\n\t\t[Fact]\n\t\tpublic void SingleValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(SingleValue);\n\t\t}\n\n\t\tprivate const Int32 Int32Value = Int32.MaxValue;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Int32 value) { Assert.Equal(Int32Value, value); }\n\n\t\t[Fact]\n\t\tpublic void Int32Values_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(Int32Value);\n\t\t}\n\n\t\tprivate const UInt32 UInt32Value = UInt32.MaxValue;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(UInt32 value) { Assert.Equal(UInt32Value, value); }\n\n\t\t[Fact]\n\t\tpublic void UInt32Values_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(UInt32Value);\n\t\t}\n\n\t\tprivate const Int64 Int64Value = Int64.MaxValue;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Int64 value) { Assert.Equal(Int64Value, value); }\n\n\t\t[Fact]\n\t\tpublic void Int64Values_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(Int64Value);\n\t\t}\n\n#if !NETCOREAPP1_0\n\t\tprivate const UInt64 UInt64Value = UInt64.MaxValue;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(UInt64 value) { Assert.Equal(UInt64Value, value); }\n\n\t\t[Fact]\n\t\tpublic void UInt64Values_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(UInt64Value);\n\t\t}\n#endif\n\n\t\tprivate const Int16 Int16Value = Int16.MaxValue;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Int16 value) { Assert.Equal(Int16Value, value); }\n\n\t\t[Fact]\n\t\tpublic void Int16Values_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(Int16Value);\n\t\t}\n\n\t\tprivate const UInt16 UInt16Value = UInt16.MaxValue;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(UInt16 value) { Assert.Equal(UInt16Value, value); }\n\n\t\t[Fact]\n\t\tpublic void UInt16Values_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(UInt16Value);\n\t\t}\n\n\t\tprivate const String StringValue = \"jkashdgfa$%^&\";\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(String value) { Assert.Equal(StringValue, value); }\n\n\t\t[Fact]\n\t\tpublic void StringValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(StringValue);\n\t\t}\n\n\t\tprivate static readonly TimeSpan TimeSpanValue = TimeSpan.FromDays(1);\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(TimeSpan value) { Assert.Equal(TimeSpanValue, value); }\n\n\t\t[Fact]\n\t\tpublic void TimeSpanValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(TimeSpanValue);\n\t\t}\n\n\t\tprivate static readonly Object ObjectValue = \"Hellojkadg\";\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Object value) { Assert.Equal(ObjectValue, value); }\n\n\t\t[Fact]\n\t\tpublic void ObjectValues_AreBeingDeserializedAsStrings()\n\t\t{\n\t\t\tCreateAndPerform(ObjectValue);\n\t\t}\n\n\t\tprivate static readonly DateTimeOffset DateTimeOffsetValue = new DateTimeOffset(new DateTime(2012, 12, 12), TimeSpan.FromHours(1));\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(DateTimeOffset value) {  Assert.Equal(DateTimeOffsetValue, value); }\n\n\t\t[Fact]\n\t\tpublic void DateTimeOffsetValues_AreBeingDeserializedCorrectly()\n\t\t{\n\t\t\t// Don't run this test on Mono – https://bugzilla.xamarin.com/show_bug.cgi?id=25158\n\t\t\tif (Type.GetType(\"Mono.Runtime\") == null)\n\t\t\t{\n\t\t\t\tCreateAndPerform(DateTimeOffsetValue);\n\t\t\t}\n\t\t}\n\n#if !NETCOREAPP1_0\n\t\tprivate static readonly CultureInfo CultureInfoValue = new CultureInfo(\"ru-RU\");\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(CultureInfo value) { Assert.Equal(CultureInfoValue, value); }\n\n\t\t[Fact]\n\t\tpublic void CultureInfoValues_AreBeingDeserializedCorrectly()\n\t\t{\n\t\t\tCreateAndPerform(CultureInfoValue);\n\t\t}\n#endif\n\n\t\tprivate const DayOfWeek EnumValue = DayOfWeek.Saturday;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(DayOfWeek value) { Assert.Equal(EnumValue, value); }\n\n\t\t[Fact]\n\t\tpublic void EnumValues_AreBeingDeserializedCorrectly()\n\t\t{\n\t\t\tCreateAndPerform(EnumValue);\n\t\t}\n\n\t\tprivate static readonly Guid GuidValue = Guid.NewGuid();\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Guid value) { Assert.Equal(GuidValue, value); }\n\n\t\t[Fact]\n\t\tpublic void GuidValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(GuidValue);\n\t\t}\n\n\t\tprivate static readonly Uri UriValue = new Uri(\"https://example.com\", UriKind.Absolute);\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Uri value) { Assert.Equal(UriValue, value); }\n\n\t\t[Fact]\n\t\tpublic void UriValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(UriValue);\n\t\t}\n\n\t\tprivate static readonly Int64? NotNullNullableValue = Int64.MaxValue;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Int64? value) { Assert.Equal(NotNullNullableValue, value); }\n\n\t\t[Fact]\n\t\tpublic void NotNullNullableValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(NotNullNullableValue);\n\t\t}\n\n\t\tprivate static readonly Int32? NullNullableValue = null;\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Int32? value) { Assert.Equal(NullNullableValue, value); }\n\n\t\t[Fact]\n\t\tpublic void NullNullableValues_AreBeingCorrectlyDeserialized()\n\t\t{\n\t\t\tCreateAndPerform(NullNullableValue);\n\t\t}\n\n\t\tprivate static readonly string[] ArrayValue = { \"Hello\", \"world\" };\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(string[] value) { Assert.Equal(ArrayValue, value); }\n\n\t\t[Fact]\n\t\tpublic void ArrayValues_AreBeingCorrectlyDeserialized_FromJson()\n\t\t{\n\t\t\tCreateAndPerform(ArrayValue, true);\n\t\t}\n\n\t\tprivate static readonly List<DateTime> ListValue = new List<DateTime> { DateTime.UtcNow.AddDays(-1), DateTime.UtcNow.AddDays(1) };\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(List<DateTime> value) { Assert.Equal(ListValue, value); }\n\n\t\t[Fact]\n\t\tpublic void ListValues_AreBeingCorrectlyDeserialized_FromJson()\n\t\t{\n\t\t\tCreateAndPerform(ListValue, true);\n\t\t}\n\n\t\tprivate static readonly Dictionary<TimeSpan, string> DictionaryValue = new Dictionary<TimeSpan, string>\n\t\t{\n\t\t\t{ TimeSpan.FromSeconds(1), \"123\" },\n\t\t\t{ TimeSpan.FromDays(12), \"376\" }\n\t\t};\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(Dictionary<TimeSpan, string> value) { Assert.Equal(DictionaryValue, value); }\n\n\t\t[Fact]\n\t\tpublic void DictionaryValues_AreBeingCorrectlyDeserialized_FromJson()\n\t\t{\n\t\t\tCreateAndPerform(DictionaryValue, true);\n\t\t}\n\n\t\tpublic struct MyStruct\n\t\t{\n\t\t\tpublic Guid Id { get; set; } \n\t\t\tpublic string Name { get; set; }\n\t\t}\n\n\t\tprivate static readonly MyStruct CustomStructValue = new MyStruct { Id = Guid.NewGuid(), Name = \"Hangfire\" };\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(MyStruct value) { Assert.Equal(CustomStructValue, value); }\n\n\t\t[Fact]\n\t\tpublic void CustomStructValues_AreBeingCorrectlyDeserialized_FromJson()\n\t\t{\n\t\t\tCreateAndPerform(CustomStructValue, true);\n\t\t}\n\n#pragma warning disable 659\n        public class MyClass : IEquatable<MyClass>\n\t\t{\n\t\t\tpublic DateTime CreatedAt { get; set; }\n\n\t\t\tpublic bool Equals(MyClass other)\n\t\t\t{\n\t\t\t\tif (other == null) return false;\n\t\t\t\treturn CreatedAt.Equals(other.CreatedAt);\n\t\t\t}\n\n\t\t\tpublic override bool Equals(object obj)\n\t\t\t{\n\t\t\t\treturn Equals(obj as MyClass);\n\t\t\t}\n\t\t}\n#pragma warning restore 659\n\n\t\tprivate static readonly MyClass CustomClassValue = new MyClass { CreatedAt = DateTime.UtcNow };\n\n\t\t[UsedImplicitly]\n\t\t[SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n\t\t[SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n\t\tpublic void Method(MyClass value) { Assert.Equal(CustomClassValue.CreatedAt, value.CreatedAt); }\n\n\t\t[Fact]\n\t\tpublic void CustomClassValues_AreBeingCorrectlyDeserialized_FromJson()\n\t\t{\n\t\t\tCreateAndPerform(CustomClassValue, true);\n\t\t}\n\n\t\tprivate static void CreateAndPerform<T>(T argumentValue, bool checkJsonOnly = false)\n\t\t{\n\t\t\tvar type = typeof(JobArgumentFacts);\n\t\t\tvar methodInfo = type.GetMethod(\"Method\", new[] { typeof(T) });\n\n\t\t\tvar serializationMethods = new List<Tuple<string, Func<string>>>();\n\n#if !NETCOREAPP1_0\n\t\t\tif (!checkJsonOnly)\n\t\t\t{\n\t\t\t\tvar converter = TypeDescriptor.GetConverter(typeof(T));\n\t\t\t\tserializationMethods.Add(new Tuple<string, Func<string>>(\n\t\t\t\t\t\"TypeDescriptor\",\n\t\t\t\t\t() => converter.ConvertToInvariantString(argumentValue)));\n\t\t\t}\n#endif\n\n\t\t\tserializationMethods.Add(new Tuple<string, Func<string>>(\n\t\t\t\t\"JSON\",\n\t\t\t\t() => JsonConvert.SerializeObject(argumentValue)));\n\n\t\t\tforeach (var method in serializationMethods)\n\t\t\t{\n\t\t\t\tvar data = new InvocationData(\n\t\t\t\t\tmethodInfo?.DeclaringType?.AssemblyQualifiedName,\n\t\t\t\t\tmethodInfo?.Name,\n\t\t\t\t\tJobHelper.ToJson(methodInfo?.GetParameters().Select(x => x.ParameterType).ToArray()),\n\t\t\t\t\tJobHelper.ToJson(new[] { method.Item2() }));\n\n\t\t\t\tvar job = data.DeserializeJob();\n\n\t\t\t\tAssert.Equal(argumentValue, job.Args[0]);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/JobFacts.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Moq;\nusing Newtonsoft.Json;\nusing Xunit;\n\n// ReSharper disable LocalizableElement\n// ReSharper disable AssignNullToNotNullAttribute\n\n#pragma warning disable 618\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class JobFacts\n    {\n        private static readonly DateTime SomeDateTime = new DateTime(2014, 5, 30, 12, 0, 0);\n        private static bool _methodInvoked;\n        private static bool _disposed;\n\n        private readonly Type _type;\n        private readonly MethodInfo _method;\n        private readonly object[] _arguments;\n        private readonly string _queue;\n        private readonly Mock<JobActivator> _activator;\n        private readonly Mock<IJobCancellationToken> _token;\n        \n        public JobFacts()\n        {\n            _type = typeof (JobFacts);\n            _method = _type.GetMethod(\"StaticMethod\");\n            _arguments = new object[0];\n            _queue = \"critical\";\n\n            _activator = new Mock<JobActivator> { CallBase = true };\n            _token = new Mock<IJobCancellationToken>();\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenTheTypeIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                // ReSharper disable once AssignNullToNotNullAttribute\n                () => new Job(null, _method, _arguments));\n\n            Assert.Equal(\"type\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenTheMethodIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                // ReSharper disable once AssignNullToNotNullAttribute\n                () => new Job(_type, null, _arguments));\n\n            Assert.Equal(\"method\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenTheTypeDoesNotContainTheGivenMethod()\n        {\n            var exception = Assert.Throws<ArgumentException>(\n                () => new Job(typeof(Job), _method, _arguments));\n\n            Assert.Equal(\"type\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenArgumentsArrayIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                // ReSharper disable once AssignNullToNotNullAttribute\n                () => new Job(_type, _method, (object[])null));\n\n            Assert.Equal(\"args\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_DoesNotThrow_WhenQueueIsNull()\n        {\n            var job = new Job(_type, _method, _arguments, null);\n            Assert.NotNull(job);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenQueueValidationFails()\n        {\n            var exception = Assert.Throws<ArgumentException>(\n                () => new Job(_type, _method, _arguments, \"&^*%\"));\n\n            Assert.Equal(\"queue\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_InitializesAllProperties()\n        {\n            var job = new Job(_type, _method, _arguments);\n\n            Assert.Same(_type, job.Type);\n            Assert.Same(_method, job.Method);\n            Assert.True(_arguments.SequenceEqual(job.Arguments));\n            Assert.Null(job.Queue);\n        }\n\n        [Fact]\n        public void Ctor_WithQueue_InitializesAllTheProperties()\n        {\n            var job = new Job(_type, _method, _arguments, _queue);\n\n            Assert.Same(_type, job.Type);\n            Assert.Same(_method, job.Method);\n            Assert.True(_arguments.SequenceEqual(job.Args));\n            Assert.Equal(_queue, job.Queue);\n        }\n\n        [Fact]\n        public void Ctor_HasDefaultValueForArguments()\n        {\n            var job = new Job(_type, _method);\n\n            Assert.Empty(job.Arguments);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenArgumentCountIsNotEqualToParameterCount()\n        {\n            var exception = Assert.Throws<ArgumentException>(\n                () => new Job(_type, _method, new[] { \"hello!\" }));\n\n            Assert.Contains(\"count\", exception.Message);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenMethodContains_UnassignedGenericTypeParameters()\n        {\n            var method = _type.GetMethod(\"GenericMethod\");\n\n            Assert.Throws<NotSupportedException>(\n                () => new Job(_type, method, new[] { \"hello!\" }));\n        }\n\n        [Fact]\n        public void Ctor_CanUsePropertyValues_OfAnotherJob_AsItsArguments()\n        {\n            var method = _type.GetMethod(\"MethodWithArguments\");\n            var job = new Job(_type, method, \"hello\", 456);\n\n            var anotherJob = new Job(job.Type, job.Method, job.Args);\n\n            Assert.Equal(_type, anotherJob.Type);\n            Assert.Equal(method, anotherJob.Method);\n            Assert.Equal(\"hello\", anotherJob.Args[0]);\n            Assert.Equal(456, anotherJob.Args[1]);\n        }\n\n        [Fact]\n        public void FromExpression_Action_ThrowsException_WhenNullExpressionProvided()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => Job.FromExpression((Expression<Action>)null));\n\n            Assert.Equal(\"methodCall\", exception.ParamName);\n        }\n\n        [Fact]\n        public void FromExpression_Action_DoesNotThrowAnException_WhenNullQueueProvided()\n        {\n            var job = Job.FromExpression(() => Console.WriteLine(), null);\n            Assert.NotNull(job);\n        }\n\n        [Fact]\n        public void FromExpression_ThrowsAnException_WhenNewExpressionIsGiven()\n        {\n            Assert.Throws<ArgumentException>(\n                // ReSharper disable once ObjectCreationAsStatement\n                () => Job.FromExpression(() => new JobFacts()));\n        }\n\n        [Fact]\n        public void FromExpression_Action_ReturnsTheJob()\n        {\n            var job = Job.FromExpression(() => Console.WriteLine());\n\n            Assert.Equal(typeof(Console), job.Type);\n            Assert.Equal(\"WriteLine\", job.Method.Name);\n            Assert.Null(job.Queue);\n        }\n\n        [Fact]\n        public void FromExpression_ActionWithQueue_ReturnsTheJobWithQueueSet()\n        {\n            var job = Job.FromExpression(() => Console.WriteLine(), \"critical\");\n\n            Assert.Equal(typeof(Console), job.Type);\n            Assert.Equal(\"WriteLine\", job.Method.Name);\n            Assert.Equal(\"critical\", job.Queue);\n        }\n\n        [Fact]\n        public void FromExpression_Func_ThrowsException_WhenNullExpressionProvided()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => Job.FromExpression((Expression<Func<Task>>)null));\n\n            Assert.Equal(\"methodCall\", exception.ParamName);\n        }\n\n        [Fact]\n        public void FromExpression_Func_DoesNotThrowAnException_WhenNullQueueProvided()\n        {\n            var job = Job.FromExpression(() => AsyncMethod(), null);\n            Assert.NotNull(job);\n        }\n\n        [Fact]\n        public void FromExpression_Func_ReturnsTheJob()\n        {\n            var job = Job.FromExpression(() => AsyncMethod());\n\n            Assert.Equal(typeof(JobFacts), job.Type);\n            Assert.Equal(\"AsyncMethod\", job.Method.Name);\n            Assert.Null(job.Queue);\n        }\n\n        [Fact]\n        public void FromExpression_FuncWithQueue_ReturnsTheJobWithQueueSet()\n        {\n            var job = Job.FromExpression(() => AsyncMethod(), \"critical\");\n\n            Assert.Equal(typeof(JobFacts), job.Type);\n            Assert.Equal(\"AsyncMethod\", job.Method.Name);\n            Assert.Equal(\"critical\", job.Queue);\n        }\n\n        [Fact]\n        public void FromExpression_ConvertsDateTimeRepresentation_ToIso8601Format()\n        {\n            var date = new DateTime(2014, 5, 30, 12, 0, 0, 777);\n            var expected = date.ToString(\"o\");\n\n            var job = Job.FromExpression(() => MethodWithDateTimeArgument(date));\n\n            Assert.Equal(expected, job.Arguments[0]);\n        }\n\n        [Fact]\n        public void FromExpression_ConvertsArgumentsToJson()\n        {\n            var job = Job.FromExpression(() => MethodWithArguments(\"123\", 1));\n\n            Assert.Equal(\"\\\"123\\\"\", job.Arguments[0]);\n            Assert.Equal(\"1\", job.Arguments[1]);\n        }\n\n        [Fact]\n        public void FromExpression_ConvertsObjectArgumentsToJson()\n        {\n            var job = Job.FromExpression(() => MethodWithObjectArgument(\"hello\"));\n\n            Assert.Equal(\"\\\"hello\\\"\", job.Arguments[0]);\n        }\n\n        [Fact]\n        public void FromExpression_ReturnValueDoesNotDepend_OnCurrentCulture()\n        {\n            var date = DateTime.UtcNow;\n\n            CultureHelper.SetCurrentCulture(\"en-US\");\n            var enJob = Job.FromExpression(() => MethodWithDateTimeArgument(date));\n\n            CultureHelper.SetCurrentCulture(\"ru-RU\");\n            var ruJob = Job.FromExpression(() => MethodWithDateTimeArgument(date));\n\n            Assert.Equal(enJob.Arguments[0], ruJob.Arguments[0]);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenMethodIsAsyncVoid()\n        {\n            var method = typeof(JobFacts).GetMethod(nameof(AsyncVoidMethod));\n\n            Assert.Throws<NotSupportedException>(\n                () => new Job(typeof(JobFacts), method, new string[0]));\n        }\n\n        [Fact]\n        public void FromInstanceExpression_Action_ThrowsException_WhenNullExpressionIsProvided()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => Job.FromExpression((Expression<Action<JobFacts>>)null));\n\n            Assert.Equal(\"methodCall\", exception.ParamName);\n        }\n\n        [Fact]\n        public void FromInstanceExpression_Action_DoesNotThrowAnException_WhenNullQueueIsProvided()\n        {\n            var job = Job.FromExpression<Instance>(x => x.Method(), null);\n            Assert.NotNull(job);\n        }\n\n        [Fact]\n        public void FromInstanceExpression_Func_ThrowsException_WhenNullExpressionIsProvided()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => Job.FromExpression((Expression<Func<JobFacts, Task>>)null));\n\n            Assert.Equal(\"methodCall\", exception.ParamName);\n        }\n\n        [Fact]\n        public void FromInstanceExpression_Func_DoesNotThrowAnException_WhenNullQueueIsProvided()\n        {\n            var job = Job.FromExpression<Instance>(x => x.FunctionReturningTask(), null);\n            Assert.NotNull(job);\n        }\n\n        [Fact]\n        public void FromInstanceExpression_ThrowsAnException_WhenNewExpressionIsGiven()\n        {\n            Assert.Throws<ArgumentException>(\n                // ReSharper disable once ObjectCreationAsStatement\n                () => Job.FromExpression<JobFacts>(x => new JobFacts()));\n        }\n\n        [Fact]\n        public void FromInstanceExpression_Action_ReturnsCorrectResult()\n        {\n            var job = Job.FromExpression<Instance>(x => x.Method());\n\n            Assert.Equal(typeof(Instance), job.Type);\n            Assert.Equal(\"Method\", job.Method.Name);\n            Assert.Null(job.Queue);\n        }\n\n        [Fact]\n        public void FromInstanceExpression_ActionWithQueue_ReturnsCorrectResultWithQueueSet()\n        {\n            var job = Job.FromExpression<Instance>(x => x.Method(), \"critical\");\n\n            Assert.Equal(typeof(Instance), job.Type);\n            Assert.Equal(\"Method\", job.Method.Name);\n            Assert.Equal(\"critical\", job.Queue);\n        }\n\n        [Fact]\n        public void FromInstanceExpression_Func_ReturnsCorrectResult()\n        {\n            var job = Job.FromExpression<Instance>(x => x.FunctionReturningTask());\n\n            Assert.Equal(typeof(Instance), job.Type);\n            Assert.Equal(\"FunctionReturningTask\", job.Method.Name);\n            Assert.Null(job.Queue);\n        }\n\n        [Fact]\n        public void FromInstanceExpression_FuncWithQueue_ReturnsCorrectResultWithQueueSet()\n        {\n            var job = Job.FromExpression<Instance>(x => x.FunctionReturningTask(), \"critical\");\n\n            Assert.Equal(typeof(Instance), job.Type);\n            Assert.Equal(\"FunctionReturningTask\", job.Method.Name);\n            Assert.Equal(\"critical\", job.Queue);\n        }\n\n        [Fact]\n        public void FromNonGenericExpression_InfersType_FromAGivenObject()\n        {\n            IDisposable instance = new Instance();\n            var job = Job.FromExpression(() => instance.Dispose());\n\n            Assert.Equal(typeof(Instance), job.Type);\n        }\n\n        [Fact]\n        public void FromNonGenericExpression_InfersACorrectMethod_FromAGivenObject_WhenInterfaceTreeIsUsed()\n        {\n            IDisposable instance = new Instance();\n            var job = Job.FromExpression(() => instance.Dispose());\n\n            Assert.Equal(typeof(Instance), job.Method.DeclaringType);\n        }\n\n        [Fact]\n        public void FromNonGenericExpression_ThrowsAnException_IfGivenObjectIsNull()\n        {\n            IDisposable instance = null;\n\n            Assert.Throws<InvalidOperationException>(\n                () => Job.FromExpression(() => instance.Dispose()));\n        }\n\n        [Fact]\n        public void FromGenericExpression_InfersType_FromAGivenObject_AndHandlesAssignableParameters()\n        {\n            IServiceInterface<MyDerivedClass> service = new MyBaseClassService();\n            MyDerivedClass myClass = new MyDerivedClass();\n\n            var job = Job.FromExpression(() => service.MyMethod(myClass));\n\n            Assert.Equal(typeof(MyBaseClassService), job.Type);\n            Assert.Equal(\"MyMethod\", job.Method.Name);\n            Assert.Equal(typeof(MyBaseClassService), job.Method.DeclaringType);\n        }\n\n        [Fact]\n        public void FromScopedExpression_HandlesGenericMethods()\n        {\n            CommandDispatcher dispatcher = new CommandDispatcher();\n            var job = Job.FromExpression(() => dispatcher.DispatchTyped(123));\n\n            Assert.Equal(typeof(CommandDispatcher), job.Type);\n            Assert.Equal(typeof(CommandDispatcher), job.Method.DeclaringType);\n        }\n\n        [Fact]\n        public void FromScopedExpression_HandlesMethodsDeclaredInBaseClasse()\n        {\n            DerivedInstance instance = new DerivedInstance();\n            var job = Job.FromExpression(() => instance.Method());\n\n            Assert.Equal(typeof(DerivedInstance), job.Type);\n            Assert.Equal(typeof(Instance), job.Method.DeclaringType);\n        }\n\n        [Fact]\n        public void FromScopedExpression_ThrowsWhenExplicitInterfaceImplementationIsPassed()\n        {\n            IService service = new ServiceImpl();\n            Assert.Throws<NotSupportedException>(() => Job.FromExpression(() => service.Method()));\n        }\n\n        public interface IService\n        {\n            void Method();\n        }\n\n        public class ServiceImpl : IService\n        {\n            void IService.Method()\n            {\n            }\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenMethodContainsReferenceParameter()\n        {\n            string test = null;\n            Assert.Throws<NotSupportedException>(\n                () => Job.FromExpression(() => MethodWithReferenceParameter(ref test)));\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenMethodContainsOutputParameter()\n        {\n            string test;\n            Assert.Throws<NotSupportedException>(\n                () => Job.FromExpression(() => MethodWithOutputParameter(out test)));\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenMethodIsNotPublic()\n        {\n            Assert.Throws<NotSupportedException>(\n                () => Job.FromExpression(() => PrivateMethod()));\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenMethodParametersContainADelegate()\n        {\n            Assert.Throws<NotSupportedException>(\n                () => Job.FromExpression(() => DelegateMethod(() => Console.WriteLine(\"Hey delegate!\"))));\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenMethodParametersContainAnExpression()\n        {\n            Assert.Throws<NotSupportedException>(\n                () => Job.FromExpression(() => ExpressionMethod(() => Console.WriteLine(\"Hey expression!\"))));\n        }\n\n        [Fact]\n        public void Perform_ThrowsAnException_WhenActivatorIsNull()\n        {\n            var job = Job.FromExpression(() => StaticMethod());\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => job.Perform(null, _token.Object));\n\n            Assert.Equal(\"activator\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Perform_ThrowsAnException_WhenCancellationTokenIsNull()\n        {\n            var job = Job.FromExpression(() => StaticMethod());\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => job.Perform(_activator.Object, null));\n\n            Assert.Equal(\"cancellationToken\", exception.ParamName);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_CanInvokeStaticMethods()\n        {\n            _methodInvoked = false;\n            var job = Job.FromExpression(() => StaticMethod());\n\n            job.Perform(_activator.Object, _token.Object);\n\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_CanInvokeInstanceMethods()\n        {\n            _methodInvoked = false;\n            var job = Job.FromExpression<Instance>(x => x.Method());\n\n            job.Perform(_activator.Object, _token.Object);\n\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_DisposesDisposableInstance_AfterPerformance()\n        {\n            _disposed = false;\n            var job = Job.FromExpression<Instance>(x => x.Method());\n\n            job.Perform(_activator.Object, _token.Object);\n\n            Assert.True(_disposed);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_PassesArguments_ToACallingMethod()\n        {\n            // Arrange\n            _methodInvoked = false;\n            var job = Job.FromExpression(() => MethodWithArguments(\"hello\", 5));\n\n            // Act\n            job.Perform(_activator.Object, _token.Object);\n\n            // Assert - see the `MethodWithArguments` method.\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_PassesObjectArguments_ToACallingMethod()\n        {\n            // Arrange\n            _methodInvoked = false;\n            var job = Job.FromExpression(() => MethodWithObjectArgument(\"5\"));\n\n            // Act\n            job.Perform(_activator.Object, _token.Object);\n\n            // Assert - see the `MethodWithArguments` method.\n            Assert.True(_methodInvoked);\n        }\n\n#if !NETCOREAPP1_0\n        [Fact, StaticLock]\n        public void Perform_PassesCorrectDateTime_IfItWasSerialized_UsingTypeConverter()\n        {\n            // Arrange\n            _methodInvoked = false;\n            var typeConverter = TypeDescriptor.GetConverter(typeof (DateTime));\n            var convertedDate = typeConverter.ConvertToInvariantString(SomeDateTime);\n\n            var type = typeof (JobFacts);\n            var method = type.GetMethod(\"MethodWithDateTimeArgument\");\n\n            var job = new Job(type, method, new[] { convertedDate });\n\n            // Act\n            job.Perform(_activator.Object, _token.Object);\n\n            // Assert - see also the `MethodWithDateTimeArgument` method.\n            Assert.True(_methodInvoked);\n        }\n#endif\n\n        [Fact, StaticLock]\n        public void Perform_PassesCorrectDateTime_IfItWasSerialized_UsingOldFormat()\n        {\n            // Arrange\n            _methodInvoked = false;\n            var convertedDate = SomeDateTime.ToString(\"MM/dd/yyyy HH:mm:ss.ffff\");\n\n            var type = typeof(JobFacts);\n            var method = type.GetMethod(\"MethodWithDateTimeArgument\");\n\n            var job = new Job(type, method, new[] { convertedDate });\n\n            // Act\n            job.Perform(_activator.Object, _token.Object);\n\n            // Assert - see also the `MethodWithDateTimeArgument` method.\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_PassesCorrectDateTimeArguments()\n        {\n            // Arrange\n            _methodInvoked = false;\n            var job = Job.FromExpression(() => MethodWithDateTimeArgument(SomeDateTime));\n\n            // Act\n            job.Perform(_activator.Object, _token.Object);\n\n            // Assert - see also the `MethodWithDateTimeArgument` method.\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_WorksCorrectly_WithNullValues()\n        {\n            // Arrange\n            _methodInvoked = false;\n            var job = Job.FromExpression(() => NullArgumentMethod(null));\n\n            // Act\n            job.Perform(_activator.Object, _token.Object);\n\n            // Assert - see also `NullArgumentMethod` method.\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact]\n        public void Perform_ThrowsPerformanceException_WhenActivatorThrowsAnException()\n        {\n            var exception = new InvalidOperationException();\n            _activator.Setup(x => x.ActivateJob(It.IsAny<Type>())).Throws(exception);\n\n            var job = Job.FromExpression(() => InstanceMethod());\n\n            var thrownException = Assert.Throws<JobPerformanceException>(\n                () => job.Perform(_activator.Object, _token.Object));\n\n            Assert.Same(exception, thrownException.InnerException);\n        }\n\n        [Fact]\n        public void Perform_ThrowsPerformanceException_WhenActivatorReturnsNull()\n        {\n            _activator.Setup(x => x.ActivateJob(It.IsNotNull<Type>())).Returns(null);\n            var job = Job.FromExpression(() => InstanceMethod());\n\n            var thrownException = Assert.Throws<JobPerformanceException>(\n                () => job.Perform(_activator.Object, _token.Object));\n\n            Assert.IsType<InvalidOperationException>(thrownException.InnerException);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsJsonReaderException_OnArgumentsDeserializationFailure()\n        {\n            var type = typeof (JobFacts);\n            var method = type.GetMethod(\"MethodWithDateTimeArgument\");\n\n            Assert.Throws<JsonReaderException>(\n                () => new Job(type, method, new []{ JobHelper.ToJson(\"sdfa\") }));\n        }\n\n        [Fact, StaticLock]\n        public void Perform_ThrowsPerformanceException_OnDisposalFailure()\n        {\n            _methodInvoked = false;\n\n            var job = Job.FromExpression<BrokenDispose>(x => x.Method());\n\n            var exception = Assert.Throws<JobPerformanceException>(\n                () => job.Perform(_activator.Object, _token.Object));\n\n            Assert.True(_methodInvoked);\n            Assert.NotNull(exception.InnerException);\n        }\n\n        [Fact]\n        public void Perform_ThrowsPerformanceException_WithUnwrappedInnerException()\n        {\n            var job = Job.FromExpression(() => ExceptionMethod());\n\n            var thrownException = Assert.Throws<JobPerformanceException>(\n                () => job.Perform(_activator.Object, _token.Object));\n\n            Assert.IsType<InvalidOperationException>(thrownException.InnerException);\n            Assert.Equal(\"exception\", thrownException.InnerException.Message);\n        }\n\n        [Fact]\n        public void Perform_ThrowsPerformanceException_WhenAMethodThrowsTaskCanceledException()\n        {\n            var job = Job.FromExpression(() => TaskCanceledExceptionMethod());\n\n            var thrownException = Assert.Throws<JobPerformanceException>(\n                () => job.Perform(_activator.Object, _token.Object));\n\n            Assert.IsType<TaskCanceledException>(thrownException.InnerException);\n        }\n\n        [Fact]\n        public void Perform_RethrowsOperationCanceledException_WhenShutdownTokenIsCanceled()\n        {\n            // Arrange\n            var job = Job.FromExpression(() => CancelableJob(JobCancellationToken.Null));\n            _token.Setup(x => x.ShutdownToken).Returns(new CancellationToken(true));\n            _token.Setup(x => x.ThrowIfCancellationRequested()).Throws<OperationCanceledException>();\n\n            // Act & Assert\n            Assert.Throws<OperationCanceledException>(() => job.Perform(_activator.Object, _token.Object));\n        }\n\n        [Fact]\n        public void Run_RethrowsTaskCanceledException_WhenShutdownTokenIsCanceled()\n        {\n            // Arrange\n            var job = Job.FromExpression(() => CancelableJob(JobCancellationToken.Null));\n            _token.Setup(x => x.ShutdownToken).Returns(new CancellationToken(true));\n            _token.Setup(x => x.ThrowIfCancellationRequested()).Throws<TaskCanceledException>();\n\n            // Act & Assert\n            Assert.Throws<TaskCanceledException>(() => job.Perform(_activator.Object, _token.Object));\n        }\n\n        [Fact]\n        public void Run_RethrowsJobAbortedException()\n        {\n            // Arrange\n            var job = Job.FromExpression(() => CancelableJob(JobCancellationToken.Null));\n            _token.Setup(x => x.ShutdownToken).Returns(CancellationToken.None);\n            _token.Setup(x => x.ThrowIfCancellationRequested()).Throws<JobAbortedException>();\n\n            // Act & Assert\n            Assert.Throws<JobAbortedException>(() => job.Perform(_activator.Object, _token.Object));\n        }\n\n        [Fact]\n        public void Run_ThrowsJobPerformanceException_InsteadOfOperationCanceled_WhenShutdownWasNOTInitiated()\n        {\n            // Arrange\n            var job = Job.FromExpression(() => CancelableJob(JobCancellationToken.Null));\n            _token.Setup(x => x.ShutdownToken).Returns(CancellationToken.None);\n            _token.Setup(x => x.ThrowIfCancellationRequested()).Throws<OperationCanceledException>();\n\n            // Act & Assert\n            Assert.Throws<JobPerformanceException>(() => job.Perform(_activator.Object, _token.Object));\n        }\n\n        [Fact]\n        public void Perform_ReturnsValue_WhenCallingFunctionReturningValue()\n        {\n            var job = Job.FromExpression<Instance>(x => x.FunctionReturningValue());\n\n            var result = job.Perform(_activator.Object, _token.Object);\n\n            Assert.Equal(\"Return value\", result);\n        }\n\n        [Fact]\n        public void GetTypeFilterAttributes_ReturnsCorrectAttributes()\n        {\n            var job = Job.FromExpression<Instance>(x => x.Method());\n            var nonCachedAttributes = job.GetTypeFilterAttributes(false).ToArray();\n            var cachedAttributes = job.GetTypeFilterAttributes(true).ToArray();\n\n            Assert.Single(nonCachedAttributes);\n            Assert.Single(cachedAttributes);\n\n            Assert.True(nonCachedAttributes[0] is TestTypeAttribute);\n            Assert.True(cachedAttributes[0] is TestTypeAttribute);\n        }\n\n        [Fact]\n        public void GetMethodFilterAttributes_ReturnsCorrectAttributes()\n        {\n            var job = Job.FromExpression<Instance>(x => x.Method());\n            var nonCachedAttributes = job.GetMethodFilterAttributes(false).ToArray();\n            var cachedAttributes = job.GetMethodFilterAttributes(true).ToArray();\n\n            Assert.Single(nonCachedAttributes);\n            Assert.Single(cachedAttributes);\n\n            Assert.True(nonCachedAttributes[0] is TestMethodAttribute);\n            Assert.True(cachedAttributes[0] is TestMethodAttribute);\n        }\n\n        private static void PrivateMethod()\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void MethodWithReferenceParameter(ref string a)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void MethodWithOutputParameter(out string a)\n        {\n            a = \"hello\";\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void StaticMethod()\n        {\n            _methodInvoked = true;\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        public void InstanceMethod()\n        {\n            _methodInvoked = true;\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void CancelableJob(IJobCancellationToken token)\n        {\n            token.ThrowIfCancellationRequested();\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void NullArgumentMethod(string[] argument)\n        {\n            _methodInvoked = true;\n            Assert.Null(argument);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public void MethodWithArguments(string stringArg, int intArg)\n        {\n            _methodInvoked = true;\n\n            Assert.Equal(\"hello\", stringArg);\n            Assert.Equal(5, intArg);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public void MethodWithObjectArgument(object argument)\n        {\n            _methodInvoked = true;\n\n            Assert.Equal(\"5\", argument);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        public void MethodWithCustomArgument(Instance argument)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        public void MethodWithDateTimeArgument(DateTime argument)\n        {\n            _methodInvoked = true;\n\n            Assert.Equal(SomeDateTime, argument);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void ExceptionMethod()\n        {\n            throw new InvalidOperationException(\"exception\");\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void TaskCanceledExceptionMethod()\n        {\n            throw new TaskCanceledException();\n        }\n\n        [UsedImplicitly]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        public void GenericMethod<T>(T arg)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        public Task AsyncMethod()\n        {\n            var source = new TaskCompletionSource<bool>();\n            return source.Task;\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        public async void AsyncVoidMethod()\n        {\n            await Task.Yield();\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public void DelegateMethod(Action action)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        public void ExpressionMethod(Expression<Action> expression)\n        {\n        }\n\n        private interface ICommandDispatcher\n        {\n            void DispatchTyped<TCommand>(TCommand command);\n        }\n\n        private sealed class CommandDispatcher : ICommandDispatcher\n        {\n            public void DispatchTyped<TCommand>(TCommand command)\n            {\n            }\n        }\n\n        [TestType]\n        public class Instance : IDisposable\n        {\n            [TestMethod]\n            public void Method()\n            {\n                _methodInvoked = true;\n            }\n\n            public void Dispose()\n            {\n                _disposed = true;\n            }\n\n            public string FunctionReturningValue()\n            {\n                return \"Return value\";\n            }\n\n            public async Task FunctionReturningTask()\n            {\n                await Task.Yield();\n            }\n\n            public async Task FunctionReturningValueTask()\n            {\n                await Task.Yield();\n            }\n\n            public async Task<string> FunctionReturningTaskResultingInString(bool continueOnCapturedContext)\n            {\n                await Task.Yield();\n                await Task.Delay(15).ConfigureAwait(continueOnCapturedContext);\n\n                return FunctionReturningValue();\n            }\n            \n            public ValueTask<string> FunctionReturningValueTaskResultingInString(bool continueOnCapturedContext)\n            {\n                return new ValueTask<string>(FunctionReturningTaskResultingInString(continueOnCapturedContext));\n            }\n        }\n\n        public class DerivedInstance : Instance\n        {\n        }\n\n        public class BrokenDispose : IDisposable\n        {\n            public void Method()\n            {\n                _methodInvoked = true;\n            }\n\n            public void Dispose()\n            {\n                throw new InvalidOperationException();\n            }\n        }\n\n        // ReSharper disable once UnusedTypeParameter\n        public class JobClassWrapper<T> : IDisposable where T : IDisposable\n        {\n            public void Dispose()\n            {\n            }\n        }\n\n        public class TestTypeAttribute : JobFilterAttribute\n        {\n        }\n\n        public class TestMethodAttribute : JobFilterAttribute\n        {\n        }\n\n        class MyBaseClass\n        {\n            public string BaseProp { get; set; }\n        }\n\n        class MyDerivedClass : MyBaseClass\n        {\n            public int MyProperty { get; set; }\n        }\n\n        interface IServiceInterface<in T> where T : MyBaseClass\n        {\n            Task MyMethod(T input);\n        }\n\n        class MyBaseClassService : IServiceInterface<MyBaseClass>\n        {\n            public Task MyMethod(MyBaseClass input)\n            {\n                return Task.FromResult(true);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/JobFilterAttributeFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class JobFilterAttributeFacts\n    {\n        [Fact]\n        public void SetOrder_ThrowsAnException_WhenValueIsLessThanDefaultOrder()\n        {\n            var filterAttribute = new Mock<JobFilterAttribute> { CallBase = true };\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => filterAttribute.Object.Order = -2);\n        }\n\n        [Fact]\n        public void TypeId_Property_IsNotIncludedIntoSerializedForm()\n        {\n            var attribute = new SampleJobAttribute();\n            var serialized = SerializationHelper.Serialize(attribute);\n            Assert.DoesNotContain(\"TypeId\", serialized);\n        }\n\n        [Fact]\n        public void AllowMultiple_Property_IsNotIncludedIntoSerializedForm_SinceItIsGetOnlyProperty()\n        {\n            var attribute = new SampleJobAttribute();\n            var serialized = SerializationHelper.Serialize(attribute);\n            Assert.DoesNotContain(\"AllowMultiple\", serialized);\n        }        \n\n        [Fact]\n        public void Order_Property_IsNotIncludedIntoSerializedForm_WhenDefaultValueIsUsed()\n        {\n            var attribute = new SampleJobAttribute();\n            var serialized = SerializationHelper.Serialize(attribute);\n            Assert.DoesNotContain(\"Order\", serialized);\n        }\n\n        [Fact]\n        public void Order_Property_IsIncludedIntoSerializedForm_WhenNonDefaultValueIsUsed()\n        {\n            var attribute = new SampleJobAttribute { Order = 555 };\n            var serialized = SerializationHelper.Serialize(attribute);\n            Assert.Contains(\"\\\"Order\\\":555\", serialized);\n        }\n\n        [Fact]\n        public void Order_Property_ProperlyHandlesDefaultValue_WhenBeingDeserialized()\n        {\n            var attribute = SerializationHelper.Deserialize<SampleJobAttribute>(\"{}\");\n            Assert.Equal(-1, attribute.Order);\n        }\n\n        private sealed class SampleJobAttribute : JobFilterAttribute\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/JobFilterAttributeFilterProviderFacts.cs",
    "content": "﻿using System.Linq;\nusing Hangfire.Common;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class JobFilterAttributeFilterProviderFacts\n    {\n        [Fact]\n        public void GetFilters_WithNullJob_ReturnsEmptyList()\n        {\n            // Arrange\n            var provider = new JobFilterAttributeFilterProvider();\n\n            // Act\n            var result = provider.GetFilters(null);\n\n            // Assert\n            Assert.Empty(result);\n        }\n\n        [MyFilter(Order = 2112)]\n// ReSharper disable once ClassNeverInstantiated.Local\n        private class ClassWithTypeAttribute\n        {\n            public static void Method() { }\n        }\n\n        [Fact]\n        public void GetFilters_IncludesAttributesOnClassType()\n        {\n            // Arrange\n            var job = Job.FromExpression(() => ClassWithTypeAttribute.Method());\n            var provider = new JobFilterAttributeFilterProvider();\n\n            // Act\n            var filter = provider.GetFilters(job).Single();\n\n            // Assert\n            var attribute = filter.Instance as MyFilterAttribute;\n            Assert.NotNull(attribute);\n            Assert.Equal(JobFilterScope.Type, filter.Scope);\n            Assert.Equal(2112, filter.Order);\n        }\n\n// ReSharper disable once ClassNeverInstantiated.Local\n        private class ClassWithActionAttribute\n        {\n            [MyFilter(Order = 1234)]\n            public static void Method()\n            {\n            }\n        }\n\n        [Fact]\n        public void GetFilters_IncludesAttributesOMethod()\n        {\n            // Arrange\n            var job = Job.FromExpression(() => ClassWithActionAttribute.Method());\n            var provider = new JobFilterAttributeFilterProvider();\n\n            // Act\n            var filter = provider.GetFilters(job).Single();\n\n            // Assert\n            var attribute = filter.Instance as MyFilterAttribute;\n            Assert.NotNull(attribute);\n            Assert.Equal(JobFilterScope.Method, filter.Scope);\n            Assert.Equal(1234, filter.Order);\n        }\n\n        private abstract class BaseClass\n        {\n            public void MyMethod()\n            {\n            }\n        }\n\n        [MyFilter]\n// ReSharper disable once ClassNeverInstantiated.Local\n        private class DerivedClass : BaseClass\n        {\n        }\n\n        [Fact]\n        public void GetFilters_IncludesTypeAttributesFromDerivedTypeWhenMethodIsOnBaseClass()\n        { \n            // Arrange\n            var job = Job.FromExpression<DerivedClass>(x => x.MyMethod());\n            var provider = new JobFilterAttributeFilterProvider();\n\n            // Act\n            var filters = provider.GetFilters(job);\n\n            // Assert\n            Assert.NotNull(filters.Select(f => f.Instance).Cast<MyFilterAttribute>().Single());\n        }\n\n        private class MyFilterAttribute : JobFilterAttribute\n        {\n        }\n\n        [Fact]\n        public void GetFilters_RetrievesNonCachedAttributesWhenConfiguredNotTo()\n        {\n            // Arrange\n            var job = Job.FromExpression<DerivedClass>(x => x.MyMethod());\n            var provider = new JobFilterAttributeFilterProvider(false);\n\n            // Act\n            var filters = provider.GetFilters(job);\n\n            // Assert\n            Assert.NotNull(filters.Select(f => f.Instance).Cast<MyFilterAttribute>().Single());\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/JobFilterCollectionFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Hangfire.States;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class JobFilterCollectionFacts\n    {\n        private readonly JobFilterCollection _collection = new JobFilterCollection();\n        private readonly object _filterInstance = GetFilterInstance<IClientFilter>();\n\n        public static IEnumerable<object[]> AddRejectsNonFilterInstancesData => new List<object[]>\n        {\n            new object[] { \"string\" },\n            new object[] { 42 },\n            new object[] { new JobFilterCollectionFacts() },\n        };\n\n        [Fact]\n        public void AddRejectsNonFilterInstances()\n        {\n            foreach (var instance in AddRejectsNonFilterInstancesData)\n            {\n                // Act + Assert\n                Assert.Throws<InvalidOperationException>(() => _collection.Add(instance));\n            }\n        }\n\n        [Fact]\n        public void AddAcceptsFilterInstances()\n        {\n            // Arrange\n            var filters = new object[] {\n                GetFilterInstance<IClientFilter>(),\n                GetFilterInstance<IServerFilter>(),\n                GetFilterInstance<IClientExceptionFilter>(),\n                GetFilterInstance<IServerExceptionFilter>(),\n                GetFilterInstance<IApplyStateFilter>(),\n                GetFilterInstance<IElectStateFilter>()\n            }.ToList();\n\n            // Act\n            filters.ForEach(f => _collection.Add(f));\n\n            // Assert\n            Assert.Equal(filters, _collection.Select(i => i.Instance));\n        }\n\n        [Fact]\n        public void AddPlacesFilterInGlobalScope()\n        {\n            // Act\n            _collection.Add(_filterInstance);\n\n            // Assert\n            JobFilter filter = Assert.Single(_collection);\n            Assert.Same(_filterInstance, filter.Instance);\n            Assert.Equal(JobFilterScope.Global, filter.Scope);\n            Assert.Equal(-1, filter.Order);\n        }\n\n        [Fact]\n        public void AddWithOrderPlacesFilterInGlobalScope()\n        {\n            // Act\n            _collection.Add(_filterInstance, 42);\n\n            // Assert\n            JobFilter filter = Assert.Single(_collection);\n            Assert.Same(_filterInstance, filter.Instance);\n            Assert.Equal(JobFilterScope.Global, filter.Scope);\n            Assert.Equal(42, filter.Order);\n        }\n\n        [Fact]\n        public void ContainsFindsFilterByInstance()\n        {\n            // Arrange\n            _collection.Add(_filterInstance);\n\n            // Act\n            bool result = _collection.Contains(_filterInstance);\n\n            // Assert\n            Assert.True(result);\n        }\n\n        [Fact]\n        public void RemoveDeletesFilterByInstance()\n        {\n            // Arrange\n            _collection.Add(_filterInstance);\n\n            // Act\n            _collection.Remove(_filterInstance);\n\n            // Assert\n            Assert.Empty(_collection);\n        }\n\n        [Fact]\n        public void RemoveWithTypeDeletesFilterByType()\n        {\n            // Arrange\n            _collection.Add(_filterInstance);\n\n            // Act\n            _collection.Remove(_filterInstance.GetType());\n\n            // Assert\n            Assert.Empty(_collection);\n        }\n\n        [Fact]\n        public void CollectionIsIFilterProviderWhichReturnsAllFilters()\n        {\n            // Arrange\n            _collection.Add(_filterInstance);\n            var provider = (IJobFilterProvider)_collection;\n\n            // Act\n            IEnumerable<JobFilter> result = provider.GetFilters(null);\n\n            // Assert\n            JobFilter filter = Assert.Single(result);\n            Assert.Same(_filterInstance, filter.Instance);\n        }\n\n        [Fact]\n        public void Count_ReturnsNumberOfElements()\n        {\n            _collection.Add(_filterInstance);\n\n            Assert.Equal(1, _collection.Count);\n        }\n\n        [Fact]\n        public void Clear_RemovesAllElementsFromCollection()\n        {\n            _collection.Add(_filterInstance);\n\n            _collection.Clear();\n            \n            Assert.Equal(0, _collection.Count);\n        }\n\n        private static TFilter GetFilterInstance<TFilter>() where TFilter : class\n        {\n            return new Mock<TFilter>().Object;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/JobFilterFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class JobFilterFacts\n    {\n        [Fact]\n        public void GuardClause()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new JobFilter(null, JobFilterScope.Method, null));\n\n            Assert.Equal(\"instance\", exception.ParamName);\n        }\n\n        [Fact]\n        public void FilterDoesNotImplementIJobFilter()\n        {\n            // Arrange\n            var filterInstance = new object();\n\n            // Act\n            var filter = new JobFilter(filterInstance, JobFilterScope.Method, null);\n\n            // Assert\n            Assert.Same(filterInstance, filter.Instance);\n            Assert.Equal(JobFilterScope.Method, filter.Scope);\n            Assert.Equal(JobFilter.DefaultOrder, filter.Order);\n        }\n\n        [Fact]\n        public void FilterImplementsIJobFilter()\n        {\n            // Arrange\n            var filterInstance = new Mock<IJobFilter>();\n            filterInstance.SetupGet(f => f.Order).Returns(42);\n\n            // Act\n            var filter = new JobFilter(filterInstance.Object, JobFilterScope.Type, null);\n\n            // Assert\n            Assert.Same(filterInstance.Object, filter.Instance);\n            Assert.Equal(JobFilterScope.Type, filter.Scope);\n            Assert.Equal(42, filter.Order);\n        }\n\n        [Fact]\n        public void ExplicitOrderOverridesIJobFilter()\n        {\n            // Arrange\n            var filterInstance = new Mock<IJobFilter>();\n            filterInstance.SetupGet(f => f.Order).Returns(42);\n\n            // Act\n            var filter = new JobFilter(filterInstance.Object, JobFilterScope.Type, 2112);\n\n            // Assert\n            Assert.Same(filterInstance.Object, filter.Instance);\n            Assert.Equal(JobFilterScope.Type, filter.Scope);\n            Assert.Equal(2112, filter.Order);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/JobFilterProviderCollectionFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing Hangfire.Common;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class JobFilterProviderCollectionFacts\n    {\n        private readonly Job _job;\n\n        public JobFilterProviderCollectionFacts()\n        {\n            _job = Job.FromExpression(() => Sample());\n        }\n\n        [Fact]\n        public void GetFilters_ReturnsNull_WhenJobIsNull()\n        {\n            var collection = new JobFilterProviderCollection();\n            var filters = collection.GetFilters(null);\n\n            Assert.Empty(filters);\n        }\n\n        [Fact]\n        public void GetFiltersUsesRegisteredProviders()\n        {\n            // Arrange\n            var filter = new JobFilter(new Object(), JobFilterScope.Method, null);\n            var provider = new Mock<IJobFilterProvider>(MockBehavior.Strict);\n            var collection = new JobFilterProviderCollection(provider.Object);\n            provider.Setup(p => p.GetFilters(_job)).Returns(new[] { filter });\n\n            // Act\n            IEnumerable<JobFilter> result = collection.GetFilters(_job);\n\n            // Assert\n            Assert.Same(filter, result.Single());\n        }\n\n        [Fact]\n        public void GetFiltersSortsFiltersByOrderFirstThenScope()\n        {\n            // Arrange\n            var actionFilter = new JobFilter(new Object(), JobFilterScope.Method, null);\n            var controllerFilter = new JobFilter(new Object(), JobFilterScope.Type, null);\n            var globalFilter = new JobFilter(new Object(), JobFilterScope.Global, null);\n            var earlyActionFilter = new JobFilter(new Object(), JobFilterScope.Method, -100);\n            var lateGlobalFilter = new JobFilter(new Object(), JobFilterScope.Global, 100);\n            var provider = new Mock<IJobFilterProvider>(MockBehavior.Strict);\n            var collection = new JobFilterProviderCollection(provider.Object);\n            provider.Setup(p => p.GetFilters(_job))\n                .Returns(new[] { actionFilter, controllerFilter, globalFilter, earlyActionFilter, lateGlobalFilter });\n\n            // Act\n            JobFilter[] result = collection.GetFilters(_job).ToArray();\n\n            // Assert\n            Assert.Equal(5, result.Length);\n            Assert.Same(earlyActionFilter, result[0]);\n            Assert.Same(globalFilter, result[1]);\n            Assert.Same(controllerFilter, result[2]);\n            Assert.Same(actionFilter, result[3]);\n            Assert.Same(lateGlobalFilter, result[4]);\n        }\n\n        [Fact]\n        public void GetFiltersSortsFiltersStablyAllowMultipleFalse()\n        {\n            // Arrange\n            var objectFilter = new JobFilter(new Object(), JobFilterScope.Global, null);\n            var allowMultipleFalseAttribute1 = new JobFilter(new AllowMultipleFalseAttribute(), JobFilterScope.Global, null);\n            var allowMultipleFalseAttribute2 = new JobFilter(new AllowMultipleFalseAttribute(), JobFilterScope.Global, null);\n            var provider = new Mock<IJobFilterProvider>(MockBehavior.Strict);\n            var collection = new JobFilterProviderCollection(provider.Object);\n\n            // to prove instability we need at least 17 items in the list\n            provider.Setup(p => p.GetFilters(_job))\n                .Returns(new[] {\n                    objectFilter,\n                    objectFilter,\n                    objectFilter,\n                    objectFilter,\n                    objectFilter,\n                    objectFilter,\n                    objectFilter,\n                    allowMultipleFalseAttribute1,\n                    objectFilter,\n                    objectFilter,\n                    objectFilter,\n                    allowMultipleFalseAttribute2,\n                    objectFilter,\n                    objectFilter,\n                    objectFilter,\n                    objectFilter,\n                    objectFilter\n                });\n\n            // Act\n            IEnumerable<JobFilter> result = collection.GetFilters(_job);\n\n            // Assert\n            Assert.Same(allowMultipleFalseAttribute2, result.Last(item => item.Instance is AllowMultipleFalseAttribute));\n        }\n\n        [AttributeUsage(AttributeTargets.All)]\n        private class AllowMultipleFalseAttribute : JobFilterAttribute\n        {\n        }\n\n        [Fact]\n        public void GetFiltersIncludesLastFilterOnlyWithAttributeUsageAllowMultipleFalse()\n        {\n            // Arrange\n            var globalFilter = new JobFilter(new AllowMultipleFalseAttribute(), JobFilterScope.Global, null);\n            var controllerFilter = new JobFilter(new AllowMultipleFalseAttribute(), JobFilterScope.Type, null);\n            var actionFilter = new JobFilter(new AllowMultipleFalseAttribute(), JobFilterScope.Method, null);\n            var provider = new Mock<IJobFilterProvider>(MockBehavior.Strict);\n            var collection = new JobFilterProviderCollection(provider.Object);\n            provider.Setup(p => p.GetFilters(_job))\n                .Returns(new[] { controllerFilter, actionFilter, globalFilter });\n\n            // Act\n            IEnumerable<JobFilter> result = collection.GetFilters(_job);\n\n            // Assert\n            Assert.Same(actionFilter, result.Single());\n        }\n\n        [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]\n        private class AllowMultipleTrueAttribute : JobFilterAttribute\n        {\n        }\n\n        [Fact]\n        public void GetFiltersIncludesAllFiltersWithAttributeUsageAllowMultipleTrue()\n        {\n            // Arrange\n            var globalFilter = new JobFilter(new AllowMultipleTrueAttribute(), JobFilterScope.Global, null);\n            var controllerFilter = new JobFilter(new AllowMultipleTrueAttribute(), JobFilterScope.Type, null);\n            var actionFilter = new JobFilter(new AllowMultipleTrueAttribute(), JobFilterScope.Method, null);\n            var provider = new Mock<IJobFilterProvider>(MockBehavior.Strict);\n            var collection = new JobFilterProviderCollection(provider.Object);\n            provider.Setup(p => p.GetFilters(_job))\n                .Returns(new[] { controllerFilter, actionFilter, globalFilter });\n\n            // Act\n            List<JobFilter> result = collection.GetFilters(_job).ToList();\n\n            // Assert\n            Assert.Same(globalFilter, result[0]);\n            Assert.Same(controllerFilter, result[1]);\n            Assert.Same(actionFilter, result[2]);\n        }\n\n        private class AllowMultipleCustomFilter : IJobFilter\n        {\n            public AllowMultipleCustomFilter(bool allowMultiple)\n            {\n                AllowMultiple = allowMultiple;\n            }\n\n            public bool AllowMultiple { get; }\n            public int Order => -1;\n        }\n\n        [Fact]\n        public void GetFiltersIncludesLastFilterOnlyWithCustomFilterAllowMultipleFalse()\n        {\n            // Arrange\n            var globalFilter = new JobFilter(new AllowMultipleCustomFilter(false), JobFilterScope.Global, null);\n            var controllerFilter = new JobFilter(new AllowMultipleCustomFilter(false), JobFilterScope.Type, null);\n            var actionFilter = new JobFilter(new AllowMultipleCustomFilter(false), JobFilterScope.Method, null);\n            var provider = new Mock<IJobFilterProvider>(MockBehavior.Strict);\n            var collection = new JobFilterProviderCollection(provider.Object);\n            provider.Setup(p => p.GetFilters(_job))\n                .Returns(new[] { controllerFilter, actionFilter, globalFilter });\n\n            // Act\n            IEnumerable<JobFilter> result = collection.GetFilters(_job);\n\n            // Assert\n            Assert.Same(actionFilter, result.Single());\n        }\n\n        [Fact]\n        public void GetFiltersIncludesAllFiltersWithCustomFilterAllowMultipleTrue()\n        {\n            // Arrange\n            var globalFilter = new JobFilter(new AllowMultipleCustomFilter(true), JobFilterScope.Global, null);\n            var controllerFilter = new JobFilter(new AllowMultipleCustomFilter(true), JobFilterScope.Type, null);\n            var actionFilter = new JobFilter(new AllowMultipleCustomFilter(true), JobFilterScope.Method, null);\n            var provider = new Mock<IJobFilterProvider>(MockBehavior.Strict);\n            var collection = new JobFilterProviderCollection(provider.Object);\n            provider.Setup(p => p.GetFilters(_job))\n                .Returns(new[] { controllerFilter, actionFilter, globalFilter });\n\n            // Act\n            List<JobFilter> result = collection.GetFilters(_job).ToList();\n\n            // Assert\n            Assert.Same(globalFilter, result[0]);\n            Assert.Same(controllerFilter, result[1]);\n            Assert.Same(actionFilter, result[2]);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Sample() { }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/JobHelperFacts.cs",
    "content": "﻿using System;\n#if NETCOREAPP1_0\nusing System.Reflection;\n#endif\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Storage;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Serialization;\nusing Xunit;\n#pragma warning disable 618\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class JobHelperFacts\n    {\n        private static readonly DateTime WellKnownDateTime = new DateTime(1988, 04, 20, 01, 12, 32, DateTimeKind.Utc);\n        private const int WellKnownTimestamp = 577501952;\n        private const long WellKnownMillisecondTimestamp = 577501952000;\n\n        [DataCompatibilityRangeFact]\n        public void ToJson_EncodesNullValueAsNull()\n        {\n            var result = JobHelper.ToJson(null);\n            Assert.Null(result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void ToJson_EncodesGivenValue_ToJsonString()\n        {\n            var result = JobHelper.ToJson(\"hello\");\n            Assert.Equal(\"\\\"hello\\\"\", result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void FromJson_DecodesNullAsDefaultValue()\n        {\n            var stringResult = JobHelper.FromJson<string>(null);\n            var intResult = JobHelper.FromJson<int>(null);\n\n            Assert.Null(stringResult);\n            Assert.Equal(0, intResult);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void FromJson_DecodesFromJsonString()\n        {\n            var result = JobHelper.FromJson<string>(\"\\\"hello\\\"\");\n            Assert.Equal(\"hello\", result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void FromJson_ThrowsAnException_WhenTypeIsNull()\n        {\n            Assert.Throws<ArgumentNullException>(() => JobHelper.FromJson(\"1\", null));\n        }\n\n        [DataCompatibilityRangeFact]\n        public void FromJson_WithType_DecodesFromJsonString()\n        {\n            var result = (string) JobHelper.FromJson(\"\\\"hello\\\"\", typeof (string));\n            Assert.Equal(\"hello\", result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void FromJson_WithType_DecodesNullValue_ToNull()\n        {\n            var result = (string) JobHelper.FromJson(null, typeof (string));\n            Assert.Null(result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void ToTimestamp_ReturnsUnixTimestamp_OfTheGivenDateTime()\n        {\n            var result = JobHelper.ToTimestamp(\n                WellKnownDateTime);\n\n            Assert.Equal(WellKnownTimestamp, result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void FromTimestamp_ReturnsDateTime_ForGivenTimestamp()\n        {\n            var result = JobHelper.FromTimestamp(WellKnownTimestamp);\n\n            Assert.Equal(WellKnownDateTime, result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void ToMillisecondTimestamp_ReturnsTheCorrectResult()\n        {\n            var result = JobHelper.ToMillisecondTimestamp(WellKnownDateTime);\n            Assert.Equal(WellKnownMillisecondTimestamp, result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void FromMillisecondTimestamp_ReturnsTheCorrectDateTime()\n        {\n            var result = JobHelper.FromMillisecondTimestamp(WellKnownMillisecondTimestamp);\n            Assert.Equal(WellKnownDateTime, result);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void SerializeDateTime_ReturnsString_InISO8601Format_In_Version_Pre_170()\n        {\n            var result = JobHelper.SerializeDateTime(WellKnownDateTime);\n\n            Assert.Equal(WellKnownDateTime.ToString(\"O\"), result);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializeDateTime_ReturnsString_WithMillisecondTimestamp_In_Version_170()\n        {\n            var result = JobHelper.SerializeDateTime(WellKnownDateTime);\n\n            Assert.Equal(WellKnownMillisecondTimestamp.ToString(), result);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializeDateTime_ReturnsMillisecondTimestamp_ForRecentDates()\n        {\n            var dateTime = DateTime.UtcNow;\n            var result = JobHelper.SerializeDateTime(dateTime);\n\n            Assert.True(long.TryParse(result, out var timestamp));\n            Assert.Equal(JobHelper.ToMillisecondTimestamp(dateTime), timestamp);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializeDateTime_ReturnsMillisecondTimestamp_AtLeastUpTo2100()\n        {\n            var dateTime = new DateTime(2100, 01, 01, 00, 00, 00, DateTimeKind.Utc);\n            var result = JobHelper.SerializeDateTime(dateTime);\n\n            Assert.True(long.TryParse(result, out var timestamp));\n            Assert.Equal(JobHelper.ToMillisecondTimestamp(dateTime), timestamp);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializeDateTime_ReturnsISO8601String_ForConflictingRanges_WithSecondBasedTimestamps()\n        {\n            var dateTime = new DateTime(1975, 01, 01, 00, 00, 00, DateTimeKind.Utc);\n            var result = JobHelper.SerializeDateTime(dateTime);\n\n            Assert.False(long.TryParse(result, out _));\n            Assert.Equal(dateTime.ToString(\"O\"), result);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializeDateTime_ReturnsISO8601String_WhenTimestampIsNotApplicable()\n        {\n            var dateTime = new DateTime(1900, 01, 01, 00, 00, 00, DateTimeKind.Utc);\n            var result = JobHelper.SerializeDateTime(dateTime);\n\n            Assert.False(long.TryParse(result, out _));\n            Assert.Equal(dateTime.ToString(\"O\"), result);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializeDateTime_ReturnsISO8601String_WithMaxDateTime()\n        {\n            var dateTime = DateTime.MaxValue;\n            var result = JobHelper.SerializeDateTime(dateTime);\n\n            Assert.False(long.TryParse(result, out _));\n            Assert.Equal(dateTime.ToString(\"O\"), result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void DeserializeDateTime_CanDeserialize_Timestamps()\n        {\n            var result = JobHelper.DeserializeDateTime(WellKnownTimestamp.ToString());\n\n            Assert.Equal(WellKnownDateTime, result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void DeserializeDateTime_CanDeserialize_ISO8601Format()\n        {\n            var result = JobHelper.DeserializeDateTime(WellKnownDateTime.ToString(\"o\"));\n            Assert.Equal(WellKnownDateTime, result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void DeserializeDateTime_CanDeserialize_MillisecondTimestamp()\n        {\n            var result = JobHelper.DeserializeDateTime(WellKnownMillisecondTimestamp.ToString());\n            Assert.Equal(WellKnownDateTime, result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void DeserializeNullableDateTime_ReturnsNull_IfNullOrEmptyStringGiven()\n        {\n            Assert.Null(JobHelper.DeserializeNullableDateTime(\"\"));\n            Assert.Null(JobHelper.DeserializeNullableDateTime(null));\n        }\n\n        [DataCompatibilityRangeFact]\n        public void DeserializeNullableDateTime_ReturnsCorrectValue_OnNonNullString()\n        {\n            var result = JobHelper.DeserializeNullableDateTime(WellKnownTimestamp.ToString());\n            Assert.Equal(WellKnownDateTime, result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void FromJson_WithObjectType_DecodesFromJsonString()\n        {\n            var result = (ClassA)JobHelper.FromJson(@\"{ \"\"PropertyA\"\": \"\"hello\"\" }\", typeof(ClassA));\n            Assert.NotNull(result);\n            Assert.Equal(\"hello\", result.PropertyA);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void ForSerializeUseDefaultConfigurationOfJsonNet()\n        {\n            var result = JobHelper.ToJson(new ClassA(\"A\"));\n            Assert.Equal(@\"{\"\"PropertyA\"\":\"\"A\"\"}\", result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void ForSerializeCanUseCustomConfigurationOfJsonNet()\n        {\n            try\n            {\n                JobHelper.SetSerializerSettings(new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });\n\n                var result = JobHelper.ToJson(new ClassA(\"A\"));\n                Assert.Equal(@\"{\"\"propertyA\"\":\"\"A\"\"}\", result);\n            }\n            finally\n            {\n                JobHelper.SetSerializerSettings(null);\n            }\n        }\n\n        [DataCompatibilityRangeFact]\n        public void ForDeserializeCanUseCustomConfigurationOfJsonNet()\n        {\n            try\n            {\n                JobHelper.SetSerializerSettings(new JsonSerializerSettings\n                {\n                    TypeNameHandling = TypeNameHandling.Objects\n                });\n\n                var result = (ClassA)JobHelper.FromJson<IClass>(@\"{ \"\"$type\"\": \"\"Hangfire.Core.Tests.Common.JobHelperFacts+ClassA, Hangfire.Core.Tests\"\", \"\"propertyA\"\":\"\"A\"\" }\");\n                Assert.Equal(\"A\", result.PropertyA);\n            }\n            finally\n            {\n                JobHelper.SetSerializerSettings(null);\n            }\n        }\n\n        [DataCompatibilityRangeFact]\n        public void ForDeserializeCanUseCustomConfigurationOfJsonNetWithInvocationData()\n        {\n            try\n            {\n                JobHelper.SetSerializerSettings(new JsonSerializerSettings\n                {\n                    TypeNameHandling = TypeNameHandling.All,\n#if !NET452 && !NET461 && !NETCOREAPP1_0\n                    TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple\n#endif\n                });\n\n                var method = typeof (BackgroundJob).GetMethod(\"DoWork\");\n                var args = new object[] { \"123\", \"Test\" };\n                var job = new Job(typeof(BackgroundJob), method, args);\n\n                var invocationData = InvocationData.Serialize(job);\n                var deserializedJob = invocationData.Deserialize();\n\n                Assert.Equal(typeof(BackgroundJob), deserializedJob.Type);\n                Assert.Equal(method, deserializedJob.Method);\n                Assert.Equal(args, deserializedJob.Args);\n            }\n            finally\n            {\n                JobHelper.SetSerializerSettings(null);\n            }\n        }\n\n        [DataCompatibilityRangeFact]\n        public void ForDeserializeWithGenericMethodCanUseCustomConfigurationOfJsonNet()\n        {\n            try\n            {\n                JobHelper.SetSerializerSettings(new JsonSerializerSettings\n                {\n                    TypeNameHandling = TypeNameHandling.Objects\n                });\n\n                var result = (ClassA)JobHelper.FromJson(@\"{ \"\"$type\"\": \"\"Hangfire.Core.Tests.Common.JobHelperFacts+ClassA, Hangfire.Core.Tests\"\", \"\"propertyA\"\":\"\"A\"\" }\", typeof(IClass));\n\n                Assert.NotNull(result);\n                Assert.Equal(\"A\", result.PropertyA);\n            }\n            finally\n            {\n                JobHelper.SetSerializerSettings(null);\n            }\n        }\n\n        private interface IClass\n        {\n        }\n\n        private class ClassA : IClass\n        {\n            public ClassA(string propertyA)\n            {\n                PropertyA = propertyA;\n            }\n\n            public string PropertyA { get; }\n        }\n\n        private class BackgroundJob\n        {\n            [UsedImplicitly]\n            public void DoWork(string workId, string message)\n            {\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/JobLoadExceptionFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class JobLoadExceptionFacts\n    {\n        [Fact]\n        public void Ctor_CreatesException_WithGivenMessageAnInnerException()\n        {\n            var innerException = new Exception();\n            var exception = new JobLoadException(\"1\", innerException);\n\n            Assert.Equal(\"1\", exception.Message);\n            Assert.Same(innerException, exception.InnerException);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/MethodInfoExtensionsFacts.cs",
    "content": "﻿using System.Linq;\nusing System.Reflection;\nusing Hangfire.Common;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class MethodInfoExtensionsFacts\n    {\n        [Fact]\n        public void GetNormalizedName_ReturnsNormalizedName_ForRegularMethod()\n        {\n            var service = new RegularInterfaceImplementation();\n            var normalizedName = service.GetType().GetRuntimeMethods().First().GetNormalizedName();\n\n            Assert.Equal(\"Method\", normalizedName);\n        }\n\n        [Fact]\n        public void GetNormalizedName_ReturnsNormalizedName_ForExplicitlyImplementedMethod()\n        {\n            var service = new ExplicitInterfaceImplementation();\n            var normalizedName = service.GetType().GetRuntimeMethods().First().GetNormalizedName();\n\n            Assert.Equal(\"Method\", normalizedName);\n        }\n\n        private interface IService\n        {\n            void Method();\n        }\n\n        private class RegularInterfaceImplementation : IService\n        {\n            public void Method()\n            {\n            }\n        }\n\n        private class ExplicitInterfaceImplementation : IService\n        {\n            void IService.Method()\n            {\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/SerializationHelperFacts.cs",
    "content": "﻿using System;\nusing System.Runtime.Serialization;\nusing Hangfire.Common;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Serialization;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class SerializationHelperFacts\n    {\n        [DataCompatibilityRangeFact]\n        public void Serialize_ReturnsNull_WhenValueIsNull()\n        {\n            Assert.Null(SerializationHelper.Serialize((string)null));\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Serialize_ReturnsCorrectResult_WhenValueIsString()\n        {\n            var result = SerializationHelper.Serialize(\"Simple string\");\n            Assert.Equal(\"\\\"Simple string\\\"\", result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Serialize_ReturnsCorrectValue_WhenValueIsCustomObject()\n        {\n            var result = SerializationHelper.Serialize(new ClassA(\"B\"));\n            Assert.Equal(@\"{\"\"PropertyA\"\":\"\"B\"\"}\", result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Serialize_ReturnsCorrectJson_WhenOptionsIsTypedInternal()\n        {\n            var result = SerializationHelper.Serialize<object>(new ClassA(\"B\"), SerializationOption.TypedInternal);\n            Assert.Equal(@\"{\"\"$type\"\":\"\"Hangfire.Core.Tests.Common.SerializationHelperFacts+ClassA, Hangfire.Core.Tests\"\",\"\"PropertyA\"\":\"\"B\"\"}\", result);\n        }\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void Serialize_SerializesWithUserSettings_WhenOptionsIsUser()\n        {\n            SerializationHelper.SetUserSerializerSettings(new JsonSerializerSettings\n            {\n                ContractResolver = new CamelCasePropertyNamesContractResolver()\n            });\n\n            var result = SerializationHelper.Serialize(new ClassA(\"A\"), SerializationOption.User);\n            Assert.Equal(@\"{\"\"propertyA\"\":\"\"A\"\"}\", result);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170), CleanSerializerSettings]\n        public void Serialize_SerializesWithUserSettings_WhenOptionsIsInternal_BeforeVersion170()\n        {\n            SerializationHelper.SetUserSerializerSettings(new JsonSerializerSettings\n            {\n                ContractResolver = new CamelCasePropertyNamesContractResolver(),\n            });\n\n            var result = SerializationHelper.Serialize(new ClassA(\"A\"));\n            Assert.Equal(@\"{\"\"propertyA\"\":\"\"A\"\"}\", result);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170), CleanSerializerSettings]\n        public void Serialize_SerializesWithDefaultSettingsWithTypeInformation_WhenOptionIsTypedInternal_BeforeVersion170()\n        {\n            SerializationHelper.SetUserSerializerSettings(new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.None,\n                NullValueHandling = NullValueHandling.Ignore,\n                DefaultValueHandling = DefaultValueHandling.Ignore,\n                ContractResolver = new CamelCasePropertyNamesContractResolver()\n            });\n\n            var result = SerializationHelper.Serialize(new ClassB { StringValue = \"hi\"}, SerializationOption.TypedInternal);\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.Core.Tests.Common.SerializationHelperFacts+ClassB, Hangfire.Core.Tests\\\",\" + \n                \"\\\"StringValue\\\":\\\"hi\\\",\\\"NullValue\\\":null,\\\"DefaultValue\\\":0,\\\"DateTimeValue\\\":null}\", \n                result);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170), CleanSerializerSettings]\n        public void Serialize_DoesNotSerializeWithUserSettings_WhenOptionsIsInternal_AfterVersion170()\n        {\n            SerializationHelper.SetUserSerializerSettings(new JsonSerializerSettings\n            {\n                ContractResolver = new CamelCasePropertyNamesContractResolver()\n            });\n\n            var result = SerializationHelper.Serialize(new ClassA(\"A\"));\n            Assert.Equal(@\"{\"\"PropertyA\"\":\"\"A\"\"}\", result);\n        }\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void Serialize_DoesNotSerializeWithUserSettings_WhenOptionsIsTypedInternal()\n        {\n            SerializationHelper.SetUserSerializerSettings(new JsonSerializerSettings\n            {\n                ContractResolver = new CamelCasePropertyNamesContractResolver()\n            });\n\n            var result = SerializationHelper.Serialize<object>(new ClassA(\"A\"), SerializationOption.TypedInternal);\n            Assert.Equal(\n                @\"{\"\"$type\"\":\"\"Hangfire.Core.Tests.Common.SerializationHelperFacts+ClassA, Hangfire.Core.Tests\"\",\"\"PropertyA\"\":\"\"A\"\"}\",\n                result);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170), CleanSerializerSettings]\n        public void Serialize_ProducesObjectThatCanBeDeserialized_UsingJsonConvert_WithInternalSettings_BeforeVersion170()\n        {\n            // Arrange\n            SerializationHelper.SetUserSerializerSettings(new JsonSerializerSettings\n            {\n                ContractResolver = new CamelCasePropertyNamesContractResolver(),\n                TypeNameHandling = TypeNameHandling.All\n            });\n\n            var initialObject = new ClassA(\"A\");\n\n            // Act\n            var result = SerializationHelper.Serialize(initialObject);\n\n            var resultingObject = JsonConvert.DeserializeObject<ClassA>(result, new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.None\n            });\n\n            // Assert\n            Assert.Equal(initialObject.PropertyA, resultingObject.PropertyA);\n        }\n\n#if !NET452 && !NET461\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void Serialize_JsonDefaultSettingsAffectResult_WhenOptionIs_User()\n        {\n            JsonConvert.DefaultSettings = () => new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.All,\n                NullValueHandling = NullValueHandling.Ignore,\n                DefaultValueHandling = DefaultValueHandling.Ignore,\n                Binder = new CustomSerializerBinder(),\n                DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,\n                DateFormatString = \"ddMMyyyy\"\n            };\n\n            var result = SerializationHelper.Serialize(new ClassB { StringValue = \"B\", DateTimeValue = new DateTime(1961, 4, 12) }, SerializationOption.User);\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"HANGFIRE.CORE.TESTS.COMMON.SERIALIZATIONHELPERFACTS+CLASSB, someAssembly\\\",\\\"StringValue\\\":\\\"B\\\",\\\"DateTimeValue\\\":\\\"12041961\\\"}\",\n                result);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170), CleanSerializerSettings]\n        public void Serialize_JsonDefaultSettingsDoAffectResult_WhenOptionIs_Internal_BeforeVersion_170()\n        {\n            JsonConvert.DefaultSettings = () => new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.All,\n                NullValueHandling = NullValueHandling.Ignore,\n                DefaultValueHandling = DefaultValueHandling.Ignore,\n                Binder = new CustomSerializerBinder(),\n                DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,\n                DateFormatString = \"ddMMyyyy\"\n            };\n\n            var result = SerializationHelper.Serialize(new ClassB { StringValue = \"B\", DateTimeValue = new DateTime(1961, 4, 12) });\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"HANGFIRE.CORE.TESTS.COMMON.SERIALIZATIONHELPERFACTS+CLASSB, someAssembly\\\",\\\"StringValue\\\":\\\"B\\\",\\\"DateTimeValue\\\":\\\"12041961\\\"}\",\n                result);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170), CleanSerializerSettings]\n        public void Serialize_JsonDefaultSettingsDoNotAffectResult_WhenOptionIs_Internal_StartingFromVersion_170()\n        {\n            JsonConvert.DefaultSettings = () => new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.All,\n                NullValueHandling = NullValueHandling.Include,\n                DefaultValueHandling = DefaultValueHandling.Include,\n                Binder = new CustomSerializerBinder(),\n                DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,\n                DateFormatString = \"ddMMyyyy\"\n            };\n\n            var result = SerializationHelper.Serialize(new ClassB { StringValue = \"B\", DateTimeValue = new DateTime(1961, 4, 12) });\n            Assert.Equal(@\"{\"\"StringValue\"\":\"\"B\"\",\"\"DateTimeValue\"\":\"\"1961-04-12T00:00:00\"\"}\", result);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170), CleanSerializerSettings]\n        public void Serialize_JsonDefaultSettingsDoAffectResult_WhenOptionIs_TypedInternal_BeforeVersion_170()\n        {\n            JsonConvert.DefaultSettings = () => new JsonSerializerSettings\n            {\n                NullValueHandling = NullValueHandling.Ignore,\n                DefaultValueHandling = DefaultValueHandling.Ignore,\n                Binder = new CustomSerializerBinder(),\n                DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,\n                DateFormatString = \"ddMMyyyy\"\n            };\n\n            var result = SerializationHelper.Serialize<object>(\n                new ClassB { StringValue = \"B\", DateTimeValue = new DateTime(1961, 4, 12) },\n                SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"HANGFIRE.CORE.TESTS.COMMON.SERIALIZATIONHELPERFACTS+CLASSB, someAssembly\\\",\\\"StringValue\\\":\\\"B\\\",\\\"DateTimeValue\\\":\\\"12041961\\\"}\",\n                result);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170), CleanSerializerSettings]\n        public void Serialize_JsonDefaultSettingsDoNotAffectResult_WhenOptionIs_TypedInternal_StartingFromVersion_170()\n        {\n            JsonConvert.DefaultSettings = () => new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.None,\n                NullValueHandling = NullValueHandling.Include,\n                DefaultValueHandling = DefaultValueHandling.Include,\n                Binder = new CustomSerializerBinder(),\n                DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,\n                DateFormatString = \"ddMMyyyy\"\n            };\n\n            var result = SerializationHelper.Serialize<object>(\n                new ClassB { StringValue = \"B\", DateTimeValue = new DateTime(1961, 4, 12) },\n                SerializationOption.TypedInternal);\n\n            Assert.Equal(@\"{\"\"$type\"\":\"\"Hangfire.Core.Tests.Common.SerializationHelperFacts+ClassB, Hangfire.Core.Tests\"\",\"\"StringValue\"\":\"\"B\"\",\"\"DateTimeValue\"\":\"\"1961-04-12T00:00:00\"\"}\", result);\n        }\n#endif\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void Serialize_WithSpecifiedType_DoesNotIncludeTypeProperty_WhenItEqualsToDeclared_AndTypeNameHandlingAutoIsUsed()\n        {\n            JobHelper.SetSerializerSettings(new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.Auto\n            });\n\n            var result = SerializationHelper.Serialize(\n                new ClassA(\"hello\"),\n                typeof(ClassA),\n                SerializationOption.User);\n\n            Assert.Equal(\"{\\\"PropertyA\\\":\\\"hello\\\"}\", result);\n        }\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void Serialize_WithSpecifiedType_IncludesTypeProperty_WhenItDoesNotEqualToDeclared_AndTypeNameHandlingAutoIsUsed()\n        {\n            JobHelper.SetSerializerSettings(new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.Auto\n            });\n\n            var result = SerializationHelper.Serialize(\n                new ClassA(\"hello\"),\n                typeof(object),\n                SerializationOption.User);\n\n            Assert.Equal(\"{\\\"$type\\\":\\\"Hangfire.Core.Tests.Common.SerializationHelperFacts+ClassA, Hangfire.Core.Tests\\\",\\\"PropertyA\\\":\\\"hello\\\"}\", result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_ReturnsNull_WhenValueIsNull()\n        {\n            var result = SerializationHelper.Deserialize(null, typeof(string));\n            Assert.Null(result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_ThrowsException_WhenTypeIsNull()\n        {\n            // ReSharper disable once AssignNullToNotNullAttribute\n            var exception = Assert.Throws<ArgumentNullException>(() => SerializationHelper.Deserialize(\"someString\", null));\n            Assert.Equal(\"type\", exception.ParamName);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_ReturnsCorrectValue_WhenValueIsString()\n        {\n            var result = SerializationHelper.Deserialize(\"\\\"hello\\\"\", typeof(string));\n            Assert.Equal(\"hello\", result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_ReturnsCorrectObject_WhenTypeIsCustomClass()\n        {\n            var valueJson = @\"{\"\"PropertyA\"\":\"\"A\"\"}\";\n\n            var value = SerializationHelper.Deserialize(valueJson, typeof(ClassA)) as ClassA;\n\n            Assert.NotNull(value);\n            Assert.Equal(\"A\", value.PropertyA);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_ReturnsCorrectObject_WhenOptionsIsTypedInternal()\n        {\n            var valueJson = @\"{\"\"$type\"\":\"\"Hangfire.Core.Tests.Common.SerializationHelperFacts+ClassA, Hangfire.Core.Tests\"\",\"\"PropertyA\"\":\"\"A\"\"}\";\n\n            var value = SerializationHelper.Deserialize(valueJson, typeof(ClassA), SerializationOption.TypedInternal);\n            var customObj = value as ClassA;\n\n            Assert.NotNull(customObj);\n            Assert.Equal(\"A\", customObj.PropertyA);\n        }\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        //This test is here to check backward compatibility. Earlier user settings is used for serialization internal data.\n        public void Deserialize_HandlesDeserializationUsingUserOption_WhenUsingInternalOptionThrewException()\n        {\n            SerializationHelper.SetUserSerializerSettings(new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.All,\n                NullValueHandling = NullValueHandling.Ignore,\n                DefaultValueHandling = DefaultValueHandling.Ignore,\n                Binder = new CustomSerializerBinder(),\n                DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,\n            });\n\n            var json = \"{\\\"$type\\\":\\\"HANGFIRE.CORE.TESTS.COMMON.SERIALIZATIONHELPERFACTS+CLASSB, mscorlib\\\",\\\"StringValue\\\":\\\"B\\\",\\\"DateTimeValue\\\":\\\"\\\\/Date(1356044400000)\\\\/\\\"}\";\n\n            var value = SerializationHelper.Deserialize(json, typeof(ClassB));\n\n            var obj = value as ClassB;\n\n            Assert.NotNull(obj);\n            Assert.Equal(\"B\", obj.StringValue);\n            Assert.Equal(new DateTime(2012, 12, 20, 23, 00, 00, DateTimeKind.Utc), obj.DateTimeValue);\n        }\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        //This test is here to check backward compatibility. Earlier user settings is used for serialization internal data.\n        public void Deserialize_RethrowsAnException_WhenUsingInternalOptionThrewException_AndUserSettingsAttemptFailedToo()\n        {\n            SerializationHelper.SetUserSerializerSettings(new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.All,\n                DateFormatString = \"ddMMyyyy\"\n            });\n\n            var json = \"{\\\"$type\\\":\\\"HANGFIRE.CORE.TESTS.COMMON.SERIALIZATIONHELPERFACTS+CLASSB, someAssembly\\\",\\\"StringValue\\\":\\\"B\\\",\\\"DateTimeValue\\\":\\\"12041961\\\"}\";\n\n            Assert.Throws<JsonSerializationException>(() => SerializationHelper.Deserialize(json, typeof(ClassB)));\n        }\n\n        [DataCompatibilityRangeFact]\n        public void DeserializeGeneric_ReturnsNull_WhenValueIsNull()\n        {\n            var result = SerializationHelper.Deserialize<object>(null);\n            Assert.Null(result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void DeserializeGeneric_ReturnsDefaultValue_WhenGenericArgumentIsValueType()\n        {\n            var result = SerializationHelper.Deserialize<int>(null);\n            Assert.Equal(0, result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void DeserializeGeneric_ReturnsCorrectValue_WhenValueIsString()\n        {\n            var result = SerializationHelper.Deserialize<string>(\"\\\"hello\\\"\");\n            Assert.Equal(\"hello\", result);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void DeserializeGeneric_ReturnsCorrectObject_WhenTypeIsCustomClass()\n        {\n            var valueJson = @\"{\"\"PropertyA\"\":\"\"A\"\"}\";\n\n            var value = SerializationHelper.Deserialize<ClassA>(valueJson);\n\n            Assert.NotNull(value);\n            Assert.Equal(\"A\", value.PropertyA);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void DeserializeGeneric_ReturnsCorrectObject_WhenOptionsIsTypedInternal()\n        {\n            var valueJson = @\"{\"\"PropertyA\"\":\"\"A\"\"}\";\n\n            var value = SerializationHelper.Deserialize<ClassA>(valueJson, SerializationOption.TypedInternal);\n            Assert.NotNull(value);\n            Assert.Equal(\"A\", value.PropertyA);\n        }\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        //This test is here to check backward compatibility. Earlier user settings is used for serialization internal data.\n        public void DeserializeGeneric_HandlesUsingUserOption_WhenUsingTypedInternalOptionThrewException()\n        {\n            SerializationHelper.SetUserSerializerSettings(new JsonSerializerSettings\n            {\n                TypeNameHandling = TypeNameHandling.All,\n            });\n\n            var valueJson = SerializationHelper.Serialize(new ClassA(\"A\"), SerializationOption.User);\n\n            var value = SerializationHelper.Deserialize<ClassA>(valueJson, SerializationOption.TypedInternal);\n\n            Assert.NotNull(value);\n            Assert.Equal(\"A\", value.PropertyA);\n        }\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void DeserializeGeneric_RethrowsJsonException_WhenValueHasIncorrectFormat()\n        {\n            var valueJson = \"asdfaljsadkfh\";\n\n            Assert.Throws<JsonReaderException>(() => SerializationHelper.Deserialize<ClassA>(valueJson));\n        }\n\n        [DataCompatibilityRangeFact]\n        public void GetProtectedSettings_SetsDefaultSettings()\n        {\n            var serializerSettings = SerializationHelper.GetInternalSettings();\n\n            Assert.Equal(TypeNameHandling.Auto, serializerSettings.TypeNameHandling);\n#if !NET452 && !NET461 && !NETCOREAPP1_0\n            Assert.Equal(TypeNameAssemblyFormatHandling.Simple, serializerSettings.TypeNameAssemblyFormatHandling);\n#endif\n            Assert.Equal(DefaultValueHandling.IgnoreAndPopulate, serializerSettings.DefaultValueHandling);\n            Assert.Equal(NullValueHandling.Ignore, serializerSettings.NullValueHandling);\n            Assert.True(serializerSettings.CheckAdditionalContent);\n        }\n\n        private interface IClass\n        {\n        }\n\n        private class ClassA : IClass\n        {\n            public ClassA(string propertyA)\n            {\n                PropertyA = propertyA;\n            }\n\n            public string PropertyA { get; }\n        }\n\n        private class ClassB\n        {\n            // ReSharper disable once UnusedAutoPropertyAccessor.Local\n            public string StringValue { get; set; }\n\n            // ReSharper disable once UnusedMember.Local\n            public object NullValue { get; set; }\n\n            // ReSharper disable once UnusedMember.Local\n            public int DefaultValue { get; set; }\n\n            // ReSharper disable once UnusedAutoPropertyAccessor.Local\n            public DateTime? DateTimeValue { get; set; }\n        }\n\n#pragma warning disable 618\n        private class CustomSerializerBinder : SerializationBinder\n#pragma warning restore 618\n        {\n            public override void BindToName(Type serializedType, out string assemblyName, out string typeName)\n            {\n                assemblyName = \"someAssembly\";\n                typeName = serializedType.FullName.ToUpper();\n            }\n\n            public override Type BindToType(string assemblyName, string typeName)\n            {\n                if (typeName.Contains(\"ClassA\")) return typeof(ClassA);\n                return typeof(ClassB);\n            }\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/ShallowExceptionHelperFacts.cs",
    "content": "using System;\nusing Hangfire.Common;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class ShallowExceptionHelperFacts\n    {\n        [Fact]\n        public void PreserveOriginalStackTrace_CanBeCalledTwice_WithoutThrowingAnyException()\n        {\n            try\n            {\n                throw new InvalidOperationException(\"Hello, world!\");\n            }\n            catch (Exception ex)\n            {\n                ex.PreserveOriginalStackTrace();\n                ex.PreserveOriginalStackTrace();\n            }\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Common/TypeExtensionsFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\nusing Xunit;\n\n// ReSharper disable InvokeAsExtensionMethod\n// ReSharper disable AssignNullToNotNullAttribute\n// ReSharper disable UnusedTypeParameter\n\npublic class ClassWithoutNamespace\n{\n}\n\nnamespace Hangfire.Core.Tests.Common\n{\n    public class TypeExtensionsFacts\n    {\n        [Fact]\n        public void ToGenericTypeString_PrintsNonGenericNestedClassName_WithDot()\n        {\n            Assert.Equal(\"NonGenericClass\", typeof(NonGenericClass).ToGenericTypeString());\n            Assert.Equal(\"NonGenericClass.NestedNonGenericClass\", typeof(NonGenericClass.NestedNonGenericClass).ToGenericTypeString());\n            Assert.Equal(\"NonGenericClass.NestedNonGenericClass.DoubleNestedNonGenericClass\", typeof(NonGenericClass.NestedNonGenericClass.DoubleNestedNonGenericClass).ToGenericTypeString());\n        }\n\n        [Fact]\n        public void ToGenericTypeString_PrintsOpenGenericNestedClassName_WithGenericParameters()\n        {\n            Assert.Equal(\"NonGenericClass.NestedGenericClass<T1,T2>\", typeof(NonGenericClass.NestedGenericClass<,>).ToGenericTypeString());\n            Assert.Equal(\"GenericClass<T1>\", typeof(GenericClass<>).ToGenericTypeString());\n            Assert.Equal(\"GenericClass<T1>.NestedNonGenericClass\", typeof(GenericClass<>.NestedNonGenericClass).ToGenericTypeString());\n            Assert.Equal(\"GenericClass<T1>.NestedNonGenericClass.DoubleNestedGenericClass<T2,T3,T4>\", typeof(GenericClass<>.NestedNonGenericClass.DoubleNestedGenericClass<,,>).ToGenericTypeString());\n        }\n        \n        [Fact]\n        public void ToGenericTypeString_PrintsClosedGenericNestedClassName_WithGivenTypes()\n        {\n            Assert.Equal(\"NonGenericClass.NestedGenericClass<Assert,List<Assert>>\", typeof(NonGenericClass.NestedGenericClass<Assert, List<Assert>>).ToGenericTypeString());\n            Assert.Equal(\"GenericClass<Assert>\", typeof(GenericClass<Assert>).ToGenericTypeString());\n            Assert.Equal(\"GenericClass<List<Assert>>.NestedNonGenericClass\", typeof(GenericClass<List<Assert>>.NestedNonGenericClass).ToGenericTypeString());\n            Assert.Equal(\"GenericClass<List<GenericClass<List<Assert>>.NestedNonGenericClass.DoubleNestedGenericClass<Assert,List<Assert>,Stack<Assert>>>>.NestedNonGenericClass.DoubleNestedGenericClass<Assert,List<Assert>,Stack<Assert>>\", typeof(GenericClass<List<GenericClass<List<Assert>>.NestedNonGenericClass.DoubleNestedGenericClass<Assert, List<Assert>, Stack<Assert>>>>.NestedNonGenericClass.DoubleNestedGenericClass<Assert, List<Assert>, Stack<Assert>>).ToGenericTypeString());        \n        }\n\n        [Fact]\n        public void ToGenericTypeString_CorrectlyHandlesTypesWithoutNamespace()\n        {\n            Assert.Equal(\"ClassWithoutNamespace\", typeof(ClassWithoutNamespace).ToGenericTypeString());\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ThrowsAnException_WhenTypeIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => TypeExtensions.GetNonOpenMatchingMethod(null, \"Method\", new Type[0]));\n\n            Assert.Equal(\"type\", exception.ParamName);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ThrowsAnException_WhenNameIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), null, new Type[0]));\n\n            Assert.Equal(\"name\", exception.ParamName);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsCorrectMethod()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"Method\", new Type[0]);\n\n            Assert.Equal(\"Method\", method.Name);\n            Assert.Equal(typeof(NonGenericClass), method.DeclaringType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsCorrectMethodWithNoParameter_WhenParameterTypesIsNull()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"Method\", null);\n\n            Assert.Equal(\"Method\", method.Name);\n            Assert.Equal(typeof(NonGenericClass), method.DeclaringType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsCorrectMethodWithOneParameter()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"Method\",\n                new[] { typeof(int) });\n\n            Assert.Equal(\"Method\", method.Name);\n            Assert.Equal(typeof(NonGenericClass), method.DeclaringType);\n            Assert.Single(method.GetParameters());\n            Assert.Equal(typeof(int), method.GetParameters()[0].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsCorrectMethodWithManyParameters()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"Method\",\n                new[] { typeof(int), typeof(int) });\n\n            Assert.Equal(\"Method\", method.Name);\n            Assert.Equal(typeof(NonGenericClass), method.DeclaringType);\n            Assert.Equal(2, method.GetParameters().Length);\n            Assert.Equal(typeof(int), method.GetParameters()[0].ParameterType);\n            Assert.Equal(typeof(int), method.GetParameters()[1].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsCorrectMethod_WhenTypeIsInterface()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(IParent), \"Method\", new Type[0]);\n            Assert.Equal(\"Method\", method.Name);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodDefinedInBaseInterface()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(IChild), \"Method\", new Type[0]);\n            Assert.Equal(\"Method\", method.Name);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsCorrectGenericMethod()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"TrivialGenericMethod\",\n                new[] { typeof(int), typeof(string), typeof(object) });\n\n            Assert.Equal(\"TrivialGenericMethod\", method.Name);\n            Assert.Equal(typeof(NonGenericClass), method.DeclaringType);\n            Assert.True(method.IsGenericMethod);\n            Assert.False(method.ContainsGenericParameters);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsNull_WhenMethodCouldNotBeFound()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"NonExistingMethod\", new Type[0]);\n\n            Assert.Null(method);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsNull_WhenOveroladedMethodCouldNotBeFound()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"Method\",\n                new[] { typeof(object), typeof(int) });\n\n            Assert.Null(method);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodNameIsCaseSensitive()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"method\", new Type[0]);\n\n            Assert.Null(method);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsNull_WhenMethodParameterTypeIsAssignableFromPassedType()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"Method\",\n                new[] { typeof(NonGenericClass) });\n\n            Assert.Null(method);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodHasParameterWhoseTypeContainsGenericParameter()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"OtherGenericMethod\",\n                new[] { typeof(IEnumerable<int>) });\n\n            Assert.Equal(\"OtherGenericMethod\", method.Name);\n            Assert.Single(method.GetParameters());\n            Assert.Equal(typeof(IEnumerable<int>), method.GetParameters()[0].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodHasParameterWhoseTypeContainsGenericParameterAndIsComplicated()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"OtherGenericMethod\",\n                new[] { typeof(List<IEnumerable<int>>) });\n\n            Assert.Equal(\"OtherGenericMethod\", method.Name);\n            Assert.Single(method.GetParameters());\n            Assert.Equal(typeof(List<IEnumerable<int>>), method.GetParameters()[0].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodHasParameterWhoseTypeIsGenericAndContainsTwoGenericParameters()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"OtherGenericMethod\",\n                new[] { typeof(Tuple<int, double>) });\n\n            Assert.Equal(\"OtherGenericMethod\", method.Name);\n            Assert.Single(method.GetParameters());\n            Assert.Equal(typeof(Tuple<int, double>), method.GetParameters()[0].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesNonTrivialOrderOfUsingMethodGenericParametersInMethodParameterTypes()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"OneMoreGenericMethod\",\n                new[] { typeof(Tuple<int, double, float>) });\n\n            Assert.Equal(\"OneMoreGenericMethod\", method.Name);\n            Assert.Single(method.GetParameters());\n            Assert.Equal(typeof(Tuple<int, double, float>), method.GetParameters()[0].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodHasSomeParametersOfTheSameTypeWhichIsMethodGenericParameter()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"GenericMethod\",\n                new[] { typeof(int), typeof(int) });\n\n            Assert.Equal(\"GenericMethod\", method.Name);\n            Assert.Equal(2, method.GetParameters().Length);\n            Assert.Equal(typeof(int), method.GetParameters()[0].ParameterType);\n            Assert.Equal(typeof(int), method.GetParameters()[1].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodHasGenericAndNonGenericParameters()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"GenericMethod\",\n                new[] { typeof(int), typeof(NonGenericClass), typeof(double) });\n\n            Assert.Equal(\"GenericMethod\", method.Name);\n            Assert.Equal(3, method.GetParameters().Length);\n            Assert.Equal(typeof(int), method.GetParameters()[0].ParameterType);\n            Assert.Equal(typeof(NonGenericClass), method.GetParameters()[1].ParameterType);\n            Assert.Equal(typeof(double), method.GetParameters()[2].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodHasParameterOfGenericTypeWhichContainsMe()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"GenericMethod\",\n                new[] { typeof(Tuple<double, List<int>>)  });\n\n            Assert.Equal(\"GenericMethod\", method.Name);\n            Assert.Single(method.GetParameters());\n            Assert.Equal(typeof(Tuple<double,List<int>>), method.GetParameters()[0].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodHasSomeParametersWhoseTypesContainsTheSameGenericParameter()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"GenericMethod\",\n                new[] { typeof(int), typeof(double) });\n\n            Assert.Null(method);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsNull_WhenParameterTypeIsMatchedByGenericTypeAndNotMatchedByGenericArguments()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"OtherGenericMethod\",\n                new[] { typeof(List<int>)});\n\n            Assert.Null(method);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsCorrectMethod_WhenParameterTypeIsGenericArray()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"GenericMethod\",\n                new[] { typeof(string[]) });\n\n            Assert.Equal(\"GenericMethod\", method.Name);\n            Assert.Equal(typeof(string[]), method.GetParameters()[0].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsCorrectMethod_WhenParameterTypeIsComplicatedGenericArray()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"GenericMethod\",\n                new[] { typeof(List<int>[]) });\n\n            Assert.Equal(\"GenericMethod\", method.Name);\n            Assert.Equal(typeof(List<int>[]), method.GetParameters()[0].ParameterType);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_ReturnsNull_WhenMatchingGenricMethodNotBeFound()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"GenericMethod\",\n                new[] { typeof(Tuple<List<int>, string>) });\n\n            Assert.Null(method);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodHasNoParametersOrTypes()\n        {\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"GenericMethod\",\n                new Type[0]);\n\n            Assert.Null(method);\n        }\n\n        [Fact]\n        public void GetNonOpenMatchingMethod_HandlesMethodHasNoParametersOrTypes2()\n        {\n            // public void GenericMethod<T0, T1>(T0 arg) { }\n            var method = TypeExtensions.GetNonOpenMatchingMethod(typeof(NonGenericClass), \"GenericMethod\",\n                new [] { typeof(string) });\n\n            Assert.Null(method);\n        }\n    }\n\n    public class GenericClass<T1>\n    {\n        public class NestedNonGenericClass\n        {\n            public class DoubleNestedGenericClass<T2, T3, T4>\n            {\n\n            }\n        }\n    }\n\n    public interface IParent\n    {\n        void Method();\n    }\n\n    public interface IChild : IParent { }\n\n    public class NonGenericClass\n    {\n        public void Method() { }\n\n        public void Method(int arg) { }\n\n        public void Method(int arg0, int arg1) { }\n\n        public void Method(IParent arg) { }\n\n        public void Method(object arg) { }\n\n        public void TrivialGenericMethod<T0, T1, T2>(T0 arg0, T1 arg1, T2 arg2) { }\n\n        public void OtherGenericMethod<T>(IEnumerable<T> arg0) { }\n\n        public void OtherGenericMethod<T>(List<IEnumerable<T>> arg0) { }\n\n        public void OtherGenericMethod<T0, T1>(Tuple<T0, T1> arg0) { }\n\n        public void OneMoreGenericMethod<T0, T1, T2>(Tuple<T2, T0, T1> arg0) { }\n\n        public void GenericMethod<T0>() { }\n\n        public void GenericMethod<T0, T1>(T0 arg) { }\n\n        public void GenericMethod<T>(T arg0, T arg1) { }\n\n        public void GenericMethod<T>(int arg0, T arg1, double arg2) { }\n\n        public void GenericMethod<T>(Tuple<T, List<int>> arg) { }\n\n        public void GenericMethod<T>(T[] arg) { }\n\n        public void GenericMethod<T>(List<T>[] arg) { }\n\n        public class NestedNonGenericClass\n        {\n            public class DoubleNestedNonGenericClass\n            {\n\n            }\n        }\n\n        public class NestedGenericClass<T1, T2>\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/ContinuationsSupportAttributeFacts.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Serialization;\nusing Xunit;\nusing static Hangfire.ContinuationsSupportAttribute;\n\nnamespace Hangfire.Core.Tests\n{\n    public class ContinuationsSupportAttributeFacts\n    {\n        private const string _parentId = \"parent-id\";\n        private const string _continuationId = \"continuation-id\";\n        private readonly ElectStateContextMock _context;\n        private readonly Mock<IState> _nextState;\n        private readonly Mock<IBackgroundJobStateChanger> _stateChanger;\n\n        public ContinuationsSupportAttributeFacts()\n        {\n            _context = new ElectStateContextMock();\n            _nextState = new Mock<IState>();\n            _nextState.SetupGet(x => x.Name).Returns(\"SomeState\");\n            _stateChanger = new Mock<IBackgroundJobStateChanger>();\n\n            _context.ApplyContext.Connection.Setup(x => x.GetStateData(_parentId)).Returns(new StateData());\n            _context.ApplyContext.Connection.Setup(x => x.GetJobData(_parentId)).Returns(new JobData());\n\n            _context.ApplyContext.NewStateObject = new AwaitingState(_parentId, _nextState.Object);\n            _context.ApplyContext.BackgroundJob = new BackgroundJobMock { Id = _continuationId };\n        }\n\n        [Fact]\n        public void OnStateElection_AddsAContinuationForParentJob_IfCandidateStateIsAwaiting()\n        {\n            // Arrange\n            var filter = CreateFilter();\n            \n            // Act\n            filter.OnStateElection(_context.Object);\n\n            // Assert\n            _context.ApplyContext.Connection.Verify(x => x.AcquireDistributedLock(\"job:parent-id:state-lock\", It.IsAny<TimeSpan>()));\n            _context.ApplyContext.Connection.Verify(x => x.SetJobParameter(\n                _parentId,\n                \"Continuations\",\n                It.Is<string>(value => SerializationHelper.Deserialize<List<Continuation>>(value)\n                    .Contains(new Continuation { JobId = _continuationId, Options = JobContinuationOptions.OnAnyFinishedState}))));\n            \n            Assert.IsType<AwaitingState>(_context.Object.CandidateState);\n        }\n\n        [Fact]\n        public void OnStateElection_ChangesCandidateToTheNextState_OnAwaitingCompletedParentJob()\n        {\n            _context.ApplyContext.Connection.Setup(x => x.GetStateData(_parentId)).Returns(new StateData { Name = SucceededState.StateName });\n            var filter = CreateFilter();\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.Equal(_nextState.Object.Name, _context.Object.CandidateState.Name);\n        }\n\n        [Fact]\n        public void OnStateElection_AddsAnotherContinuationForParentJob_OnAwaitingState()\n        {\n            // Arrange\n            _context.ApplyContext.Connection.Setup(x => x.GetJobParameter(_parentId, \"Continuations\"))\n                .Returns(SerializationHelper.Serialize(new List<Continuation> { new Continuation { JobId = \"another-id\" } }));\n\n            var filter = CreateFilter();\n\n            // Act\n            filter.OnStateElection(_context.Object);\n\n            // Assert\n            var expectedList = JsonConvert.SerializeObject(new List<Continuation>\n            {\n                new Continuation { JobId = \"another-id\", Options = JobContinuationOptions.OnAnyFinishedState },\n                new Continuation { JobId = _continuationId, Options = JobContinuationOptions.OnAnyFinishedState }\n            });\n\n            _context.ApplyContext.Connection.Verify(x => x.SetJobParameter(_parentId, \"Continuations\", expectedList));\n        }\n\n        [Fact]\n        public void OnStateElection_ThrowsAnException_WhenParentJobDoesNotExist()\n        {\n            _context.ApplyContext.Connection.Setup(x => x.GetJobData(_parentId)).Returns((JobData)null);\n\n            var filter = CreateFilter();\n\n            Assert.Throws<InvalidOperationException>(() => filter.OnStateElection(_context.Object));\n        }\n\n        [Fact]\n        public void OnStateElection_ExecuteContinuations_IfExist()\n        {\n            // Arrange\n            _context.ApplyContext.BackgroundJob = new BackgroundJobMock { Id = _parentId };\n            _context.ApplyContext.NewStateObject = new SucceededState(null, 0, 0);\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobData(_continuationId)).Returns(new JobData());\n            _context.ApplyContext.Connection.Setup(x => x.GetStateData(_continuationId)).Returns(new StateData\n            {\n                Name = AwaitingState.StateName,\n                Data = new Dictionary<string, string>\n                {\n                    { \"NextState\", SerializationHelper.Serialize<IState>(new EnqueuedState(), SerializationOption.TypedInternal) }\n                }\n            });\n            \n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobParameter(_parentId, \"Continuations\"))\n                .Returns(SerializationHelper.Serialize(new List<Continuation> { new Continuation { JobId = _continuationId } }));\n\n            var filter = CreateFilter();\n            \n            // Act\n            filter.OnStateElection(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(\n                ctx => \n                    ctx.BackgroundJobId == _continuationId &&\n                    ctx.ExpectedStates.SingleOrDefault(s => s == AwaitingState.StateName) != null &&\n                    ctx.NewState.Name == \"Enqueued\")));\n        }\n\n        [Fact]\n        public void OnStateElection_ExecuteContinuations_InFailedState_OnNextStateDeserializationError()\n        {\n            // Arrange\n            _context.ApplyContext.BackgroundJob = new BackgroundJobMock { Id = _parentId };\n            _context.ApplyContext.NewStateObject = new SucceededState(null, 0, 0);\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobData(_continuationId)).Returns(new JobData());\n            _context.ApplyContext.Connection.Setup(x => x.GetStateData(_continuationId)).Returns(new StateData\n            {\n                Name = AwaitingState.StateName,\n                Data = new Dictionary<string, string> { { \"NextState\", \"hello\" } }\n            });\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobParameter(_parentId, \"Continuations\"))\n                .Returns(SerializationHelper.Serialize(new List<Continuation> { new Continuation { JobId = _continuationId } }));\n\n            var filter = CreateFilter();\n\n            // Act\n            filter.OnStateElection(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.BackgroundJobId == _continuationId &&\n                ctx.ExpectedStates.SingleOrDefault(s => s == AwaitingState.StateName) != null &&\n                ctx.NewState.Name == FailedState.StateName)));\n        }\n\n        [Fact]\n        public void OnStateElection_ExecuteContinuations_InFailedState_WhenNextStateDoesNotExist()\n        {\n            // Arrange\n            _context.ApplyContext.BackgroundJob = new BackgroundJobMock { Id = _parentId };\n            _context.ApplyContext.NewStateObject = new SucceededState(null, 0, 0);\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobData(_continuationId)).Returns(new JobData());\n            _context.ApplyContext.Connection.Setup(x => x.GetStateData(_continuationId)).Returns(new StateData\n            {\n                Name = AwaitingState.StateName\n            });\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobParameter(_parentId, \"Continuations\"))\n                .Returns(SerializationHelper.Serialize(new List<Continuation> { new Continuation { JobId = _continuationId } }));\n\n            var filter = CreateFilter();\n\n            // Act\n            filter.OnStateElection(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.BackgroundJobId == _continuationId &&\n                ctx.ExpectedStates.SingleOrDefault(s => s == AwaitingState.StateName) != null &&\n                ctx.NewState.Name == FailedState.StateName)));\n        }\n\n        [Fact]\n        public void OnStateElection_SkipsContinuations_WhenTheirCurrentState_IsNotAwaiting()\n        {\n            // Arrange\n            _context.ApplyContext.BackgroundJob = new BackgroundJobMock { Id = _parentId };\n            _context.ApplyContext.NewStateObject = new SucceededState(null, 0, 0);\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobData(_continuationId)).Returns(new JobData());\n            _context.ApplyContext.Connection.Setup(x => x.GetStateData(_continuationId)).Returns(new StateData());\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobParameter(_parentId, \"Continuations\"))\n                .Returns(SerializationHelper.Serialize(new List<Continuation> { new Continuation { JobId = _continuationId } }));\n\n            var filter = CreateFilter();\n\n            // Act\n            filter.OnStateElection(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.IsAny<StateChangeContext>()), Times.Never);\n            Assert.Equal(\"Succeeded\", _context.Object.CandidateState.Name);\n        }\n\n        [Fact]\n        public void OnStateElection_SkipsExpiredContinuations()\n        {\n            // Arrange\n            _context.ApplyContext.BackgroundJob = new BackgroundJobMock { Id = _parentId };\n            _context.ApplyContext.NewStateObject = new SucceededState(null, 0, 0);\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobData(_continuationId)).Returns((JobData)null);\n            _context.ApplyContext.Connection.Setup(x => x.GetStateData(_continuationId)).Returns((StateData)null);\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobParameter(_parentId, \"Continuations\"))\n                .Returns(SerializationHelper.Serialize(new List<Continuation> { new Continuation { JobId = _continuationId } }));\n\n            var filter = CreateFilter();\n\n            // Act & Assert\n            filter.OnStateElection(_context.Object);\n\n            _stateChanger.Verify(x => x.ChangeState(It.IsAny<StateChangeContext>()), Times.Never);\n            Assert.Equal(\"Succeeded\", _context.Object.CandidateState.Name);\n        }\n\n        [Fact(Timeout = 20 * 1000)]\n        public void OnStateElection_DoesNotStuckForever_WhenContinuationHasNoCorrespondingStateEntry()\n        {\n            // Arrange\n            _context.ApplyContext.BackgroundJob = new BackgroundJobMock { Id = _parentId };\n            _context.ApplyContext.NewStateObject = new SucceededState(null, 0, 0);\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobData(_continuationId)).Returns(new JobData());\n            _context.ApplyContext.Connection.Setup(x => x.GetStateData(_continuationId)).Returns((StateData)null);\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobParameter(_parentId, \"Continuations\"))\n                .Returns(SerializationHelper.Serialize(new List<Continuation> { new Continuation { JobId = _continuationId } }));\n\n            var filter = CreateFilter();\n\n            // Act\n            filter.OnStateElection(_context.Object);\n\n            // Asser\n            _stateChanger.Verify(x => x.ChangeState(It.IsAny<StateChangeContext>()), Times.Never);\n            Assert.Equal(\"Succeeded\", _context.Object.CandidateState.Name);\n        }\n\n        [Fact]\n        public void OnStateElection_SkipsContinuations_WithNullIds()\n        {\n            // Arrange\n            _context.ApplyContext.BackgroundJob = new BackgroundJobMock { Id = _parentId };\n            _context.ApplyContext.NewStateObject = new SucceededState(null, 0, 0);\n\n            _context.ApplyContext.Connection.Setup(x => x.GetStateData(null)).Throws<ArgumentNullException>();\n\n            _context.ApplyContext.Connection.Setup(x => x.GetJobParameter(_parentId, \"Continuations\"))\n                .Returns(SerializationHelper.Serialize(new List<Continuation> { new Continuation { JobId = null } }));\n            \n            var filter = CreateFilter();\n\n            // Act\n            filter.OnStateElection(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.IsAny<StateChangeContext>()), Times.Never);\n            Assert.Equal(\"Succeeded\", _context.Object.CandidateState.Name);\n        }\n\n        [Fact]\n        public void OnStateUnapplied_DoesNotThrow()\n        {\n            var filter = (IApplyStateFilter)CreateFilter();\n            // Does not throw\n            filter.OnStateUnapplied(null, null);\n        }\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void HandlesChangingProcessOfInternalDataSerialization()\n        {\n            SerializationHelper.SetUserSerializerSettings(SerializerSettingsHelper.DangerousSettings);\n\n            var continuationsJson = SerializationHelper.Serialize(new List<Continuation>\n            {\n                new Continuation {JobId = \"1\", Options = JobContinuationOptions.OnAnyFinishedState},\n                new Continuation {JobId = \"3214324\", Options = JobContinuationOptions.OnlyOnSucceededState}\n            }, SerializationOption.User);\n\n            var continuations = SerializationHelper.Deserialize<List<Continuation>>(continuationsJson);\n\n            Assert.NotNull(continuations);\n            Assert.Equal(2, continuations.Count);\n            Assert.Equal(\"1\", continuations[0].JobId);\n            Assert.Equal(JobContinuationOptions.OnAnyFinishedState, continuations[0].Options);\n            Assert.Equal(\"3214324\", continuations[1].JobId);\n            Assert.Equal(JobContinuationOptions.OnlyOnSucceededState, continuations[1].Options);\n        }\n\n        // https://github.com/HangfireIO/Hangfire/issues/1470\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void DeserializeContinuations_CanHandleFieldBasedSerialization_OfContinuationClass()\n        {\n#pragma warning disable 618\n            JobHelper.SetSerializerSettings(new JsonSerializerSettings { ContractResolver = new FieldsOnlyContractResolver() });\n#pragma warning restore 618\n            var payload = \"[{\\\"<JobId>k__BackingField\\\":\\\"123\\\",\\\"<Options>k__BackingField\\\":1},{\\\"<JobId>k__BackingField\\\":\\\"456\\\",\\\"<Options>k__BackingField\\\":0}]\";\n\n            var data = DeserializeContinuations(payload);\n\n            Assert.Equal(\"123\", data[0].JobId);\n            Assert.Equal(JobContinuationOptions.OnlyOnSucceededState, data[0].Options);\n\n            Assert.Equal(\"456\", data[1].JobId);\n            Assert.Equal(JobContinuationOptions.OnAnyFinishedState, data[1].Options);\n        }\n\n        private class FieldsOnlyContractResolver: DefaultContractResolver \n        {\n            protected override List<MemberInfo> GetSerializableMembers(Type objectType)\n                => objectType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)\n                    .Cast<MemberInfo>()\n                    .ToList();\n\n            protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) \n                => base.CreateProperties(type, MemberSerialization.Fields);\n        }\n\n        private ContinuationsSupportAttribute CreateFilter()\n        {\n            return new ContinuationsSupportAttribute(\n                pushResults: false,\n                ContinuationsSupportAttribute.KnownFinalStates,\n                _stateChanger.Object);\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/CronFacts.cs",
    "content": "﻿using System;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class CronFacts\n    {\n        [Fact]\n        public void Minutely_ReturnsFormattedString()\n        {\n            string expected = \"* * * * *\";\n            string actual = Cron.Minutely();\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Hourly_WithoutMinute_ReturnsFormattedStringWithDefaults()\n        {\n            string expected = \"0 * * * *\";\n            string actual = Cron.Hourly();\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Hourly_WithMinute_ReturnsFormattedStringWithMinute()\n        {\n            string expected = \"5 * * * *\";\n            string actual = Cron.Hourly(5);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Daily_WithoutMinuteOrHour_ReturnsFormattedStringWithDefaults()\n        {\n            string expected = \"0 0 * * *\";\n            string actual = Cron.Daily();\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Daily_WithoutMinute_ReturnsFormattedStringWithHourAndZeroMinute()\n        {\n            string expected = \"0 5 * * *\";\n            string actual = Cron.Daily(5);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Daily_WithMinuteAndHour_ReturnsFormattedStringWithHourAndMinute()\n        {\n            string expected = \"5 5 * * *\";\n            string actual = Cron.Daily(5, 5);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Weekly_WithoutDayHourMinute_ReturnsFormattedStringWithDefaults()\n        {\n            string expected = \"0 0 * * \" + ((int)DayOfWeek.Monday).ToString();\n            string actual = Cron.Weekly();\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Weekly_WithDayWithoutHourMinute_ReturnsFormattedStringWithDay()\n        {\n            DayOfWeek day = DayOfWeek.Thursday;\n            string expected = \"0 0 * * \" + ((int)day).ToString();\n            string actual = Cron.Weekly(day);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Weekly_WithDayHourWithoutMinute_ReturnsFormattedStringWithDayHour()\n        {\n            DayOfWeek day = DayOfWeek.Thursday;\n            int hour = 5;\n            string expected = \"0 \" + hour.ToString() + \" * * \" + ((int)day).ToString();\n            string actual = Cron.Weekly(day, hour);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Weekly_WithDayHourMinute_ReturnsFormattedStringWithDayHourMinute()\n        {\n            DayOfWeek day = DayOfWeek.Thursday;\n            int hour = 5;\n            int minute = 5;\n            string expected = minute.ToString() + \" \" + hour.ToString() + \" * * \" + ((int)day).ToString();\n            string actual = Cron.Weekly(day, hour, minute);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Monthly_WithoutDayHourMinute_ReturnsFormattedStringWithDefaults()\n        {\n            string expected = \"0 0 1 * *\";\n            string actual = Cron.Monthly();\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Monthly_WithoutHourMinuteWithDay_ReturnsFormattedStringWithDay()\n        {\n            int day = 6;\n            string expected = \"0 0 \" + day.ToString() + \" * *\";\n            string actual = Cron.Monthly(day);\n            Assert.Equal(expected, actual);\n        }\n        \n        [Fact]\n        public void Monthly_WithoutMinuteWithDayHour_ReturnsFormattedStringWithDayHour()\n        {\n            int day = 7;\n            int hour = 4;\n            string expected = \"0 \" + hour.ToString() + \" \" + day.ToString() + \" * *\";\n            string actual = Cron.Monthly(day, hour);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Monthly_WithDayHourMinute_ReturnsFormattedStringWithDayHourMinute()\n        {\n            int day = 7;\n            int hour = 4;\n            int minute = 23;\n            string expected = minute.ToString() + \" \" + hour.ToString() + \" \" + day.ToString() + \" * *\";\n            string actual = Cron.Monthly(day, hour, minute);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Yearly_WithoutMonthDayHourMinute_ReturnsFormattedStringWithDefaults()\n        {\n            string expected = \"0 0 1 1 *\";\n            string actual = Cron.Yearly();\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Yearly_WithoutDayHourMinuteWithMonth_ReturnsFormattedStringWithMonth()\n        {\n            int month = 7;\n            string expected = \"0 0 1 \" + month.ToString() + \" *\";\n            string actual = Cron.Yearly(month);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Yearly_WithoutHourMinuteWithMonthDay_ReturnsFormattedStringWithMonthDay()\n        {\n            int month = 8;\n            int day = 18;\n            string expected = \"0 0 \" + day.ToString() + \" \" + month.ToString() + \" *\";\n            string actual = Cron.Yearly(month, day);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Yearly_WithoutMinuteWithMonthDayHour_ReturnsFormattedStringWithMonthDayHour()\n        {\n            int month = 3;\n            int day = 18;\n            int hour = 14;\n            string expected = \"0 \" + hour.ToString() + \" \" + day.ToString() + \" \" + month.ToString() + \" *\";\n            string actual = Cron.Yearly(month, day, hour);\n            Assert.Equal(expected, actual);\n        }\n\n        [Fact]\n        public void Yearly_WithMonthDayHourMinute_ReturnsFormattedStringWithMonthDayHourMinute()\n        {\n            int month = 4;\n            int day = 17;\n            int hour = 3;\n            int minute = 45;\n            string expected = minute.ToString() + \" \" + hour.ToString() + \" \" + day.ToString() + \" \" + month.ToString() + \" *\";\n            string actual = Cron.Yearly(month, day, hour, minute);\n            Assert.Equal(expected, actual);\n        }\n\n\t\t[Fact]\n\t\tpublic void Never_ReturnsFormattedString()\n\t\t{\n\t\t\tstring expected = \"0 0 31 2 *\";\n\t\t\tstring actual = Cron.Yearly(2, 31);\n\t\t\tAssert.Equal(expected, actual);\n\t\t}\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Dashboard/BatchCommandDispatcherFacts.cs",
    "content": "﻿using Hangfire.Core.Tests.Stubs;\nusing Hangfire.Dashboard;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Dashboard\n{\n    public class BatchCommandDispatcherFacts\n    {\n        [Fact]\n        public void Dispatch_Sets401StatusCode_WhenNotPermitted()\n        {\n            var options = new DashboardOptions\n            {\n                IsReadOnlyFunc = _ => true\n            };\n            var context = new DashboardContextStub(options);\n            var dispatcher = new BatchCommandDispatcher((DashboardContext ctx, string str) => { });\n            dispatcher.Dispatch(context);\n            Assert.Equal(401, context.Response.StatusCode);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Dashboard/CommandDispatcherFacts.cs",
    "content": "﻿using Hangfire.Core.Tests.Stubs;\nusing Hangfire.Dashboard;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Dashboard\n{\n    public class CommandDispatcherFacts\n    {\n        [Fact]\n        public void Dispatch_Sets401StatusCode_WhenNotPermitted()\n        {\n            var options = new DashboardOptions\n            {\n                IsReadOnlyFunc = _ => true\n            };\n            var context = new DashboardContextStub(options);\n            var dispatcher = new CommandDispatcher((DashboardContext _) => false);\n            dispatcher.Dispatch(context);\n            Assert.Equal(401, context.Response.StatusCode);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Dashboard/DashboardOptionsFacts.cs",
    "content": "﻿using System.Linq;\nusing Hangfire.Dashboard;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Dashboard\n{\n    public class DashboardOptionsFacts\n    {\n        [Fact]\n        public void Ctor_SetsDefaultValues_ForAllOptions()\n        {\n            var options = new DashboardOptions();\n            Assert.Equal(\"/\", options.AppPath);\n            Assert.Equal(\"\", options.PrefixPath);\n            Assert.NotNull(options.Authorization);\n            Assert.IsType<LocalRequestsOnlyAuthorizationFilter>(options.Authorization.FirstOrDefault());\n            Assert.Equal(2000, options.StatsPollingInterval);\n            Assert.True(options.DisplayStorageConnectionString);\n            Assert.Equal(\"Hangfire Dashboard\", options.DashboardTitle);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Dashboard/HtmlHelperFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Dashboard;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Dashboard\n{\n    public class HtmlHelperFacts\n    {\n        private readonly Mock<RazorPage> _page;\n\n        public HtmlHelperFacts()\n        {\n            _page = new Mock<RazorPage>();\n        }\n\n        [Fact]\n        public void ToHumanDuration_FormatsFractionalSeconds()\n        {\n            var helper = CreateHelper();\n            var result = helper.ToHumanDuration(TimeSpan.FromSeconds(1.087));\n            Assert.Equal(\"+1.087s\", result);\n        }\n\n        private HtmlHelper CreateHelper()\n        {\n            return new HtmlHelper(_page.Object);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/GlobalConfigurationExtensionsFacts.cs",
    "content": "﻿using Hangfire.Common;\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Serialization;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class GloabalConfigurationExtensionsFacts\n    {\n        [Fact, CleanSerializerSettings]\n        public void UseSerializationSettings_AffectSerializationWithUserSettings()\n        {\n            GlobalConfiguration.Configuration.UseSerializerSettings(new JsonSerializerSettings\n            {\n                ContractResolver = new CamelCasePropertyNamesContractResolver()\n            });\n\n            var result = SerializationHelper.Serialize(new CustomClass { StringProperty = \"Value\" }, SerializationOption.User);\n            Assert.Equal(@\"{\"\"stringProperty\"\":\"\"Value\"\"}\", result);\n        }\n\n        [Fact, CleanSerializerSettings]\n        public void UseSerializationSettingsWithCallback_AffectSerializationWithUserSettings()\n        {\n            GlobalConfiguration.Configuration.UseRecommendedSerializerSettings(settings =>\n            {\n                settings.ContractResolver = new CamelCasePropertyNamesContractResolver();\n            });\n\n            var result = SerializationHelper.Serialize(new CustomClass { StringProperty = \"Value\" }, SerializationOption.User);\n            Assert.Equal(@\"{\"\"stringProperty\"\":\"\"Value\"\"}\", result);\n        }\n\n        public class CustomClass\n        {\n            public string StringProperty { get; set; }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/GlobalStateHandlersFacts.cs",
    "content": "﻿using System.Linq;\nusing Hangfire.States;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class GlobalStateHandlersFacts\n    {\n        [Fact]\n        public void AllBasicHandlersShouldBeIncluded()\n        {\n            var handlerTypes = GlobalStateHandlers.Handlers.Select(x => x.GetType()).ToArray();\n\n            Assert.Contains(typeof(SucceededState.Handler), handlerTypes);\n            Assert.Contains(typeof(ScheduledState.Handler), handlerTypes);\n            Assert.Contains(typeof(EnqueuedState.Handler), handlerTypes);\n            Assert.Contains(typeof(DeletedState.Handler), handlerTypes);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/GlobalTestsConfiguration.cs",
    "content": "﻿using Xunit;\n\n[assembly: CollectionBehavior(MaxParallelThreads = 1)]\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Hangfire.Core.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n  <PropertyGroup>\r\n    <TargetFrameworks>net452;net461;net6.0;net8.0</TargetFrameworks>\r\n    <NoWarn>0618</NoWarn>\r\n  </PropertyGroup>\r\n\r\n  <ItemGroup>\r\n    <PackageReference Include=\"Moq.Sequences\" Version=\"2.1.0\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net452' or '$(TargetFramework)'=='net461'\">\r\n    <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies\" Version=\"1.0.3\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"5.0.1\" NoWarn=\"NU1903\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net6.0' or '$(TargetFramework)'=='net8.0'\">\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.Core\\Hangfire.Core.csproj\" />\r\n  </ItemGroup>\r\n\r\n  <Target Name=\"ChangeAliasesOfStrongNameAssemblies\" BeforeTargets=\"FindReferenceAssembliesForReferences;ResolveReferences\">\r\n    <ItemGroup>\r\n      <ReferencePath Condition=\"'%(FileName)' == 'Cronos'\">\r\n        <Aliases>ReferencedCronos</Aliases>\r\n      </ReferencePath>\r\n    </ItemGroup>\r\n  </Target>\r\n</Project>\r\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Hangfire.Core.Tests.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=mocks/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "tests/Hangfire.Core.Tests/JobActivatorFacts.cs",
    "content": "﻿using System;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class JobActivatorFacts\n    {\n        [Fact, GlobalLock]\n        public void SetCurrent_ThrowsAnException_WhenValueIsNull()\n        {\n            Assert.Throws<ArgumentNullException>(() => JobActivator.Current = null);\n        }\n\n        [Fact, GlobalLock]\n        public void GetCurrent_ReturnsPreviouslySetValue()\n        {\n            var activator = new JobActivator();\n            JobActivator.Current = activator;\n\n            Assert.Same(activator, JobActivator.Current);\n        }\n\n        [Fact]\n        public void DefaultActivator_CanCreateInstanceOfClassWithDefaultConstructor()\n        {\n            var activator = new JobActivator();\n\n            var instance = activator.ActivateJob(typeof (DefaultConstructor));\n\n            Assert.NotNull(instance);\n        }\n\n        [Fact]\n        public void DefaultActivator_ThrowAnException_IfThereIsNoDefaultConstructor()\n        {\n            var activator = new JobActivator();\n\n            Assert.Throws<MissingMethodException>(\n                () => activator.ActivateJob(typeof (CustomConstructor)));\n        }\n\n        public class DefaultConstructor\n        {\n        }\n\n        public class CustomConstructor\n        {\n// ReSharper disable once UnusedParameter.Local\n            public CustomConstructor(string arg)\n            {\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/JobCancellationTokenFacts.cs",
    "content": "﻿using System;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class JobCancellationTokenFacts\n    {\n        [Fact]\n        public void ShutdownToken_IsInCanceledState_WhenPassingTrueValue()\n        {\n            var token = new JobCancellationToken(true);\n            Assert.True(token.ShutdownToken.IsCancellationRequested);\n        }\n\n        [Fact]\n        public void ThrowIfCancellationRequested_DoesNotThrowOnFalseValue()\n        {\n            var token = new JobCancellationToken(false);\n\n            // Does not throw\n            token.ThrowIfCancellationRequested();\n        }\n\n        [Fact]\n        public void ThrowIfCancellationRequested_ThrowsOnTrueValue()\n        {\n            var token = new JobCancellationToken(true);\n\n            Assert.Throws<OperationCanceledException>(\n                () => token.ThrowIfCancellationRequested());\n        }\n\n        [Fact]\n        public void Null_ReturnsNullValue()\n        {\n            Assert.Null(JobCancellationToken.Null);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/JobParameterInjectionFilterFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Moq;\nusing Newtonsoft.Json;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class JobParameterInjectionFilterFacts\n    {\n        private readonly PerformContextMock _context;\n\n        public JobParameterInjectionFilterFacts()\n        {\n            _context = new PerformContextMock();\n        }\n\n        [Fact]\n        public void OnPerforming_ThrowsArgumentNullException_WhenContextIsNull()\n        {\n            var filter = CreateFilter();\n\n            var exception = Assert.Throws<ArgumentNullException>(() => filter.OnPerforming(null));\n\n            Assert.Equal(\"context\", exception.ParamName);\n        }\n\n        [Fact]\n        public void OnPerforming_HandlesParameterlessMethods_WithoutDoingAnything()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => Parameterless());\n            var filter = CreateFilter();\n\n            filter.OnPerforming(_context.GetPerformingContext());\n\n            _context.Connection.Verify(x => x.GetJobParameter(It.IsAny<string>(), It.IsAny<string>()), Times.Never);\n        }\n\n        [Fact]\n        public void OnPerforming_DoesNotModifyNonDecoratedParameters()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => NonDecorated(\"hello\", 1));\n            var filter = CreateFilter();\n\n            filter.OnPerforming(_context.GetPerformingContext());\n\n            _context.Connection.Verify(x => x.GetJobParameter(It.IsAny<string>(), It.IsAny<string>()), Times.Never);\n            Assert.Equal(\"hello\", _context.BackgroundJob.Job.Args[0]);\n            Assert.Equal(1, _context.BackgroundJob.Job.Args[1]);\n        }\n\n        [Fact]\n        public void OnPerforming_ModifiesParameters_DecoratedWithASpecialAttribute_ThatHaveNullValue()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => Decorated(null));\n            _context.Connection.Setup(x => x.GetJobParameter(_context.BackgroundJob.Id, \"Result123\")).Returns(\"\\\"Hello!\\\"\");\n            var filter = CreateFilter();\n\n            filter.OnPerforming(_context.GetPerformingContext());\n\n            Assert.Equal(\"Hello!\", _context.BackgroundJob.Job.Args[0]);\n        }\n        \n        [Fact]\n        public void OnPerforming_RewritesValueTypeParameters_DecoratedWithASpecialAttribute_ThatHaveDefaultValue()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => Decorated(default(int)));\n            _context.Connection.Setup(x => x.GetJobParameter(_context.BackgroundJob.Id, \"Result123\")).Returns(\"12345\");\n            var filter = CreateFilter();\n\n            filter.OnPerforming(_context.GetPerformingContext());\n\n            Assert.Equal(12345, _context.BackgroundJob.Job.Args[0]);\n        }\n\n        [Fact]\n        public void OnPerforming_RewritesParameters_ThatDoNotHaveNullValue()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => Decorated(\"NonNull\"));\n            _context.Connection.Setup(x => x.GetJobParameter(_context.BackgroundJob.Id, \"Result123\")).Returns(\"\\\"Hello!\\\"\");\n            var filter = CreateFilter();\n\n            filter.OnPerforming(_context.GetPerformingContext());\n\n            Assert.Equal(\"Hello!\", _context.BackgroundJob.Job.Args[0]);\n        }\n\n        [Fact]\n        public void OnPerforming_RewritesValueTypeParameters_DecoratedWithASpecialAttribute_ThatHaveNonDefaultValue()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => Decorated(123456));\n            _context.Connection.Setup(x => x.GetJobParameter(_context.BackgroundJob.Id, \"Result123\")).Returns(\"54321\");\n            var filter = CreateFilter();\n\n            filter.OnPerforming(_context.GetPerformingContext());\n\n            Assert.Equal(54321, _context.BackgroundJob.Job.Args[0]);\n        }\n\n        [Fact]\n        public void OnPerforming_DoesNotRewriteDefaultValueWithNull_WhenJobParameterIsNull()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => Decorated(0));\n            _context.Connection.Setup(x => x.GetJobParameter(_context.BackgroundJob.Id, \"Result123\")).Returns<string>(null);\n            var filter = CreateFilter();\n\n            filter.OnPerforming(_context.GetPerformingContext());\n\n            Assert.Equal(0, _context.BackgroundJob.Job.Args[0]);\n        }\n\n        [Fact]\n        public void OnPerforming_DoesNotRewriteNonDefaultValueWithNull_WhenJobParameterIsNull()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => Decorated(123456));\n            _context.Connection.Setup(x => x.GetJobParameter(_context.BackgroundJob.Id, \"Result123\")).Returns<string>(null);\n            var filter = CreateFilter();\n\n            filter.OnPerforming(_context.GetPerformingContext());\n\n            Assert.Equal(123456, _context.BackgroundJob.Job.Args[0]);\n        }\n\n        [Fact]\n        public void OnPerforming_ThrowsDeserializationException_WhenParameterCanNotBeDeserialized()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => Decorated(null));\n            _context.Connection.Setup(x => x.GetJobParameter(_context.BackgroundJob.Id, \"Result123\")).Returns(\"adk;hg\");\n            var filter = CreateFilter();\n\n            Assert.ThrowsAny<JsonException>(() => filter.OnPerforming(_context.GetPerformingContext()));\n        }\n\n        [Fact]\n        public void OnPerforming_ModifiesParameters_BasedOnFromResultAttribute()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => DecoratedWithFromResult(null));\n            _context.Connection.Setup(x => x.GetJobParameter(_context.BackgroundJob.Id, \"AntecedentResult\")).Returns(\"\\\"Result\\\"\");\n            var filter = CreateFilter();\n\n            filter.OnPerforming(_context.GetPerformingContext());\n\n            Assert.Equal(\"Result\", _context.BackgroundJob.Job.Args[0]);\n        }\n\n        [Fact]\n        public void OnPerforming_CanModifyMultipleParameters()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => Decorated(null, null));\n            _context.Connection.Setup(x => x.GetJobParameter(_context.BackgroundJob.Id, \"Param1\")).Returns(\"\\\"Value1\\\"\");\n            _context.Connection.Setup(x => x.GetJobParameter(_context.BackgroundJob.Id, \"Param2\")).Returns(\"2\");\n            var filter = CreateFilter();\n\n            filter.OnPerforming(_context.GetPerformingContext());\n\n            Assert.Equal(\"Value1\", _context.BackgroundJob.Job.Args[0]);\n            Assert.Equal(2, _context.BackgroundJob.Job.Args[1]);\n        }\n\n        [Fact]\n        public void OnPerformed_DoesNotThrow_AnyException()\n        {\n            var filter = CreateFilter();\n            filter.OnPerformed(_context.GetPerformedContext());\n        }\n\n        private JobParameterInjectionFilter CreateFilter()\n        {\n            return new JobParameterInjectionFilter();\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Parameterless()\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void NonDecorated(string arg1, int arg2)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Decorated([FromParameter(\"Result123\")] string value)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Decorated([FromParameter(\"Result123\")] int value)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void DecoratedWithFromResult([FromResult] string value)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Decorated([FromParameter(\"Param1\")] string value1, [FromParameter(\"Param2\")] int? value2)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/JobStorageFacts.cs",
    "content": "﻿using System;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class JobStorageFacts\n    {\n        private readonly Mock<JobStorage> _storage;\n\n        public JobStorageFacts()\n        {\n            _storage = new Mock<JobStorage>() { CallBase = true };\n        }\n\n        [Fact, GlobalLock(Reason = \"Access static JobStorage.Current member\")]\n        public void SetCurrent_DoesNotThrowAnException_WhenValueIsNull()\n        {\n            // Does not throw\n            JobStorage.Current = null;\n        }\n\n        [Fact, GlobalLock(Reason = \"Access static JobStorage.Current member\")]\n        public void GetCurrent_ThrowsAnException_OnUninitializedValue()\n        {\n            JobStorage.Current = null;\n\n            Assert.Throws<InvalidOperationException>(() => JobStorage.Current);\n        }\n\n        [Fact, GlobalLock(Reason = \"Access static JobStorage.Current member\")]\n        public void GetCurrent_ReturnsCurrentValue_WhenInitialized()\n        {\n            var storage = new Mock<JobStorage>();\n            JobStorage.Current = storage.Object;\n\n            Assert.Same(storage.Object, JobStorage.Current);\n        }\n\n        [Fact]\n        public void GetComponents_ReturnsEmptyCollectionByDefault()\n        {\n            Assert.Empty(_storage.Object.GetComponents());\n        }\n\n        [Fact]\n        public void GetStateHandlers_ReturnsEmptyCollectionByDefault()\n        {\n            Assert.Empty(_storage.Object.GetStateHandlers());\n        }\n\n        [Fact]\n        public void JobExpirationTimeout_HasDefaultTimeoutFromDays1()\n        {\n            Assert.Equal(TimeSpan.FromDays(1), _storage.Object.JobExpirationTimeout);\n        }\n\n        [Fact]\n        public void JobExpirationTimeout_CantAllowTimeoutLessThanOneHour()\n        {\n            var negative = TimeSpan.FromSeconds(-1);\n            var exception = Assert.Throws<ArgumentException>(() => _storage.Object.JobExpirationTimeout = negative);\n\n            Assert.StartsWith(\"JobStorage.JobExpirationTimeout value should be equal or greater than zero.\", exception.Message);\n\n        }\n\n        [Fact]\n        public void JobExpirationTimeout_CanChangeTheTimeout()\n        {\n            Assert.Equal(TimeSpan.FromDays(1), _storage.Object.JobExpirationTimeout);\n\n            _storage.Object.JobExpirationTimeout = TimeSpan.FromHours(1);\n\n            Assert.Equal(TimeSpan.FromHours(1), _storage.Object.JobExpirationTimeout);\n\n            GlobalConfiguration.Configuration\n                .UseStorage(_storage.Object)\n                .WithJobExpirationTimeout(TimeSpan.FromDays(3));\n\n            Assert.Equal(TimeSpan.FromDays(3), _storage.Object.JobExpirationTimeout);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/LatencyTimeoutAttributeFacts.cs",
    "content": "﻿using System;\nusing Hangfire.States;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class LatencyTimeoutAttributeFacts\n    {\n        private const string JobId = \"id\";\n\n        private readonly ElectStateContextMock _context;\n\n        public LatencyTimeoutAttributeFacts()\n        {\n            var state = new ProcessingState(\"Default\", \"1\");\n\n            _context = new ElectStateContextMock();\n            _context.ApplyContext.BackgroundJob.Id = JobId;\n            _context.ApplyContext.NewStateObject = state;\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenTimeoutInSecondsValueIsNegative()\n        {\n            var exception = Assert.Throws<ArgumentOutOfRangeException>(\n                () => CreateFilter(-1));\n            \n            Assert.Equal(\"timeoutInSeconds\", exception.ParamName);\n        }\n\n        [Fact]\n        public void OnStateElection_ChangesToDeleted_IfTimeoutExceeded()\n        {\n            _context.ApplyContext.BackgroundJob.CreatedAt = DateTime.UtcNow.AddMinutes(-1);\n\n            var filter = CreateFilter(10);\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<DeletedState>(_context.Object.CandidateState);\n        }\n\n        [Fact]\n        public void OnStateElection_DoesNotChangeAnything_IfTimeoutNotExceeded()\n        {\n            _context.ApplyContext.BackgroundJob.CreatedAt = DateTime.UtcNow;\n\n            var filter = CreateFilter(100);\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<ProcessingState>(_context.Object.CandidateState);\n        }\n\n        private static LatencyTimeoutAttribute CreateFilter(int timeout)\n        {\n            return new LatencyTimeoutAttribute(timeout);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Mocks/ApplyStateContextMock.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Hangfire.Profiling;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\n\nnamespace Hangfire.Core.Tests\n{\n    public class ApplyStateContextMock\n    {\n        private readonly Lazy<ApplyStateContext> _context;\n\n        public ApplyStateContextMock()\n        {\n            Storage = new Mock<JobStorage>();\n            Connection = new Mock<IStorageConnection>();\n            Transaction = new Mock<IWriteOnlyTransaction>();\n            BackgroundJob = new BackgroundJobMock();\n            NewState = new Mock<IState>();\n            OldStateName = null;\n            JobExpirationTimeout = TimeSpan.FromMinutes(1);\n            StateMachine = new Mock<IStateMachine>();\n\n            _context = new Lazy<ApplyStateContext>(\n                () => new ApplyStateContext(\n                    Storage.Object,\n                    Connection.Object,\n                    Transaction.Object,\n                    BackgroundJob.Object,\n                    NewStateObject ?? NewState.Object,\n                    OldStateName,\n                    EmptyProfiler.Instance,\n                    StateMachine.Object,\n                    CustomData)\n                {\n                    JobExpirationTimeout = JobExpirationTimeout\n                });\n        }\n\n        public Mock<JobStorage> Storage { get; set; }\n        public Mock<IStorageConnection> Connection { get; set; } \n        public Mock<IWriteOnlyTransaction> Transaction { get; set; } \n        public BackgroundJobMock BackgroundJob { get; set; } \n        public IState NewStateObject { get; set; }\n        public Mock<IState> NewState { get; set; }\n        public string OldStateName { get; set; }\n        public TimeSpan JobExpirationTimeout { get; set; }\n        public Mock<IStateMachine> StateMachine { get; set; } \n        public IReadOnlyDictionary<string, object> CustomData { get; set; }\n\n        public ApplyStateContext Object => _context.Value;\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Mocks/BackgroundJobMock.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\n\nnamespace Hangfire.Core.Tests\n{\n    public class BackgroundJobMock\n    {\n        private readonly Lazy<BackgroundJob> _object;\n\n        public BackgroundJobMock()\n        {\n            Id = \"JobId\";\n            Job = Job.FromExpression(() => SomeMethod());\n            CreatedAt = DateTime.UtcNow;\n\n            _object = new Lazy<BackgroundJob>(\n                () => new BackgroundJob(Id, Job, CreatedAt));\n        }\n\n        public string Id { get; set; }\n        public Job Job { get; set; }\n        public DateTime CreatedAt { get; set; }\n\n        public BackgroundJob Object => _object.Value;\n\n        public static void SomeMethod() { }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Mocks/BackgroundProcessContextMock.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Hangfire.Server;\nusing Moq;\n\nnamespace Hangfire.Core.Tests\n{\n    public class BackgroundProcessContextMock\n    {\n        private readonly Lazy<BackgroundProcessContext> _context;\n\n        public BackgroundProcessContextMock()\n        {\n            ServerId = \"server\";\n            Storage = new Mock<JobStorage>();\n            Properties = new Dictionary<string, object>();\n            ExecutionId = Guid.NewGuid();\n            StoppingTokenSource = new CancellationTokenSource();\n            StoppedTokenSource = new CancellationTokenSource();\n            ShutdownTokenSource = new CancellationTokenSource();\n\n            _context = new Lazy<BackgroundProcessContext>(\n                () => new BackgroundProcessContext(ServerId, Storage.Object, Properties, ExecutionId,\n                    StoppingTokenSource.Token, StoppedTokenSource.Token, ShutdownTokenSource.Token));\n        }\n\n        public BackgroundProcessContext Object => _context.Value;\n\n        public string ServerId { get; set; }\n        public Mock<JobStorage> Storage { get; set; }\n        public IDictionary<string, object> Properties { get; set; } \n        public Guid ExecutionId { get; set; }\n        public CancellationTokenSource StoppingTokenSource { get; set; }\n        public CancellationTokenSource StoppedTokenSource { get; set; }\n        public CancellationTokenSource ShutdownTokenSource { get; set; }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Mocks/CreateContextMock.cs",
    "content": "﻿using System;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\n\nnamespace Hangfire.Core.Tests\n{\n    public class CreateContextMock\n    {\n        private readonly Lazy<CreateContext> _context;\n\n        public CreateContextMock()\n        {\n            Storage = new Mock<JobStorage>();\n            Connection = new Mock<IStorageConnection>();\n            Job = Job.FromExpression(() => Method());\n            InitialState = new Mock<IState>();\n\n            _context = new Lazy<CreateContext>(\n                () => new CreateContext(\n                    Storage.Object,\n                    Connection.Object,\n                    Job,\n                    InitialState.Object));\n        }\n\n        public Mock<JobStorage> Storage { get; set; }\n        public Mock<IStorageConnection> Connection { get; set; }\n        public Job Job { get; set; }\n        public Mock<IState> InitialState { get; set; } \n\n        public CreateContext Object => _context.Value;\n\n        public static void Method() { }\n\n        public CreatingContext GetCreatingContext()\n        {\n            return new CreatingContext(Object);\n        }\n\n        public CreatedContext GetCreatedContext(\n            string jobId, DateTime? createdAt = null, bool canceled = false, Exception exception = null)\n        {\n            return new CreatedContext(\n                Object,\n                new BackgroundJob(jobId, Job, createdAt ?? DateTime.UtcNow),\n                canceled,\n                exception);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Mocks/ElectStateContextMock.cs",
    "content": "﻿using System;\nusing Hangfire.States;\n\nnamespace Hangfire.Core.Tests\n{\n    public class ElectStateContextMock\n    {\n        private readonly Lazy<ElectStateContext> _context;\n\n        public ElectStateContextMock()\n        {\n            ApplyContext = new ApplyStateContextMock();\n\n            _context = new Lazy<ElectStateContext>(\n                () => new ElectStateContext(ApplyContext.Object));\n        }\n\n        public ApplyStateContextMock ApplyContext { get; set; }\n\n        public ElectStateContext Object => _context.Value;\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Mocks/PerformContextMock.cs",
    "content": "﻿using System;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Moq;\n\nnamespace Hangfire.Core.Tests\n{\n    public class PerformContextMock\n    {\n        private readonly Lazy<PerformContext> _context;\n\n        public PerformContextMock()\n        {\n            Storage = new Mock<JobStorage>();\n            Connection = new Mock<IStorageConnection>();\n            BackgroundJob = new BackgroundJobMock();\n            CancellationToken = new Mock<IJobCancellationToken>();\n\n            _context = new Lazy<PerformContext>(\n                () => new PerformContext(Storage.Object, Connection.Object, BackgroundJob.Object, CancellationToken.Object));\n        }\n        \n        public Mock<JobStorage> Storage { get; set; }\n        public Mock<IStorageConnection> Connection { get; set; }\n        public BackgroundJobMock BackgroundJob { get; set; }\n        public Mock<IJobCancellationToken> CancellationToken { get; set; } \n\n        public PerformContext Object => _context.Value;\n\n        public static void SomeMethod()\n        {\n        }\n\n        public PerformingContext GetPerformingContext()\n        {\n            return new PerformingContext(Object);\n        }\n\n        public PerformedContext GetPerformedContext(object result = null, bool canceled = false, Exception exception = null)\n        {\n            return new PerformedContext(Object, result, canceled, exception);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Mocks/StateChangeContextMock.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Hangfire.Profiling;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\n\nnamespace Hangfire.Core.Tests\n{\n    public class StateChangeContextMock\n    {\n        private readonly Lazy<StateChangeContext> _context;\n\n        public StateChangeContextMock()\n        {\n            Storage = new Mock<JobStorage>();\n            Connection = new Mock<IStorageConnection>();\n            BackgroundJobId = \"JobId\";\n            NewState = new Mock<IState>();\n            ExpectedStates = null;\n            DisableFilters = false;\n            CancellationToken = CancellationToken.None;\n\n            _context = new Lazy<StateChangeContext>(\n                () => new StateChangeContext(\n                    Storage.Object,\n                    Connection.Object,\n                    Transaction?.Object,\n                    BackgroundJobId,\n                    NewState.Object,\n                    ExpectedStates,\n                    DisableFilters,\n                    CompleteJob?.Object,\n                    CancellationToken,\n                    EmptyProfiler.Instance,\n                    ServerId,\n                    CustomData));\n        }\n\n        public Mock<JobStorage> Storage { get; set; }\n        public Mock<IStorageConnection> Connection { get; set; }\n        public Mock<JobStorageTransaction> Transaction { get; set; }\n        public Mock<IFetchedJob> CompleteJob { get; set; }\n        public string BackgroundJobId { get; set; }\n        public Mock<IState> NewState { get; set; }\n        public IEnumerable<string> ExpectedStates { get; set; }\n        public bool DisableFilters { get; set; }\n        public CancellationToken CancellationToken { get; set; }\n        public string ServerId { get; set; }\n        public IReadOnlyDictionary<string, object> CustomData { get; set; }\n        \n        public StateChangeContext Object => _context.Value;\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Obsolete/ServerWatchdogOptionsFacts.cs",
    "content": "﻿using System;\nusing System.Threading;\nusing Hangfire.Server;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Obsolete\n{\n    public class ServerWatchdogOptionsFacts\n    {\n        [Fact]\n        public void Ctor_InitializeProperties_WithCorrectValues()\n        {\n            var options = CreateOptions();\n\n            Assert.Equal(TimeSpan.FromMinutes(5), options.CheckInterval);\n            Assert.Equal(TimeSpan.FromMinutes(5), options.ServerTimeout);\n        }\n\n        [Fact]\n        public void ServerTimeout_ThrowsAnException_WhenValueIsTooLarge()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ServerTimeout = TimeSpan.FromHours(25));\n        }\n\n        [Fact]\n        public void ServerTimeout_ThrowsAnException_WhenValueIsNegative()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ServerTimeout = TimeSpan.FromHours(-5));\n        }\n\n        [Fact]\n        public void ServerTimeout_ThrowsAnException_WhenValueIsInfinite()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ServerTimeout = Timeout.InfiniteTimeSpan);\n        }\n\n        [Fact]\n        public void CheckInterval_ThrowsAnException_WhenValueIsNegative()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.CheckInterval = TimeSpan.FromHours(-5));\n        }\n\n        [Fact]\n        public void CheckInterval_ThrowsAnException_WhenValueIsTooLarge()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.CheckInterval = TimeSpan.FromHours(25));\n        }\n\n        [Fact]\n        public void CheckInterval_ThrowsAnException_WhenValueIsInfinite()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.CheckInterval = Timeout.InfiniteTimeSpan);\n        }\n\n        [Fact]\n        public void CheckInterval_DoesNotThrowException_WhenValueIsZero()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.CheckInterval = Timeout.InfiniteTimeSpan);\n        }\n\n        private static ServerWatchdogOptions CreateOptions()\n        {\n            return new ServerWatchdogOptions();\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/PreserveCultureAttributeFacts.cs",
    "content": "using System;\nusing System.Globalization;\nusing Hangfire.Annotations;\nusing Hangfire.Client;\nusing Hangfire.Server;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class PreserveCultureAttributeFacts\n    {\n        private readonly CreatingContext _creatingContext;\n        private readonly PerformingContext _performingContext;\n        private readonly PerformedContext _performedContext;\n        private readonly Mock<IStorageConnection> _connection;\n        private const string JobId = \"id\";\n\n        public PreserveCultureAttributeFacts()\n        {\n            _connection = new Mock<IStorageConnection>();\n\n            var storage = new Mock<JobStorage>();\n            var backgroundJob = new BackgroundJobMock { Id = JobId };\n            var state = new Mock<IState>();\n\n            var createContext = new CreateContext(\n                storage.Object, _connection.Object, backgroundJob.Job, state.Object);\n            _creatingContext = new CreatingContext(createContext);\n\n            var performContext = new PerformContext(\n                _connection.Object, backgroundJob.Object, new Mock<IJobCancellationToken>().Object);\n            _performingContext = new PerformingContext(performContext);\n            _performedContext = new PerformedContext(performContext, null, false, null);\n        }\n\n        [Fact]\n        public void OnCreating_ThrowsAnException_WhenContextIsNull()\n        {\n            var filter = CreateFilter();\n\n            Assert.Throws<ArgumentNullException>(\n                () => filter.OnCreating(null));\n        }\n\n        [Fact]\n        public void OnCreating_CapturesCultures_AndSetsThemAsJobParameters()\n        {\n            CultureHelper.SetCurrentCulture(\"ru-RU\");\n            CultureHelper.SetCurrentUICulture(\"ru-RU\");\n\n            var filter = CreateFilter();\n            filter.OnCreating(_creatingContext);\n\n            Assert.Equal(\"ru-RU\", _creatingContext.GetJobParameter<string>(\"CurrentCulture\"));\n            Assert.Equal(\"ru-RU\", _creatingContext.GetJobParameter<string>(\"CurrentUICulture\"));\n        }\n\n        [Fact]\n        public void OnCreating_CapturesInvariantCulture_AndSetsStringEmptyAsJobParameters()\n        {\n            CultureHelper.SetCurrentCulture(CultureInfo.InvariantCulture);\n            CultureHelper.SetCurrentUICulture(CultureInfo.InvariantCulture);\n\n            var filter = CreateFilter();\n            filter.OnCreating(_creatingContext);\n\n            Assert.Equal(String.Empty, _creatingContext.GetJobParameter<string>(\"CurrentCulture\"));\n            Assert.Equal(String.Empty, _creatingContext.GetJobParameter<string>(\"CurrentUICulture\"));\n        }\n\n        [Fact]\n        public void OnCreated_DoesNotThrowAnException()\n        {\n            var filter = CreateFilter();\n\n            // Does not throw\n            filter.OnCreated(null);\n        }\n\n        [Fact]\n        public void OnPerforming_SetsThreadCultures_ToTheSpecifiedOnesInJobParameters()\n        {\n            _connection.Setup(x => x.GetJobParameter(JobId, \"CurrentCulture\")).Returns(\"\\\"ru-RU\\\"\");\n            _connection.Setup(x => x.GetJobParameter(JobId, \"CurrentUICulture\")).Returns(\"\\\"ru-RU\\\"\");\n\n            CultureHelper.SetCurrentCulture(\"en-US\");\n            CultureHelper.SetCurrentUICulture(\"en-US\");\n\n            var filter = CreateFilter();\n            filter.OnPerforming(_performingContext);\n\n            Assert.Equal(\"ru-RU\", CultureInfo.CurrentCulture.Name);\n            Assert.Equal(\"ru-RU\", CultureInfo.CurrentUICulture.Name);\n        }\n\n        [Fact]\n        public void OnPerforming_SetsInvariantThreadCultures_WhenJobParametersAreEmptyStrings()\n        {\n            _connection.Setup(x => x.GetJobParameter(JobId, \"CurrentCulture\")).Returns(\"\\\"\\\"\");\n            _connection.Setup(x => x.GetJobParameter(JobId, \"CurrentUICulture\")).Returns(\"\\\"\\\"\");\n\n            CultureHelper.SetCurrentCulture(\"en-US\");\n            CultureHelper.SetCurrentUICulture(\"en-US\");\n\n            var filter = CreateFilter();\n            filter.OnPerforming(_performingContext);\n\n            Assert.Equal(CultureInfo.InvariantCulture, CultureInfo.CurrentCulture);\n            Assert.Equal(CultureInfo.InvariantCulture, CultureInfo.CurrentUICulture);\n        }\n\n        [Fact]\n        public void OnPerforming_DoesNotDoAnything_WhenCultureJobParameterIsNotSet()\n        {\n            _connection.Setup(x => x.GetJobParameter(JobId, \"CurrentCulture\")).Returns((string)null);\n            _connection.Setup(x => x.GetJobParameter(JobId, \"CurrentUICulture\")).Returns((string)null);\n\n            CultureHelper.SetCurrentCulture(\"en-US\");\n            CultureHelper.SetCurrentUICulture(\"en-US\");\n\n            var filter = CreateFilter();\n            filter.OnPerforming(_performingContext);\n\n            Assert.Equal(\"en-US\", CultureInfo.CurrentCulture.Name);\n            Assert.Equal(\"en-US\", CultureInfo.CurrentUICulture.Name);\n        }\n\n        [Fact]\n        public void OnPerformed_ThrowsAnException_WhenContextIsNull()\n        {\n            var filter = CreateFilter();\n\n            Assert.Throws<ArgumentNullException>(() => filter.OnPerformed(null));\n        }\n\n        [Fact]\n        public void OnPerformed_RestoresPreviousCurrentCulture()\n        {\n            _connection.Setup(x => x.GetJobParameter(JobId, \"CurrentCulture\")).Returns(\"\\\"ru-RU\\\"\");\n            _connection.Setup(x => x.GetJobParameter(JobId, \"CurrentUICulture\")).Returns(\"\\\"ru-RU\\\"\");\n\n            CultureHelper.SetCurrentCulture(\"en-US\");\n            CultureHelper.SetCurrentUICulture(\"en-US\");\n\n            var filter = CreateFilter();\n            filter.OnPerforming(_performingContext);\n            filter.OnPerformed(_performedContext);\n\n            Assert.Equal(\"en-US\", CultureInfo.CurrentCulture.Name);\n            Assert.Equal(\"en-US\", CultureInfo.CurrentUICulture.Name);\n        }\n\n        [Fact]\n        public void OnPerformed_RestoresPreviousCurrentCulture_OnlyIfItWasChanged()\n        {\n            _connection.Setup(x => x.GetJobParameter(JobId, \"CurrentCulture\")).Returns((string)null);\n            _connection.Setup(x => x.GetJobParameter(JobId, \"CurrentUICulture\")).Returns((string)null);\n\n            CultureHelper.SetCurrentCulture(\"en-US\");\n            CultureHelper.SetCurrentUICulture(\"en-US\");\n\n            var filter = CreateFilter();\n            filter.OnPerforming(_performingContext);\n            filter.OnPerformed(_performedContext);\n\n            Assert.Equal(\"en-US\", CultureInfo.CurrentCulture.Name);\n            Assert.Equal(\"en-US\", CultureInfo.CurrentUICulture.Name);\n        }\n\n        private static CaptureCultureAttribute CreateFilter()\n        {\n            return new CaptureCultureAttribute();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Processing/TaskExtensionsFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Xunit;\nusing TaskExtensions = Hangfire.Processing.TaskExtensions;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Processing\n{\n    public class TaskExtensionsFacts\n    {\n        private readonly ManualResetEvent _mre;\n        private readonly CancellationTokenSource _cts;\n\n        public TaskExtensionsFacts()\n        {\n            _mre = new ManualResetEvent(false);\n            _cts = new CancellationTokenSource();\n        }\n\n        [Fact]\n        public async Task WaitOneAsync_ThrowsArgNullException_WhenWaitHandleIsNull()\n        {\n            var exception = await Assert.ThrowsAsync<ArgumentNullException>(\n                async () => await TaskExtensions.WaitOneAsync(null, TimeSpan.Zero, CancellationToken.None));\n\n            Assert.Equal(\"waitHandle\", exception.ParamName);\n        }\n\n        [Fact]\n        public async Task WaitOneAsync_ThrowsOpCanceledException_WhenCancellationTokenIsCanceled()\n        {\n            _cts.Cancel();\n\n            var exception = await Assert.ThrowsAsync<OperationCanceledException>(\n                async () => await TaskExtensions.WaitOneAsync(_mre, TimeSpan.Zero, _cts.Token));\n\n            Assert.Equal(_cts.Token, exception.CancellationToken);\n        }\n\n        [Fact]\n        public async Task WaitOneAsync_ThrowsOpCanceledException_EvenWhenWaitHandleIsSignaled()\n        {\n            _cts.Cancel();\n            _mre.Set();\n\n            var exception = await Assert.ThrowsAsync<OperationCanceledException>(\n                async () => await TaskExtensions.WaitOneAsync(_mre, Timeout.InfiniteTimeSpan, _cts.Token));\n\n            Assert.Equal(_cts.Token, exception.CancellationToken);\n        }\n\n        [Fact]\n        public async Task WaitOneAsync_ReturnsTrue_WhenWaitHandleIsSignaled()\n        {\n            _mre.Set();\n\n            var result = await TaskExtensions.WaitOneAsync(_mre, Timeout.InfiniteTimeSpan, _cts.Token);\n\n            Assert.True(result);\n        }\n\n        [Fact]\n        public async Task WaitOneAsync_ReturnsTrue_WhenWaitHandleIsSignaled_AndTimeoutIsZero()\n        {\n            _mre.Set();\n\n            var result = await TaskExtensions.WaitOneAsync(_mre, TimeSpan.Zero, _cts.Token);\n\n            Assert.True(result);\n        }\n\n        [Fact]\n        public async Task WaitOneAsync_ReturnsFalseImmediately_WhenNotSignaled_AndTimeoutIsZero()\n        {\n             var result = await TaskExtensions.WaitOneAsync(_mre, TimeSpan.Zero, _cts.Token);\n\n             Assert.False(result);\n        }\n\n        [Fact]\n        public async Task WaitOneAsync_WaitsAndReturnsFalse_WhenNotSignaled_AndNonNullTimeout()\n        {\n            using (var mre = new ManualResetEvent(false))\n            {\n                var sw = Stopwatch.StartNew();\n                var result = await TaskExtensions.WaitOneAsync(mre, TimeSpan.FromMilliseconds(500), CancellationToken.None);\n                sw.Stop();\n\n                Assert.False(result, \"result != false\");\n                Assert.False(mre.WaitOne(TimeSpan.Zero), \"mre is signaled\");\n                Assert.True(sw.Elapsed > TimeSpan.FromMilliseconds(450), $\"Elapsed: {sw.Elapsed.TotalMilliseconds} ms, Expected: 450 ms\");\n            }\n        }\n\n        [Fact]\n        public async Task WaitOneAsync_WaitsAndThrowsTaskCanceled_WhenNotSignaled_AndCancellationTokenIsCanceled()\n        {\n            var sw = Stopwatch.StartNew();\n            _cts.CancelAfter(TimeSpan.FromMilliseconds(500));\n\n            var exception = await Assert.ThrowsAnyAsync<OperationCanceledException>(\n                async () => await TaskExtensions.WaitOneAsync(_mre, Timeout.InfiniteTimeSpan, _cts.Token));\n            sw.Stop();\n\n#if !NET452\n            Assert.Equal(_cts.Token, exception.CancellationToken);\n#else\n            Assert.NotNull(exception);\n#endif\n            Assert.True(sw.Elapsed > TimeSpan.FromMilliseconds(450), $\"Elapsed: {sw.Elapsed.TotalMilliseconds} ms, Expected: 450 ms\");\n        }\n\n        [Fact]\n        public void WaitOne_ThrowsArgNullException_WhenWaitHandleIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => TaskExtensions.WaitOne(null, TimeSpan.Zero, CancellationToken.None));\n\n            Assert.Equal(\"waitHandle\", exception.ParamName);\n        }\n\n        [Fact]\n        public void WaitOne_ThrowsOpCanceledException_WhenCancellationTokenIsCanceled()\n        {\n            _cts.Cancel();\n\n            var exception = Assert.Throws<OperationCanceledException>(\n                () => TaskExtensions.WaitOne(_mre, TimeSpan.Zero, _cts.Token));\n\n            Assert.Equal(_cts.Token, exception.CancellationToken);\n        }\n\n        [Fact]\n        public void WaitOne_ThrowsOpCanceledException_EvenWhenWaitHandleIsSignaled()\n        {\n            _cts.Cancel();\n            _mre.Set();\n\n            var exception = Assert.Throws<OperationCanceledException>(\n                () => TaskExtensions.WaitOne(_mre, Timeout.InfiniteTimeSpan, _cts.Token));\n\n            Assert.Equal(_cts.Token, exception.CancellationToken);\n        }\n\n        [Fact]\n        public void WaitOne_ReturnsTrue_WhenWaitHandleIsSignaled()\n        {\n            _mre.Set();\n\n            var result = TaskExtensions.WaitOne(_mre, Timeout.InfiniteTimeSpan, _cts.Token);\n\n            Assert.True(result);\n        }\n\n        [Fact]\n        public void WaitOne_ReturnsTrue_WhenWaitHandleIsSignaled_AndTimeoutIsZero()\n        {\n            _mre.Set();\n\n            var result = TaskExtensions.WaitOne(_mre, TimeSpan.Zero, _cts.Token);\n\n            Assert.True(result);\n        }\n\n        [Fact]\n        public void WaitOne_ReturnsFalseImmediately_WhenNotSignaled_AndTimeoutIsZero()\n        {\n             var result = TaskExtensions.WaitOne(_mre, TimeSpan.Zero, CancellationToken.None);\n\n             Assert.False(result);\n        }\n\n        [Fact]\n        public void WaitOne_WaitsAndReturnsFalse_WhenNotSignaled_AndNonNullTimeout()\n        {\n            using (var mre = new ManualResetEvent(false))\n            {\n                var sw = Stopwatch.StartNew();\n                var result = TaskExtensions.WaitOne(mre, TimeSpan.FromMilliseconds(500), CancellationToken.None);\n                sw.Stop();\n\n                Assert.False(result, \"result != false\");\n                Assert.False(mre.WaitOne(TimeSpan.Zero), \"mre is signaled\");\n                Assert.True(sw.Elapsed > TimeSpan.FromMilliseconds(450), $\"Elapsed: {sw.Elapsed.TotalMilliseconds} ms, Expected: 450 ms\");\n            }\n        }\n\n        [Fact]\n        public void WaitOne_WaitsAndThrowsTaskCanceled_WhenNotSignaled_AndCancellationTokenIsCanceled()\n        {\n            var sw = Stopwatch.StartNew();\n            _cts.CancelAfter(TimeSpan.FromMilliseconds(500));\n\n            var exception = Assert.ThrowsAny<OperationCanceledException>(\n                () => TaskExtensions.WaitOne(_mre, Timeout.InfiniteTimeSpan, _cts.Token));\n            sw.Stop();\n\n#if !NET452\n            Assert.Equal(_cts.Token, exception.CancellationToken);\n#else\n            Assert.NotNull(exception);\n#endif\n            Assert.True(sw.Elapsed > TimeSpan.FromMilliseconds(450), $\"Elapsed: {sw.Elapsed.TotalMilliseconds} ms, Expected: 450 ms\");\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Profiling/ProfilerFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Hangfire.Logging;\nusing Hangfire.Profiling;\nusing Moq;\nusing Xunit;\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Profiling\n{\n    public class ProfilerFacts\n    {\n        private readonly Mock<ILog> _logger = new Mock<ILog>();\n        private readonly object _instance = new object();\n\n        public ProfilerFacts()\n        {\n            _logger.Setup(x => x.Log(It.IsAny<LogLevel>(), null, null)).Returns(true);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetProfilers))]\n        internal void InvokeMeasured_ThrowsAnException_WhenActionIsNull(IProfiler profiler)\n        {\n            var exception = Assert.Throws<ArgumentNullException>(() => profiler.InvokeMeasured(\n                _instance,\n                null));\n\n            Assert.Equal(\"action\", exception.ParamName);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetProfilers))]\n        internal void InvokeMeasured_ReturnsResult_ForFunctions(IProfiler profiler)\n        {\n            var result = profiler.InvokeMeasured(_instance, x =>\n            {\n                Assert.Same(_instance, x);\n                return x.ToString();\n            });\n\n            Assert.Equal(_instance.ToString(), result);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetProfilers))]\n        internal void InvokeMeasured_DoesNotThrowAnException_WhenInstanceIsNull(IProfiler profiler)\n        {\n            var result = profiler.InvokeMeasured((object)null, x =>\n            {\n                Assert.Null(x);\n                return true;\n            });\n\n            Assert.True(result);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetProfilers))]\n        internal void InvokeMeasured_WithAction_InvokesIt(IProfiler profiler)\n        {\n            var invoked = false;\n\n            profiler.InvokeMeasured(_instance, x =>\n            {\n                Assert.Same(_instance, x);\n                invoked = true;\n            });\n\n            Assert.True(invoked);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetProfilers))]\n        internal void InvokeMeasured_WithActionAndNullInstance_InvokesIt(IProfiler profiler)\n        {\n            var invoked = false;\n\n            profiler.InvokeMeasured((object)null, x =>\n            {\n                Assert.Null(x);\n                invoked = true;\n            });\n\n            Assert.True(invoked);\n        }\n\n        [Fact]\n        internal void SlowLog_GeneratesLogMessage_WhenThresholdReached_WithNullMessage()\n        {\n            var profiler = CreateSlowLogProfiler(_logger, TimeSpan.FromSeconds(-1));\n            profiler.InvokeMeasured(_instance, x => x.ToString());\n\n            _logger.Verify(x => x.Log(LogLevel.Warn, It.IsNotNull<Func<string>>(), null), Times.Once);\n        }\n\n        [Fact]\n        internal void SlowLog_GeneratesLogMessage_WhenThresholdReached_WithNullInstance()\n        {\n            var profiler = CreateSlowLogProfiler(_logger, TimeSpan.FromSeconds(-1));\n            profiler.InvokeMeasured((object)null, x => true);\n\n            _logger.Verify(x => x.Log(LogLevel.Warn, It.IsNotNull<Func<string>>(), null), Times.Once);\n        }\n\n        [Fact]\n        internal void SlowLog_GeneratesLogMessage_WhenThresholdReached_WithNonNullMessage()\n        {\n            var profiler = CreateSlowLogProfiler(_logger, TimeSpan.FromSeconds(-1));\n            profiler.InvokeMeasured(_instance, x => x.ToString(), _ => \"message\");\n\n            _logger.Verify(x => x.Log(LogLevel.Warn, It.IsNotNull<Func<string>>(), null), Times.Once);\n        }\n\n        [Fact]\n        internal void SlowLog_DoesNotGenerateLogMessage_WhenThresholdIsNotReached()\n        {\n            var profiler = CreateSlowLogProfiler(_logger, TimeSpan.FromSeconds(600));\n            profiler.InvokeMeasured(_instance, x => x.ToString(), _ => \"message\");\n\n            _logger.Verify(x => x.Log(LogLevel.Warn, It.IsNotNull<Func<string>>(), null), Times.Never);\n        }\n\n        public static IEnumerable<object[]> GetProfilers()\n        {\n            yield return new object[] { EmptyProfiler.Instance };\n            yield return new object[] { CreateSlowLogProfiler(new Mock<ILog>(), TimeSpan.FromSeconds(-1)) };\n        }\n\n        private static SlowLogProfiler CreateSlowLogProfiler(Mock<ILog> logger, TimeSpan threshold)\n        {\n            return new SlowLogProfiler(logger.Object, threshold);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/QueueAttributeFacts.cs",
    "content": "﻿using Hangfire.States;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class QueueAttributeFacts\n    {\n        private readonly ElectStateContextMock _context;\n\n        public QueueAttributeFacts()\n        {\n            _context = new ElectStateContextMock\n            {\n                ApplyContext = { NewStateObject = new EnqueuedState(\"queue\") }\n            };\n        }\n\n        [Fact]\n        public void Ctor_CorrectlySets_AllPropertyValues()\n        {\n            var filter = new QueueAttribute(\"hello\");\n            Assert.Equal(\"hello\", filter.Queue);\n        }\n\n        [Fact]\n        public void OnStateElection_OverridesTheQueue_OfTheCandidateState()\n        {\n            var filter = new QueueAttribute(\"override\");\n            filter.OnStateElection(_context.Object);\n\n            Assert.Equal(\"override\", ((EnqueuedState)_context.Object.CandidateState).Queue);\n        }\n\n        [Fact]\n        public void OnStateElection_DoesNotDoAnything_IfStateIsNotEnqueuedState()\n        {\n            var filter = new QueueAttribute(\"override\");\n            var context = new ElectStateContextMock\n            {\n                ApplyContext = { NewState = new Mock<IState>() }\n            };\n\n            // Does not throw\n            filter.OnStateElection(context.Object);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/RecurringJobEntityFacts.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Common;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    [SuppressMessage(\"ReSharper\", \"AssignNullToNotNullAttribute\")]\n    [SuppressMessage(\"ReSharper\", \"ConvertClosureToMethodGroup\")]\n    public class RecurringJobEntityFacts\n    {\n        private const string RecurringJobId = \"recurring-job-id\";\n        private readonly DateTime _nowInstant = new DateTime(2017, 03, 30, 15, 30, 0, DateTimeKind.Utc);\n        private readonly Dictionary<string,string> _recurringJob;\n        private readonly Mock<ITimeZoneResolver> _timeZoneResolver;\n\n        public RecurringJobEntityFacts()\n        {\n            var timeZone = TimeZoneInfo.Local;\n\n            _recurringJob = new Dictionary<string, string>\n            {\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", InvocationData.SerializeJob(Job.FromExpression(() => Console.WriteLine())).SerializePayload() },\n                { \"TimeZoneId\", timeZone.Id }\n            };\n\n            _timeZoneResolver = new Mock<ITimeZoneResolver>();\n            _timeZoneResolver.Setup(x => x.GetTimeZoneById(It.IsAny<string>())).Throws<InvalidTimeZoneException>();\n            _timeZoneResolver.Setup(x => x.GetTimeZoneById(timeZone.Id)).Returns(timeZone);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenRecurringJobId_IsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(() =>\n                new RecurringJobEntity(null, _recurringJob));\n\n            Assert.Equal(\"recurringJobId\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenRecurringJob_IsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(() =>\n                new RecurringJobEntity(RecurringJobId, null));\n\n            Assert.Equal(\"recurringJob\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_SetsRelaxedMisfireOption_WhenCorrespondingKeyIsMissing()\n        {\n            var entity = CreateEntity();\n            Assert.Equal(MisfireHandlingMode.Relaxed, entity.MisfireHandling);\n        }\n\n        [Fact]\n        public void Ctor_CorrectlyParses_RelaxedMisfireOption_SerializedAsInt()\n        {\n            _recurringJob[\"Misfire\"] = \"0\";\n\n            var entity = CreateEntity();\n\n            Assert.Equal(MisfireHandlingMode.Relaxed, entity.MisfireHandling);\n        }\n\n        [Fact]\n        public void Ctor_CorrectlyParses_StrictMisfireOption_SerializedAsInt()\n        {\n            _recurringJob[\"Misfire\"] = \"1\";\n\n            var entity = CreateEntity();\n\n            Assert.Equal(MisfireHandlingMode.Strict, entity.MisfireHandling);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenMisfireOption_CanNotBeParsed()\n        {\n            _recurringJob[\"Misfire\"] = \"2adgadsg\";\n            Assert.Throws<ArgumentException>(() => CreateEntity());\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenMisfireOption_IsNotWithinAValidRange()\n        {\n            _recurringJob[\"Misfire\"] = \"3\";\n            Assert.Throws<NotSupportedException>(() => CreateEntity());\n        }\n\n        [Fact]\n        public void IsChanged_DoesNotAddMisfireKey_WhenItIsNotPresentAndDefaultValueIsUnchanged()\n        {\n            var entity = CreateEntity();\n\n            entity.IsChanged(_nowInstant, out var fields);\n\n            Assert.False(fields.ContainsKey(\"Misfire\"));\n        }\n\n        [Fact]\n        public void IsChanged_DoesNotAddMisfireKey_WhenItIsSetToDefaultAndDefaultValueIsUnchanged()\n        {\n            _recurringJob[\"Misfire\"] = \"0\";\n            var entity = CreateEntity();\n\n            entity.IsChanged(_nowInstant, out var fields);\n\n            Assert.False(fields.ContainsKey(\"Misfire\"));\n        }\n\n        [Fact]\n        public void IsChanged_AddsMisfireKey_WhenItIsNotPresent_ButDefaultValueIsChanged()\n        {\n            var entity = CreateEntity();\n            entity.MisfireHandling = MisfireHandlingMode.Strict;\n\n            entity.IsChanged(_nowInstant, out var fields);\n\n            Assert.True(fields.ContainsKey(\"Misfire\"));\n            Assert.Equal(\"1\", fields[\"Misfire\"]);\n        }\n\n        [Fact]\n        public void IsChanged_ExplicitlySetsTheDefaultValue_WhenItWasSetToStrict()\n        {\n            _recurringJob[\"Misfire\"] = \"1\";\n            var entity = CreateEntity();\n            entity.MisfireHandling = MisfireHandlingMode.Relaxed;\n\n            entity.IsChanged(_nowInstant, out var fields);\n\n            Assert.True(fields.ContainsKey(\"Misfire\"));\n            Assert.Equal(\"0\", fields[\"Misfire\"]);\n        }\n\n        [Fact]\n        public void IsChanged_DoesNotAddMisfireKey_WhenItsNonDefaultValueIsUnchanged()\n        {\n            _recurringJob[\"Misfire\"] = \"1\";\n            var entity = CreateEntity();\n            entity.MisfireHandling = MisfireHandlingMode.Strict;\n\n            entity.IsChanged(_nowInstant, out var fields);\n\n            Assert.False(fields.ContainsKey(\"Misfire\"));\n        }\n\n        private RecurringJobEntity CreateEntity()\n        {\n            return new RecurringJobEntity(RecurringJobId, _recurringJob);\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/RecurringJobManagerFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n#pragma warning disable 618\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests\n{\n    public class RecurringJobManagerFacts\n    {\n        private readonly Mock<JobStorage> _storage;\n        private readonly string _id;\n        private Job _job;\n        private readonly string _cronExpression;\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly Mock<IWriteOnlyTransaction> _transaction;\n        private readonly Mock<IBackgroundJobFactory> _factory;\n        private readonly Mock<IStateMachine> _stateMachine;\n        private readonly DateTime _now = new DateTime(2017, 03, 30, 15, 30, 0, DateTimeKind.Utc);\n        private readonly Func<DateTime> _nowFactory;\n        private readonly BackgroundJob _backgroundJob;\n        private readonly Mock<ITimeZoneResolver> _timeZoneResolver;\n\n        public RecurringJobManagerFacts()\n        {\n            _id = \"recurring-job-id\";\n            _job = Job.FromExpression(() => Method());\n            _backgroundJob = new BackgroundJob(\"my-id\", _job, _now);\n            _cronExpression = Cron.Minutely();\n            _storage = new Mock<JobStorage>();\n            _factory = new Mock<IBackgroundJobFactory>();\n            _stateMachine = new Mock<IStateMachine>();\n            _factory.SetupGet(x => x.StateMachine).Returns(_stateMachine.Object);\n            _nowFactory = () => _now;\n\n            _timeZoneResolver = new Mock<ITimeZoneResolver>();\n            _timeZoneResolver.Setup(x => x.GetTimeZoneById(It.IsAny<string>()))\n                .Returns<string>(TimeZoneInfo.FindSystemTimeZoneById);\n\n            _connection = new Mock<IStorageConnection>();\n            _storage.Setup(x => x.GetConnection()).Returns(_connection.Object);\n\n            _transaction = new Mock<IWriteOnlyTransaction>();\n            _connection.Setup(x => x.CreateWriteTransaction()).Returns(_transaction.Object);\n\n            _factory.Setup(x => x.Create(It.Is<CreateContext>(ctx =>\n                    ctx.Storage == _storage.Object &&\n                    ctx.Connection == _connection.Object &&\n                    ctx.InitialState == null)))\n                .Returns(_backgroundJob);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new RecurringJobManager(null, _factory.Object));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenFactoryIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new RecurringJobManager(_storage.Object, (IBackgroundJobFactory)null));\n\n            Assert.Equal(\"factory\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenTimeZoneResolverIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new RecurringJobManager(_storage.Object, _factory.Object, null, _nowFactory));\n\n            Assert.Equal(\"timeZoneResolver\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenNowFactoryIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new RecurringJobManager(_storage.Object, _factory.Object, _timeZoneResolver.Object, null));\n\n            Assert.Equal(\"nowFactory\", exception.ParamName);\n        }\n\n        [Fact]\n        public void AddOrUpdate_ThrowsAnException_WhenIdIsNull()\n        {\n            var manager = CreateManager();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => manager.AddOrUpdate(null, _job, Cron.Daily()));\n\n            Assert.Equal(\"recurringJobId\", exception.ParamName);\n        }\n\n        [Fact]\n        public void AddOrUpdate_ThrowsAnException_WhenJobIsNull()\n        {\n            var manager = CreateManager();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => manager.AddOrUpdate(_id, (Job)null, Cron.Daily()));\n\n            Assert.Equal(\"job\", exception.ParamName);\n        }\n\n        [Fact]\n        public void AddOrUpdate_ThrowsAnException_WhenQueueNameIsNull()\n        {\n            var manager = CreateManager();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => manager.AddOrUpdate(_id, _job, Cron.Daily(), TimeZoneInfo.Local, null));\n\n            Assert.Equal(\"queue\", exception.ParamName);\n        }\n        \n        [Fact]\n        public void AddOrUpdate_ThrowsAnException_WhenCronExpressionIsNull()\n        {\n            var manager = CreateManager();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => manager.AddOrUpdate(_id, _job, null));\n\n            Assert.Equal(\"cronExpression\", exception.ParamName);\n        }\n\n        [Fact]\n        public void AddOrUpdate_ThrowsAnException_WhenCronExpressionIsInvalid()\n        {\n            var manager = CreateManager();\n\n            var exception = Assert.Throws<ArgumentException>(\n                () => manager.AddOrUpdate(_id, _job, \"* * *\"));\n\n            Assert.Equal(\"cronExpression\", exception.ParamName);\n        }\n\n        [Fact]\n        public void AddOrUpdate_ThrowsAnException_WhenCronExpression_HaveInvalidParts()\n        {\n            var manager = CreateManager();\n\n            var exception = Assert.Throws<ArgumentException>(\n                () => manager.AddOrUpdate(_id, _job, \"* * * * 9999\"));\n\n            Assert.Equal(\"cronExpression\", exception.ParamName);\n        }\n\n        [Fact]\n        public void AddOrUpdate_ThrowsAnException_WhenTimeZoneIsNull()\n        {\n            var manager = CreateManager();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => manager.AddOrUpdate(_id, _job, _cronExpression, (TimeZoneInfo) null));\n\n            Assert.Equal(\"timeZone\", exception.ParamName);\n        }\n\n        [Fact]\n        public void AddOrUpdate_ThrowsAnException_WhenOptionsArgumentIsNull()\n        {\n            var manager = CreateManager();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => manager.AddOrUpdate(_id, _job, _cronExpression, null));\n\n            Assert.Equal(\"options\", exception.ParamName);\n        }\n\n        [Fact]\n        public void AddOrUpdate_ThrowsAnException_WhenQueueIsNull()\n        {\n            var manager = CreateManager();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => manager.AddOrUpdate(_id, _job, _cronExpression, TimeZoneInfo.Utc, null));\n\n            Assert.Equal(\"queue\", exception.ParamName);\n        }\n\n        [Fact]\n        public void AddOrUpdate_ThrowsAnException_WhenJobQueueIsSet_ButStorageDoesNotSupportIt()\n        {\n            // Arrange\n            _storage.Setup(x => x.HasFeature(JobStorageFeatures.JobQueueProperty)).Returns(false);\n            _job = Job.FromExpression(() => Method(), \"some-queue\");\n\n            var manager = CreateManager();\n\n            // Act & Assert\n            Assert.Throws<NotSupportedException>(() => manager.AddOrUpdate(_id, _job, _cronExpression));\n        }\n\n        [Fact]\n        public void AddOrUpdate_AddsAJob_ToTheRecurringJobsSet()\n        {\n            var manager = CreateManager();\n\n            manager.AddOrUpdate(_id, _job, _cronExpression);\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now)));\n        }\n\n        [Fact]\n        public void AddOrUpdate_SetsTheRecurringJobEntry()\n        {\n            var manager = CreateManager();\n\n            manager.AddOrUpdate(_id, _job, _cronExpression);\n\n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{_id}\",\n                It.Is<Dictionary<string, string>>(rj => \n                    rj[\"Cron\"] == \"* * * * *\"\n                    && !String.IsNullOrEmpty(rj[\"Job\"])\n                    && JobHelper.DeserializeDateTime(rj[\"CreatedAt\"]) > _now.AddMinutes(-1))));\n        }\n\n        [Fact]\n        public void AddOrUpdate_CommitsTransaction()\n        {\n            var manager = CreateManager();\n\n            manager.AddOrUpdate(_id, _job, _cronExpression);\n\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_DoesNotUpdateCreatedAtValue_OfExistingJobs()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\"))\n                .Returns(new Dictionary<string, string> { { \"CreatedAt\", JobHelper.SerializeDateTime(_now) } });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, _cronExpression);\n\n            // Assert\n            _transaction.Verify(\n                x => x.SetRangeInHash(\n                    $\"recurring-job:{_id}\",\n                    It.Is<Dictionary<string, string>>(rj => rj.ContainsKey(\"CreatedAt\"))),\n                Times.Never);\n        }\n\n        [Fact]\n        public void AddOrUpdate_IsAbleToScheduleSecondBasedCronExpression()\n        {\n            var manager = CreateManager();\n\n            manager.AddOrUpdate(_id, _job, \"15 * * * * *\");\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now.AddSeconds(15))));\n        }\n\n        [Fact]\n        public void AddOrUpdate_IsAbleToScheduleMacroBasedCronExpression()\n        {\n            var manager = CreateManager();\n\n            manager.AddOrUpdate(_id, _job, \"@hourly\");\n\n            _transaction.Verify(x => x.AddToSet(\n                \"recurring-jobs\",\n                _id,\n                JobHelper.ToTimestamp(_now.AddMinutes(30))));\n        }\n\n        [Fact]\n        public void AddOrUpdate_EnsuresExistingOldJobsAreUpdated()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", _cronExpression },\n                { \"Job\", InvocationData.Serialize(_job).SerializePayload() },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now) },\n                { \"NextExecution\", JobHelper.SerializeDateTime(_now) },\n                { \"Queue\", \"default\" },\n                { \"TimeZoneId\", \"UTC\" },\n                { \"LastJobId\", \"1384\" }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, _cronExpression);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{_id}\", \n                It.Is<Dictionary<string, string>>(dict => dict.Count == 1 && dict[\"V\"] == \"2\")));\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now)));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_CanAddRecurringJob_WithCronThatNeverFires()\n        {\n            // Arrange\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"0 0 31 2 *\");\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{_id}\", \n                It.Is<Dictionary<string, string>>(dict => \n                    dict.ContainsKey(\"Cron\") && dict[\"Cron\"] == \"0 0 31 2 *\" &&\n                    !dict.ContainsKey(\"NextExecution\"))));\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, -1.0D));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_CanResumeRecurringJob_ThatNeverFires()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"0 0 31 2 *\" },\n                { \"Job\", InvocationData.Serialize(_job).SerializePayload() },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now.AddDays(-1)) },\n                { \"NextExecution\", String.Empty },\n                { \"TimeZoneId\", \"UTC\" },\n                { \"LastJobId\", \"1384\" }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"* * * * *\");\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{_id}\",\n                It.Is<Dictionary<string, string>>(dict =>\n                    dict.ContainsKey(\"Cron\") && dict[\"Cron\"] == \"* * * * *\" &&\n                    dict.ContainsKey(\"NextExecution\") && JobHelper.DeserializeDateTime(dict[\"NextExecution\"]) == _now)));\n            \n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now)));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_UsesTimeZoneResolver_WhenCalculatingNextExecution()\n        {\n            // Arrange\n            var timeZone = TimeZoneInfo.FindSystemTimeZoneById(PlatformHelper.IsRunningOnWindows()\n                ? \"Hawaiian Standard Time\"\n                : \"Pacific/Honolulu\");\n\n            _timeZoneResolver.Setup(x => x.GetTimeZoneById(It.IsAny<string>())).Throws<InvalidOperationException>();\n            _timeZoneResolver\n                .Setup(x => x.GetTimeZoneById(It.Is<string>(id => id == \"Hawaiian Standard Time\" || id == \"Pacific/Honolulu\")))\n                .Returns(timeZone);\n\n            // We are returning IANA time zone on Windows and Windows time zone on Linux.\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"0 0 * * *\" },\n                { \"Job\", InvocationData.Serialize(_job).SerializePayload() },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now) },\n                { \"TimeZoneId\", PlatformHelper.IsRunningOnWindows() ? \"Pacific/Honolulu\" : \"Hawaiian Standard Time\" },\n                { \"NextExecution\", JobHelper.SerializeDateTime(_now.AddHours(18).AddMinutes(30)) },\n                { \"Queue\", \"default\" },\n                { \"V\", \"2\" }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"0 0 * * *\", timeZone, \"default\");\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{_id}\", It.Is<Dictionary<string, string>>(dict =>\n                dict.ContainsKey(\"TimeZoneId\") && !dict.ContainsKey(\"NextExecution\"))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now.AddHours(18).AddMinutes(30))));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_DoesNotReScheduleJob_WhenUpdatingIt()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", InvocationData.Serialize(_job).SerializePayload() },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now.AddMinutes(-3)) },\n                { \"LastExecution\", JobHelper.SerializeDateTime(_now.AddMinutes(-2)) },\n                { \"NextExecution\", JobHelper.SerializeDateTime(_now.AddMinutes(-1)) }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"* * * * *\");\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{_id}\", It.Is<Dictionary<string, string>>(dict =>\n                !dict.ContainsKey(\"NextExecution\") || dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_now.AddMinutes(-1)))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now.AddMinutes(-1))));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_CanUpdateRecurringJobs_WhoseMethodCouldNotBeFound()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", InvocationData.Serialize(_job).SerializePayload().Replace(\"Hangfire\", \"Test\") },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now.AddMinutes(-2)) },\n                { \"LastExecution\", JobHelper.SerializeDateTime(_now.AddMinutes(-1)) },\n                { \"NextExecution\", JobHelper.SerializeDateTime(_now) }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"* * * * *\");\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{_id}\", It.Is<Dictionary<string, string>>(\n                dict => dict.ContainsKey(\"Job\") && dict[\"Job\"].Contains(\"Hangfire.Core.Tests\"))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now)));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_CanUpdateRecurringJobs_WhoseJobPropertyCanNotBeDeserialized()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", \"some garbage\" },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now.AddMinutes(-2)) },\n                { \"LastExecution\", JobHelper.SerializeDateTime(_now.AddMinutes(-1)) },\n                { \"NextExecution\", JobHelper.SerializeDateTime(_now) }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"* * * * *\");\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{_id}\", It.Is<Dictionary<string, string>>(\n                dict => dict.ContainsKey(\"Job\") && dict[\"Job\"].Contains(\"Hangfire.Core.Tests\"))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now)));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_ResetsRetryAttemptNumber_WhenUpdatingARecurringJob()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", \"some garbage\" },\n                { \"RetryAttempt\", \"10\" }\n            });\n            \n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"* * * * *\");\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{_id}\", It.Is<Dictionary<string, string>>(\n                dict => dict.ContainsKey(\"RetryAttempt\") && dict[\"RetryAttempt\"] == \"0\")));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_UsesCurrentTime_InsteadOfLastExecution_ToCalculateNextExecution_WhenChangingCronExpression()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"30 12 * * *\" },\n                { \"Job\", InvocationData.Serialize(_job).SerializePayload() },\n                { \"LastExecution\", JobHelper.SerializeDateTime(_now.AddHours(-3)) }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"30 13 * * *\");\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{_id}\", It.Is<Dictionary<string, string>>(dict =>\n                JobHelper.DeserializeDateTime(dict[\"NextExecution\"]) == _now.AddHours(22))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now.AddHours(22))));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_UsesCurrentTime_InsteadOfLastExecution_ToCalculateNextExecution_WhenChangingTimeZone()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"30 13 * * *\" },\n                { \"Job\", InvocationData.Serialize(_job).SerializePayload() },\n                { \"TimeZoneId\", PlatformHelper.IsRunningOnWindows() ? \"Pacific/Honolulu\" : \"Hawaiian Standard Time\" },\n                { \"LastExecution\", JobHelper.SerializeDateTime(_now.AddDays(-3)) }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"30 13 * * *\", TimeZoneInfo.Utc);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{_id}\", It.Is<Dictionary<string, string>>(dict =>\n                JobHelper.DeserializeDateTime(dict[\"NextExecution\"]) == _now.AddHours(22))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now.AddHours(22))));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_CanRecoverARecurringJob_FromErrorState_WithoutSchedulingToThePast()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", InvocationData.Serialize(_job).SerializePayload() },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now.AddDays(-1)) },\n                { \"Error\", \"Some error that's gone\" },\n                { \"NextExecution\", String.Empty },\n                { \"TimeZoneId\", \"UTC\" },\n                { \"LastJobId\", \"1384\" }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"* * * * *\");\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{_id}\",\n                It.Is<Dictionary<string, string>>(dict =>\n                    dict.ContainsKey(\"Error\") && dict[\"Error\"] == String.Empty &&\n                    dict.ContainsKey(\"NextExecution\") && JobHelper.DeserializeDateTime(dict[\"NextExecution\"]) == _now)));\n            \n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now)));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_CanNotTriggerRecurringJob_WhenNextExecutionTimeIsInFuture()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", InvocationData.Serialize(_job).SerializePayload() },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now.AddDays(-1)) },\n                { \"LastExecution\", JobHelper.SerializeDateTime(_now.AddMinutes(-5)) },\n                { \"NextExecution\", JobHelper.SerializeDateTime(_now.AddMinutes(1)) },\n                { \"TimeZoneId\", \"UTC\" }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"* * * * *\");\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{_id}\",\n                It.Is<Dictionary<string, string>>(dict =>\n                    !dict.ContainsKey(\"NextExecution\") || dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_now.AddMinutes(1)))));\n            \n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now.AddMinutes(1))));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void AddOrUpdate_DoesNotUpdate_UnchangedRecurringJob()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Queue\", \"default\" },\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", InvocationData.SerializeJob(_job).SerializePayload() },\n                { \"TimeZoneId\", \"UTC\" },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now.AddDays(-1)) },\n                { \"V\", \"2\" }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"* * * * *\");\n\n            // Assert\n            _transaction.Verify(\n                x => x.SetRangeInHash(It.IsAny<string>(), It.IsAny<IEnumerable<KeyValuePair<string, string>>>()),\n                Times.Never);\n            _transaction.Verify(x => x.Commit(), Times.Never);\n        }\n\n        [Fact]\n        public void AddOrUpdate_DoesNotUpdateRecurringJob_WhenErrorFieldIsSetToEmptyString()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Queue\", \"default\" },\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", InvocationData.SerializeJob(_job).SerializePayload() },\n                { \"TimeZoneId\", \"UTC\" },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now.AddDays(-1)) },\n                { \"V\", \"2\" },\n                { \"Error\", \"\" }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"* * * * *\");\n\n            // Assert\n            _transaction.Verify(\n                x => x.SetRangeInHash(It.IsAny<string>(), It.IsAny<IEnumerable<KeyValuePair<string, string>>>()),\n                Times.Never);\n            _transaction.Verify(x => x.Commit(), Times.Never);\n        }\n\n        [Fact]\n        public void AddOrUpdate_DoesNotUpdateRecurringJob_WhenLastJobIdFieldIsSetToEmptyString()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Queue\", \"default\" },\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", InvocationData.SerializeJob(_job).SerializePayload() },\n                { \"TimeZoneId\", \"UTC\" },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now.AddDays(-1)) },\n                { \"V\", \"2\" },\n                { \"LastJobId\", \"\" }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.AddOrUpdate(_id, _job, \"* * * * *\");\n\n            // Assert\n            _transaction.Verify(\n                x => x.SetRangeInHash(It.IsAny<string>(), It.IsAny<IEnumerable<KeyValuePair<string, string>>>()),\n                Times.Never);\n            _transaction.Verify(x => x.Commit(), Times.Never);\n        }\n\n        [Fact]\n        public void Trigger_ThrowsAnException_WhenIdIsNull()\n        {\n            var manager = CreateManager();\n\n            Assert.Throws<ArgumentNullException>(() => manager.Trigger(null));\n        }\n\n        [Fact]\n        public void Trigger_EnqueuesScheduledJob()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\"))\n                .Returns(new Dictionary<string, string>\n                {\n                    { \"Job\", JobHelper.ToJson(InvocationData.Serialize(Job.FromExpression(() => Console.WriteLine()))) },\n                    { \"Cron\", Cron.Minutely() }\n                });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.Trigger(_id);\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(\n                It.Is<ApplyStateContext>(context => context.NewState is EnqueuedState)));\n        }\n\n        [Fact]\n        public void Trigger_EnqueuedJobToTheSpecificQueue_IfSpecified()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\"))\n                .Returns(new Dictionary<string, string>\n                {\n                    { \"Job\", JobHelper.ToJson(InvocationData.Serialize(_job)) },\n                    { \"Cron\", _cronExpression },\n                    { \"Queue\", \"my_queue\" }\n                });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.Trigger(_id);\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(It.Is<ApplyStateContext>(context =>\n                ((EnqueuedState)context.NewState).Queue == \"my_queue\")));\n        }\n\n        [Fact]\n        public void Trigger_DoesNotThrowIfJobDoesNotExist()\n        {\n            var manager = CreateManager();\n\n            manager.Trigger(_id);\n\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n        }\n\n        [Fact]\n        public void Trigger_CanTriggerRecurringJob_WithCronThatNeverFires()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\"))\n                .Returns(new Dictionary<string, string>\n                {\n                    { \"Job\", JobHelper.ToJson(InvocationData.Serialize(_job)) },\n                    { \"Cron\", \"0 0 31 2 *\" },\n                });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.Trigger(_id);\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()));\n\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{_id}\", It.Is<Dictionary<string, string>>(dict =>\n                dict.ContainsKey(\"LastExecution\") && dict[\"LastExecution\"] == JobHelper.SerializeDateTime(_now) &&\n                !dict.ContainsKey(\"NextExecution\"))));\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, -1.0D));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void Trigger_SchedulesNextExecution_DependingOnCurrentTime_ToTheFuture()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\")).Returns(new Dictionary<string, string>\n            {\n                { \"Cron\", \"* * * * *\" },\n                { \"Job\", InvocationData.Serialize(_job).SerializePayload() },\n                { \"CreatedAt\", JobHelper.SerializeDateTime(_now.AddMinutes(-3)) },\n                { \"LastExecution\", JobHelper.SerializeDateTime(_now.AddMinutes(-2)) },\n                { \"NextExecution\", JobHelper.SerializeDateTime(_now.AddMinutes(-1)) }\n            });\n\n            var manager = CreateManager();\n\n            // Act\n            manager.Trigger(_id);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{_id}\", It.Is<Dictionary<string, string>>(dict =>\n                dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_now.AddMinutes(1)))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", _id, JobHelper.ToTimestamp(_now.AddMinutes(1))));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void Trigger_ThrowsAnException_WhenRecurringJobCanNotBeTriggered_AndDoesNotCreateBackgroundJob()\n        {\n            // Arrange\n            _timeZoneResolver.Setup(x => x.GetTimeZoneById(It.IsAny<string>())).Throws<Exception>();\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{_id}\"))\n                .Returns(new Dictionary<string, string>\n                {\n                    { \"Job\", JobHelper.ToJson(InvocationData.Serialize(Job.FromExpression(() => Console.WriteLine()))) },\n                    { \"Cron\", Cron.Minutely() },\n                    { \"TimeZoneId\", \"UnexistingID\" }\n                });\n\n            var manager = CreateManager();\n\n            // Act\n            Assert.Throws<AggregateException>(() => manager.Trigger(_id));\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()), Times.Never);\n        }\n\n        [Fact]\n        public void RemoveIfExists_ThrowsAnException_WhenIdIsNull()\n        {\n            var manager = CreateManager();\n\n            Assert.Throws<ArgumentNullException>(\n                () => manager.RemoveIfExists(null));\n        }\n\n        [Fact]\n        public void RemoveIfExists_RemovesEntriesAndCommitsTheTransaction()\n        {\n            var manager = CreateManager();\n\n            manager.RemoveIfExists(_id);\n\n            _transaction.Verify(x => x.RemoveFromSet(\"recurring-jobs\", _id));\n            _transaction.Verify(x => x.RemoveHash($\"recurring-job:{_id}\"));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact, CleanSerializerSettings]\n        public void HandlesChangingProcessOfInvocationDataSerialization()\n        {\n            SerializationHelper.SetUserSerializerSettings(SerializerSettingsHelper.DangerousSettings);\n\n            var initialJob = Job.FromExpression(() => Console.WriteLine());\n            var invocationData = InvocationData.Serialize(initialJob);\n\n            var serializedInvocationData = SerializationHelper.Serialize(invocationData, SerializationOption.User);\n\n            var deserializedInvocationData = SerializationHelper.Deserialize<InvocationData>(serializedInvocationData);\n            var deserializedJob = deserializedInvocationData.Deserialize();\n\n            Assert.Equal(initialJob.Args, deserializedJob.Args);\n            Assert.Equal(initialJob.Method, deserializedJob.Method);\n            Assert.Equal(initialJob.Type, deserializedJob.Type);\n        }\n\n        private RecurringJobManager CreateManager()\n        {\n            return new RecurringJobManager(_storage.Object, _factory.Object, _timeZoneResolver.Object, _nowFactory);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Method() { }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/RecurringJobOptionsFacts.cs",
    "content": "using System;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests\n{\n    public class RecurringJobOptionsFacts\n    {\n        [Fact]\n        public void Ctor_SetTheDefaultValues_ForProperties()\n        {\n            var options = new RecurringJobOptions();\n\n            Assert.Equal(TimeZoneInfo.Utc, options.TimeZone);\n            Assert.Equal(\"default\", options.QueueName);\n            Assert.Equal(MisfireHandlingMode.Relaxed, options.MisfireHandling);\n        }\n\n        [Fact]\n        public void SetTimeZone_ThrowsAnException_WhenValueIsNull()\n        {\n            var options = new RecurringJobOptions();\n\n            Assert.Throws<ArgumentNullException>(() => options.TimeZone = null);\n        }\n\n        [Fact]\n        public void SetQueueName_ThrowsAnException_WhenValueIsNull()\n        {\n            var options = new RecurringJobOptions();\n\n            Assert.Throws<ArgumentNullException>(() => options.QueueName = null);\n        }\n\n        [Fact]\n        public void SetQueueName_ThrowsAnException_WhenQueueNameHasInvalidFormat()\n        {\n            var options = new RecurringJobOptions();\n\n            var exception = Assert.Throws<ArgumentException>(\n                () => options.QueueName = \"UPPER_CASE\");\n\n            Assert.Equal(\"value\", exception.ParamName);\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/RetryAttributeFacts.cs",
    "content": "﻿using System;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class RetryAttributeFacts\n    {\n        private const string JobId = \"id\";\n\n        private readonly FailedState _failedState;\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly ElectStateContextMock _context;\n        private readonly Mock<IWriteOnlyTransaction> _transaction;\n\n        public RetryAttributeFacts()\n        {\n            _failedState = new FailedState(new InvalidOperationException());\n            _connection = new Mock<IStorageConnection>();\n            _transaction = new Mock<IWriteOnlyTransaction>();\n\n            _context = new ElectStateContextMock();\n            _context.ApplyContext.BackgroundJob.Id = JobId;\n            _context.ApplyContext.Connection = _connection;\n            _context.ApplyContext.NewStateObject = _failedState;\n        }\n\n        [Fact]\n        public void Ctor_SetsPositiveRetryAttemptsNumber_ByDefault()\n        {\n            var filter = new AutomaticRetryAttribute();\n            Assert.Equal(10, filter.Attempts);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenAttemptsValueIsNegative()\n        {\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => new AutomaticRetryAttribute { Attempts = -1 });\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenDelaysInSecondsIsEmpty()\n        {\n            Assert.Throws<ArgumentNullException>(\n                () => new AutomaticRetryAttribute { DelaysInSeconds = new int[0] });\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenDelaysInSecondsContainsNegativeNumbers()\n        {\n            Assert.Throws<ArgumentException>(\n                () => new AutomaticRetryAttribute { DelaysInSeconds = new [] { 1, -5 } });\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenDelayByAttemptIsNull()\n        {\n            Assert.Throws<ArgumentNullException>(\n                () => new AutomaticRetryAttribute { DelayInSecondsByAttemptFunc = null });\n        }\n\n        [Fact]\n        public void Ctor_SetsOnAttemptsExceededAction_ByDefault()\n        {\n            var filter = new AutomaticRetryAttribute();\n            Assert.Equal(AttemptsExceededAction.Fail, filter.OnAttemptsExceeded);\n        }\n\n        [Fact]\n        public void Ctor_DelayByAttemptIsNotNull_ByDefault()\n        {\n            var filter = new AutomaticRetryAttribute();\n            Assert.NotNull(filter.DelayInSecondsByAttemptFunc);\n        }\n\n        [Fact]\n        public void DelaysInSeconds_SetsValueCorrectly()\n        {\n            var filter = new AutomaticRetryAttribute { DelaysInSeconds = new[] { 5, 8 } };\n\n            Assert.Equal(2, filter.DelaysInSeconds.Length);\n            Assert.Equal(5, filter.DelaysInSeconds[0]);\n            Assert.Equal(8, filter.DelaysInSeconds[1]);\n        }\n\n        [Fact]\n        public void DelaysInSeconds_CanBeSetToNull()\n        {\n            var filter = new AutomaticRetryAttribute { DelaysInSeconds = null };\n            Assert.NotNull(filter);\n        }\n\n        [Fact]\n        public void DelayInSecondsByAttemptFunc_ReturnCorrectValue_WhenCustomFunctionIsSet()\n        {\n            var filter = new AutomaticRetryAttribute { DelayInSecondsByAttemptFunc = attempt => (int)attempt % 3 };\n\n            Assert.Equal(1, filter.DelayInSecondsByAttemptFunc(1));\n            Assert.Equal(2, filter.DelayInSecondsByAttemptFunc(2));\n            Assert.Equal(0, filter.DelayInSecondsByAttemptFunc(3));\n            Assert.Equal(1, filter.DelayInSecondsByAttemptFunc(4));\n            Assert.Equal(2, filter.DelayInSecondsByAttemptFunc(5));\n            Assert.Equal(1, filter.DelayInSecondsByAttemptFunc(100));\n        }\n\n        [Fact]\n        public void OnStateElection_ThrowsAnException_WhenDelayInSecondsByAttemptFuncThrowsAnException()\n        {\n            var exception = new Exception();\n            \n            var filter = new AutomaticRetryAttribute\n            {\n                DelayInSecondsByAttemptFunc = attempt =>\n                {\n                    throw exception;\n                }\n            };\n            \n            var thrownException = Assert.Throws<Exception>(() => filter.OnStateElection(_context.Object));\n\n            Assert.Equal(exception, thrownException);\n        }\n\n        [Fact]\n        public void OnStateElection_UsesDelaysInSeconds_WhenBothDelaysInSecondsAndDelayInSecondsByAttemptFuncAreSpecified()\n        {\n            var filter = new AutomaticRetryAttribute\n            {\n                DelayInSecondsByAttemptFunc = attempt => 1,\n                DelaysInSeconds = new[] { 0 }\n            };\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<EnqueuedState>(_context.Object.CandidateState);\n        }\n\n        [Fact]\n        public void OnStateElection_DoesNotChangeState_IfRetryAttemptsIsSetToZero()\n        {\n            var filter = new AutomaticRetryAttribute { Attempts = 0 };\n            filter.OnStateElection(_context.Object);\n\n            Assert.Same(_failedState, _context.Object.CandidateState);\n        }\n\n        [Fact]\n        public void OnStateElection_ChangeStateToScheduled_IfRetryAttemptsWereNotExceeded()\n        {\n            var filter = CreateFilter();\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<ScheduledState>(_context.Object.CandidateState);\n            Assert.True(((ScheduledState)_context.Object.CandidateState).EnqueueAt > DateTime.UtcNow);\n\n            Assert.NotNull(_context.Object.CandidateState.Reason);\n            Assert.Contains(\"1 of 1\", _context.Object.CandidateState.Reason);\n\n            _connection.Verify(x => x.SetJobParameter(JobId, \"RetryCount\", \"1\"));\n        }\n\n        [Fact]\n        public void OnStateElection_ChangeStateToEnqueued_IfDelayIsZero()\n        {\n            var filter = new AutomaticRetryAttribute\n            {\n                Attempts = 1,\n                DelaysInSeconds = new[] { 0 }\n            };\n            \n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<EnqueuedState>(_context.Object.CandidateState);\n            Assert.NotNull(_context.Object.CandidateState.Reason);\n            Assert.Contains(\"1 of 1\", _context.Object.CandidateState.Reason);\n\n            _connection.Verify(x => x.SetJobParameter(JobId, \"RetryCount\", \"1\"));\n        }\n\n        [Fact]\n        public void OnStateElection_DoesNotChangeAnything_IfCandidateStateIsNotFailedState()\n        {\n            var filter = CreateFilter();\n            var state = new Mock<IState>();\n            _context.ApplyContext.NewStateObject = null;\n            _context.ApplyContext.NewState = state;\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.Same(state.Object, _context.Object.CandidateState);\n        }\n\n        [Fact]\n        public void OnStateElection_DoesNotChangeState_IfRetryAttemptsNumberExceeded()\n        {\n            _connection.Setup(x => x.GetJobParameter(JobId, \"RetryCount\")).Returns(\"1\");\n            var filter = CreateFilter();\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.Same(_failedState, _context.Object.CandidateState);\n        }\n\n        [Fact]\n        public void OnStateElection_ChangesStateToDeleted_IfRetryAttemptsNumberExceededAndOnAttemptsExceededIsSetToDelete()\n        {\n            _connection.Setup(x => x.GetJobParameter(JobId, \"RetryCount\")).Returns(\"1\");\n            var filter = new AutomaticRetryAttribute { Attempts = 1, OnAttemptsExceeded = AttemptsExceededAction.Delete };\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<DeletedState>(_context.Object.CandidateState);\n        }\n\n        [Fact]\n        public void OnStateElection_ChangesStateToFailed_IfRetryAttemptsNumberExceededAndOnAttemptsExceedIsSetToFail()\n        {\n            _connection.Setup(x => x.GetJobParameter(JobId, \"RetryCount\")).Returns(\"1\");\n            var filter = new AutomaticRetryAttribute { Attempts = 1, OnAttemptsExceeded = AttemptsExceededAction.Fail };\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<FailedState>(_context.Object.CandidateState);\n        }\n\n        [Fact]\n        public void OnStateElection_ChangesStateToDeleted_IfRetryAttemptsNumberIsZeroAndOnAttemptsExceedIsSetToDelete()\n        {\n            _connection.Setup(x => x.GetJobParameter(JobId, \"RetryCount\")).Returns(\"0\");\n            var filter = new AutomaticRetryAttribute { Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete };\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<DeletedState>(_context.Object.CandidateState);\n        }\n\n        [Fact]\n        public void OnStateElection_DoesNotDoAnything_WhenOnlyOnIsSpecified_AndIneligibleExceptionIsPassed()\n        {\n            var filter = new AutomaticRetryAttribute { OnlyOn = new[] { typeof(FormatException), typeof(NullReferenceException) } };\n            _context.ApplyContext.NewStateObject = new FailedState(new ArgumentException());\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<FailedState>(_context.Object.CandidateState);\n            _connection.Verify(x => x.GetJobParameter(It.IsAny<string>(), It.IsAny<string>()), Times.Never);\n        }\n\n        [Fact]\n        public void OnStateElection_TriggersRetryAnyway_WhenEmptyOnlyOnIsSpecified()\n        {\n            var filter = new AutomaticRetryAttribute { OnlyOn = Type.EmptyTypes };\n            _context.ApplyContext.NewStateObject = new FailedState(new ArgumentException());\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<ScheduledState>(_context.Object.CandidateState);\n            _connection.Verify(x => x.SetJobParameter(JobId, \"RetryCount\", \"1\"));\n        }\n\n        [Fact]\n        public void OnStateElection_WorksAsExpected_WhenOnlyOnIsSpecified_AndAnEligibleExceptionIsPassed()\n        {\n            var filter = new AutomaticRetryAttribute { OnlyOn = new[] { typeof(NullReferenceException), typeof(InvalidOperationException) } };\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<ScheduledState>(_context.Object.CandidateState);\n            _connection.Verify(x => x.SetJobParameter(JobId, \"RetryCount\", \"1\"));\n        }\n\n        [Fact]\n        public void OnStateElection_WorksAsExpected_WhenOnlyOnIsSpecified_AndADerivedClassOfAnEligibleExceptionIsPassed()\n        {\n            var filter = new AutomaticRetryAttribute { OnlyOn = new[] { typeof(ArgumentException) } };\n            _context.ApplyContext.NewStateObject = new FailedState(new ArgumentNullException());\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<ScheduledState>(_context.Object.CandidateState);\n            _connection.Verify(x => x.SetJobParameter(JobId, \"RetryCount\", \"1\"));\n        }\n\n        [Fact]\n        public void OnStateElection_WorksAsExpected_WhenExceptOnIsSpecified_AndIneligibleExceptionIsPassed()\n        {\n            var filter = new AutomaticRetryAttribute { ExceptOn = new[] { typeof(FormatException), typeof(NullReferenceException) } };\n            _context.ApplyContext.NewStateObject = new FailedState(new ArgumentException());\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<ScheduledState>(_context.Object.CandidateState);\n            _connection.Verify(x => x.SetJobParameter(JobId, \"RetryCount\", \"1\"));\n        }\n\n        [Fact]\n        public void OnStateElection_DoesNotDoAnything_WhenExceptOnIsSpecified_AndAnEligibleExceptionIsPassed()\n        {\n            var filter = new AutomaticRetryAttribute { ExceptOn = new[] { typeof(NullReferenceException), typeof(InvalidOperationException) } };\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<FailedState>(_context.Object.CandidateState);\n            _connection.Verify(x => x.GetJobParameter(It.IsAny<string>(), It.IsAny<string>()), Times.Never);\n        }\n\n        [Fact]\n        public void OnStateElection_DoesNotDoAnything_WhenExceptOnIsSpecified_AndADerivedClassOfAnEligibleExceptionIsPassed()\n        {\n            var filter = new AutomaticRetryAttribute { ExceptOn = new[] { typeof(ArgumentException) } };\n            _context.ApplyContext.NewStateObject = new FailedState(new ArgumentNullException());\n\n            filter.OnStateElection(_context.Object);\n\n            Assert.IsType<FailedState>(_context.Object.CandidateState);\n            _connection.Verify(x => x.GetJobParameter(It.IsAny<string>(), It.IsAny<string>()), Times.Never);\n        }\n\n        [Fact]\n        public void OnStateApplied_AddsJobToRetriesSet_IfNewStateIsScheduled()\n        {\n            // Arrange\n            var filter = CreateFilter();\n\n            var newState = new ScheduledState(DateTime.UtcNow) { Reason = \"Retry attempt ...\" };\n            var applyStateContext = CreatApplyStateContext(newState);\n\n            // Act\n            filter.OnStateApplied(applyStateContext, _transaction.Object);\n\n            // Assert\n            _transaction.Verify(t => t.AddToSet(\"retries\", JobId));\n        }\n\n        [Fact]\n        public void OnStateApplied_DoesNotAddJobToRetriesSet_IfNewStateIsScheduledAndReasonIsNull()\n        {\n            // Arrange\n            var filter = CreateFilter();\n\n            var newState = new ScheduledState(DateTime.UtcNow) { Reason = null };\n            var applyStateContext = CreatApplyStateContext(newState);\n\n            // Act\n            filter.OnStateApplied(applyStateContext, _transaction.Object);\n\n            // Assert\n            _transaction.Verify(t => t.AddToSet(It.IsAny<string>(), It.IsAny<string>()), Times.Never);\n        }\n\n        [Fact]\n        public void OnStateApplied_DoesNotAddJobToRetriesSet_IfNewStateIsScheduledAndReasonDoesNotMatch()\n        {\n            // Arrange\n            var filter = CreateFilter();\n\n            var newState = new ScheduledState(DateTime.UtcNow) { Reason = \"Some reason.\" };\n            var applyStateContext = CreatApplyStateContext(newState);\n\n            // Act\n            filter.OnStateApplied(applyStateContext, _transaction.Object);\n\n            // Assert\n            _transaction.Verify(t => t.AddToSet(It.IsAny<string>(), It.IsAny<string>()), Times.Never);\n        }\n\n        [Fact]\n        public void OnStateApplied_DoesNotAddJobToRetriesSet_IfNewStateIsEnqueued()\n        {\n            // Arrange\n            var filter = CreateFilter();\n\n            var newState = new EnqueuedState { Reason = \"Retry attempt ...\" };\n            var applyStateContext = CreatApplyStateContext(newState);\n            \n            // Act\n            filter.OnStateApplied(applyStateContext, _transaction.Object);\n\n            // Assert\n            _transaction.Verify(t => t.AddToSet(It.IsAny<string>(), It.IsAny<string>()), Times.Never);\n        }\n\n        private static AutomaticRetryAttribute CreateFilter()\n        {\n            return new AutomaticRetryAttribute { Attempts = 1 };\n        }\n\n        private ApplyStateContext CreatApplyStateContext(IState newState)\n        {\n            var context = new ApplyStateContextMock();\n            context.BackgroundJob.Id = JobId;\n            context.Transaction = _transaction;\n            context.NewStateObject = newState;\n            return context.Object;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/BackgroundJobPerformerFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Moq;\nusing Moq.Sequences;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class BackgroundJobPerformerFacts\n    {\n        private readonly PerformContextMock _context;\n        private readonly Mock<IBackgroundJobPerformer> _innerPerformer;\n        private readonly IList<object> _filters;\n        private readonly Mock<IJobFilterProvider> _filterProvider;\n\n        public BackgroundJobPerformerFacts()\n        {\n            _context = new PerformContextMock();\n            _innerPerformer = new Mock<IBackgroundJobPerformer>();\n\n            _filters = new List<object>();\n            _filterProvider = new Mock<IJobFilterProvider>();\n            _filterProvider.Setup(x => x.GetFilters(It.IsNotNull<Job>())).Returns(\n                _filters.Select(f => new JobFilter(f, JobFilterScope.Type, null)));\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenFilterProvider_IsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobPerformer(null, _innerPerformer.Object));\n\n            Assert.Equal(\"filterProvider\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenInnerPerformer_IsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobPerformer(_filterProvider.Object, (IBackgroundJobPerformer)null));\n\n            Assert.Equal(\"innerPerformer\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Run_ThrowsAnException_WhenContextIsNull()\n        {\n            var performer = CreatePerformer();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => performer.Perform(null));\n\n            Assert.Equal(\"context\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Run_CallsTheRunMethod_OfInnerProcess()\n        {\n            var performer = CreatePerformer();\n\n            performer.Perform(_context.Object);\n\n            _innerPerformer.Verify(x => x.Perform(_context.Object), Times.Once);\n        }\n\n        [Fact]\n        public void Run_StoresJobReturnValueInPerformedContext()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerFilter>();\n            var performer = CreatePerformer();\n\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Returns(\"Returned value\");\n\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert\n            filter.Verify(\n                x => x.OnPerformed(It.Is<PerformedContext>(context => (string)context.Result == \"Returned value\")));\n        }\n\n        [Fact]\n        public void Run_ReturnsValueReturnedByJob()\n        {\n            // Arrange\n            // ReSharper disable once UnusedVariable\n            var filter = CreateFilter<IServerFilter>();\n            var performer = CreatePerformer();\n\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Returns(\"Returned value\");\n\n            // Act\n            var result = performer.Perform(_context.Object);\n\n            // Assert\n            Assert.Equal(\"Returned value\", result);\n        }\n\n        [Fact]\n        public void Run_DoesNotCatchExceptions()\n        {\n            // Arrange\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws<InvalidOperationException>();\n\n            var performer = CreatePerformer();\n\n            // Act & Assert\n            Assert.Throws<InvalidOperationException>(() => performer.Perform(_context.Object));\n        }\n\n        [Fact]\n        public void Run_CallsExceptionFilter_OnException()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerExceptionFilter>();\n\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws<InvalidOperationException>();\n            \n            var performer = CreatePerformer();\n\n            // Act & Assert\n            Assert.Throws<InvalidOperationException>(() => performer.Perform(_context.Object));\n\n            filter.Verify(x => x.OnServerException(It.Is<ServerExceptionContext>(context =>\n                context.Exception is InvalidOperationException)));\n        }\n\n        [Fact, Sequence]\n        public void Run_CallsExceptionFilters_InReverseOrder()\n        {\n            // Arrange\n            var filter1 = CreateFilter<IServerExceptionFilter>();\n            var filter2 = CreateFilter<IServerExceptionFilter>();\n\n            filter2.Setup(x => x.OnServerException(It.IsAny<ServerExceptionContext>())).InSequence();\n            filter1.Setup(x => x.OnServerException(It.IsAny<ServerExceptionContext>())).InSequence();\n\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws<InvalidOperationException>();\n\n            var performer = CreatePerformer();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(() => performer.Perform(_context.Object));\n\n            // Assert - see the `SequenceAttribute` class.\n        }\n\n        [Fact]\n        public void Run_EatsException_WhenItWasHandlerByFilter()\n        {\n            // Arrange\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws<InvalidOperationException>();\n\n            var filter = CreateFilter<IServerExceptionFilter>();\n            filter.Setup(x => x.OnServerException(It.IsAny<ServerExceptionContext>()))\n                .Callback((ServerExceptionContext x) => x.ExceptionHandled = true);\n            \n            var performer = CreatePerformer();\n\n            // Act & Assert does not throw\n            performer.Perform(_context.Object);\n        }\n\n        [Fact, Sequence]\n        public void Run_CallsServerFilters_BeforeAndAfterTheCreationOfAJob()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerFilter>();\n\n            filter.Setup(x => x.OnPerforming(It.IsNotNull<PerformingContext>()))\n                .InSequence();\n\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .InSequence();\n\n            filter.Setup(x => x.OnPerformed(It.IsNotNull<PerformedContext>()))\n                .InSequence();\n\n            var performer = CreatePerformer();\n\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert - see the `SequenceAttribute` class.\n        }\n\n        [Fact, Sequence]\n        public void Run_WrapsFilterCalls_OneIntoAnother()\n        {\n            // Arrange\n            var outerFilter = CreateFilter<IServerFilter>();\n            var innerFilter = CreateFilter<IServerFilter>();\n\n            outerFilter.Setup(x => x.OnPerforming(It.IsAny<PerformingContext>())).InSequence();\n            innerFilter.Setup(x => x.OnPerforming(It.IsAny<PerformingContext>())).InSequence();\n            innerFilter.Setup(x => x.OnPerformed(It.IsAny<PerformedContext>())).InSequence();\n            outerFilter.Setup(x => x.OnPerformed(It.IsAny<PerformedContext>())).InSequence();\n\n            var performer = CreatePerformer();\n\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert - see the `SequenceAttribute` class.\n        }\n\n        [Fact]\n        public void Run_DoesNotCallBoth_Perform_And_OnPerforming_WhenFilterCancelsThis()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerFilter>();\n\n            filter.Setup(x => x.OnPerforming(It.IsAny<PerformingContext>()))\n                .Callback((PerformingContext x) => x.Canceled = true);\n\n            var performer = CreatePerformer();\n\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert\n            _innerPerformer.Verify(x => x.Perform(_context.Object), Times.Never);\n\n            filter.Verify(x => x.OnPerformed(It.IsAny<PerformedContext>()), Times.Never);\n        }\n\n        [Fact]\n        public void Run_TellsOuterFilter_AboutTheCancellationOfCreation()\n        {\n            // Arrange\n            var outerFilter = CreateFilter<IServerFilter>();\n            var innerFilter = CreateFilter<IServerFilter>();\n\n            innerFilter.Setup(x => x.OnPerforming(It.IsAny<PerformingContext>()))\n                .Callback((PerformingContext context) => context.Canceled = true);\n\n            var performer = CreatePerformer();\n\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert\n            outerFilter.Verify(x => x.OnPerformed(It.Is<PerformedContext>(context => context.Canceled)));\n        }\n\n        [Fact]\n        public void Run_DoesNotCall_Perform_And_OnPerformed_WhenExceptionOccured_DuringPerformingPhase()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerFilter>();\n\n            filter.Setup(x => x.OnPerforming(It.IsAny<PerformingContext>()))\n                .Throws<InvalidOperationException>();\n\n            var performer = CreatePerformer();\n\n            // Act\n            var exception = Assert.Throws<JobPerformanceException>(\n                () => performer.Perform(_context.Object));\n\n            // Assert\n            Assert.IsType<InvalidOperationException>(exception.InnerException);\n\n            _innerPerformer.Verify(x => x.Perform(It.IsAny<PerformContext>()), Times.Never);\n\n            filter.Verify(x => x.OnPerformed(It.IsAny<PerformedContext>()), Times.Never);\n        }\n\n        [Fact]\n        public void Run_TellsFiltersAboutException_WhenItIsOccured_DuringThePerformanceOfAJob()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerFilter>();\n\n            var exception = new InvalidOperationException();\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws(exception);\n\n            var performer = CreatePerformer();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(() => performer.Perform(_context.Object));\n\n            // Assert\n            filter.Verify(x => x.OnPerformed(It.Is<PerformedContext>(\n                context => context.Exception == exception)));\n        }\n\n        [Fact]\n        public void Run_TellsOuterFilters_AboutAllExceptions()\n        {\n            // Arrange\n            var outerFilter = CreateFilter<IServerFilter>();\n            // ReSharper disable once UnusedVariable\n            var innerFilter = CreateFilter<IServerFilter>();\n\n            var exception = new InvalidOperationException();\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws(exception);\n\n            var performer = CreatePerformer();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(() => performer.Perform(_context.Object));\n\n            outerFilter.Verify(x => x.OnPerformed(It.Is<PerformedContext>(context => context.Exception == exception)));\n        }\n\n        [Fact]\n        public void Run_DoesNotThrow_HandledExceptions()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerFilter>();\n\n            var exception = new InvalidOperationException();\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws(exception);\n\n            filter.Setup(x => x.OnPerformed(It.Is<PerformedContext>(context => context.Exception == exception)))\n                .Callback((PerformedContext x) => x.ExceptionHandled = true);\n\n            var performer = CreatePerformer();\n\n            // Act & Assert does not throw\n            performer.Perform(_context.Object);\n        }\n\n        [Fact]\n        public void Run_TellsOuterFilter_EvenAboutHandledException()\n        {\n            // Arrange\n            var outerFilter = CreateFilter<IServerFilter>();\n            var innerFilter = CreateFilter<IServerFilter>();\n\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws<InvalidOperationException>();\n\n            innerFilter.Setup(x => x.OnPerformed(It.IsAny<PerformedContext>()))\n                .Callback((PerformedContext x) => x.ExceptionHandled = true);\n\n            var performer = CreatePerformer();\n\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert\n            outerFilter.Verify(x => x.OnPerformed(It.Is<PerformedContext>(context => context.Exception != null)));\n        }\n\n        [Fact]\n        public void Run_WrapsOnPerformedException_IntoJobPerformanceException()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerFilter>();\n            filter.Setup(x => x.OnPerformed(It.IsAny<PerformedContext>()))\n                .Throws<InvalidOperationException>();\n\n            var performer = CreatePerformer();\n\n            // Act & Assert\n            var exception = Assert.Throws<JobPerformanceException>(() => \n                performer.Perform(_context.Object));\n\n            Assert.IsType<InvalidOperationException>(exception.InnerException);\n        }\n\n        [Fact]\n        public void Run_WrapsOnPerformedException_OccuredAfterAnotherException_IntoJobPerformanceException()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerFilter>();\n            filter.Setup(x => x.OnPerformed(It.IsAny<PerformedContext>()))\n                .Throws<InvalidOperationException>();\n\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws<ArgumentNullException>();\n\n            var performer = CreatePerformer();\n\n            // Act & Assert\n            var exception = Assert.Throws<JobPerformanceException>(() =>\n                performer.Perform(_context.Object));\n\n            Assert.IsType<InvalidOperationException>(exception.InnerException);\n        }\n\n        [Fact]\n        public void Run_ExceptionFiltersAreNOTInvoked_OnJobAbortedException()\n        {\n            // Arrange\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws<JobAbortedException>();\n\n            var filter = CreateFilter<IServerExceptionFilter>();\n            var performer = CreatePerformer();\n\n            // Act\n            Assert.Throws<JobAbortedException>(() => performer.Perform(_context.Object));\n\n            // Assert\n            filter.Verify(\n                x => x.OnServerException(It.IsAny<ServerExceptionContext>()),\n                Times.Never);\n        }\n\n        [Fact]\n        public void Run_ExceptionFiltersAreNOTInvoked_OnOperationCanceledException_WhenShutdownTokenIsCanceled()\n        {\n            // Arrange\n            var cts = new CancellationTokenSource();\n            cts.Cancel();\n\n            _context.CancellationToken.Setup(x => x.ShutdownToken).Returns(cts.Token);\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws<OperationCanceledException>();\n\n            var filter = CreateFilter<IServerExceptionFilter>();\n            var performer = CreatePerformer();\n\n            // Act\n            Assert.Throws<OperationCanceledException>(\n                () => performer.Perform(_context.Object));\n\n            // Assert\n            filter.Verify(\n                x => x.OnServerException(It.IsAny<ServerExceptionContext>()),\n                Times.Never);\n        }\n\n        [Fact]\n        public void Run_ExceptionFiltersAreInvoked_OnOperationCanceledException_WhenShutdownTokenIsNOTCanceled()\n        {\n            // Arrange\n            _innerPerformer\n                .Setup(x => x.Perform(_context.Object))\n                .Throws<OperationCanceledException>();\n\n            var filter = CreateFilter<IServerExceptionFilter>();\n            var performer = CreatePerformer();\n\n            // Act\n            Assert.Throws<OperationCanceledException>(\n                () => performer.Perform(_context.Object));\n\n            // Assert\n            filter.Verify(\n                x => x.OnServerException(It.IsAny<ServerExceptionContext>()),\n                Times.Once);\n        }\n\n        [Fact]\n        public void Run_ThrowsOperationCanceledException_OccurredInPreFilterMethods_WhenShutdownTokenIsCanceled()\n        {\n            // Arrange\n            var cts = new CancellationTokenSource();\n            cts.Cancel();\n\n            _context.CancellationToken.Setup(x => x.ShutdownToken).Returns(cts.Token);\n            var filter = CreateFilter<IServerFilter>();\n            filter.Setup(x => x.OnPerforming(It.IsAny<PerformingContext>()))\n                .Throws<OperationCanceledException>();\n\n            var performer = CreatePerformer();\n\n            // Act & Assert\n            Assert.Throws<OperationCanceledException>(\n                () => performer.Perform(_context.Object));\n        }\n\n        [Fact]\n        public void Run_ThrowsJobPerformanceException_InsteadOfOperationCanceled_OccurredInPreFilterMethods_WhenShutdownTokenIsNotCanceled()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerFilter>();\n            filter.Setup(x => x.OnPerforming(It.IsAny<PerformingContext>()))\n                .Throws<OperationCanceledException>();\n\n            var performer = CreatePerformer();\n\n            // Act\n            var exception = Assert.Throws<JobPerformanceException>(\n                () => performer.Perform(_context.Object));\n\n            // Assert\n            Assert.IsType<OperationCanceledException>(exception.InnerException);\n        }\n\n        [Fact]\n        public void Run_ThrowsOperationCanceledException_OccurredInPostFilterMethods_WhenShutdownTokenIsCanceled()\n        {\n            // Arrange\n            var cts = new CancellationTokenSource();\n            cts.Cancel();\n\n            _context.CancellationToken.Setup(x => x.ShutdownToken).Returns(cts.Token);\n            var filter = CreateFilter<IServerFilter>();\n            filter.Setup(x => x.OnPerformed(It.IsAny<PerformedContext>()))\n                .Throws<OperationCanceledException>();\n\n            var performer = CreatePerformer();\n\n            // Act & Assert\n            Assert.Throws<OperationCanceledException>(() => performer.Perform(_context.Object));\n        }\n\n        [Fact]\n        public void Run_ThrowsJobPerformanceException_InsteadOfOperationCanceled_OccurredInPostFilterMethods_WhenShutdownTokenIsNOTCanceled()\n        {\n            // Arrange\n            var filter = CreateFilter<IServerFilter>();\n            filter.Setup(x => x.OnPerformed(It.IsAny<PerformedContext>()))\n                .Throws<OperationCanceledException>();\n\n            var performer = CreatePerformer();\n\n            // Act\n            var exception = Assert.Throws<JobPerformanceException>(\n                () => performer.Perform(_context.Object));\n\n            // Assert\n            Assert.IsType<OperationCanceledException>(exception.InnerException);\n        }\n\n#if !NET452\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Run_FlowsAsyncLocal_ThroughFilters_AndSynchronousBackgroundJobMethod(TaskScheduler scheduler)\n        {\n            // Arrange\n            var id = Guid.NewGuid();\n            _filters.Add(new AsyncLocalFilter(id));\n            _context.BackgroundJob.Job = Job.FromExpression(() => AsyncLocalSync());\n\n            var performer = CreatePerformer(CreateInnerPerformer(scheduler));\n\n            // Act\n            var result = performer.Perform(_context.Object);\n\n            // Assert\n            Assert.Equal(id, result);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Run_FlowsAsyncLocal_ThroughFilters_AndSimpleAsynchronousBackgroundJobMethod(TaskScheduler scheduler)\n        {\n            // Arrange\n            var id = Guid.NewGuid();\n            _filters.Add(new AsyncLocalFilter(id));\n            _context.BackgroundJob.Job = Job.FromExpression(() => AsyncLocalSimpleAsync());\n\n            var performer = CreatePerformer(CreateInnerPerformer(scheduler));\n\n            // Act\n            var result = performer.Perform(_context.Object);\n\n            // Assert\n            Assert.Equal(id, result);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Run_FlowsAsyncLocal_ThroughFilters_AndAsyncAwaitAsynchronousBackgroundJobMethod(TaskScheduler scheduler)\n        {\n            // Arrange\n            var id = Guid.NewGuid();\n            _filters.Add(new AsyncLocalFilter(id));\n            _context.BackgroundJob.Job = Job.FromExpression(() => AsyncLocalAsyncAwait());\n\n            var performer = CreatePerformer(CreateInnerPerformer(scheduler));\n\n            // Act\n            var result = performer.Perform(_context.Object);\n\n            // Assert\n            Assert.Equal(id, result);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Run_FlowsAsyncLocal_ThroughFilters_AndAsyncAwaitContinuationAsynchronousBackgroundJobMethod(TaskScheduler scheduler)\n        {\n            // Arrange\n            var id = Guid.NewGuid();\n            _filters.Add(new AsyncLocalFilter(id));\n            _context.BackgroundJob.Job = Job.FromExpression(() => AsyncLocalAsyncAwaitContinuation());\n\n            var performer = CreatePerformer(CreateInnerPerformer(scheduler));\n\n            // Act\n            var result = performer.Perform(_context.Object);\n\n            // Assert\n            Assert.Equal(id, result);\n        }\n\n        private static CoreBackgroundJobPerformer CreateInnerPerformer(TaskScheduler taskScheduler)\n        {\n            return new CoreBackgroundJobPerformer(new JobActivator(), taskScheduler);\n        }\n\n        public static IEnumerable<object[]> GetSchedulers()\n        {\n            yield return new object[] { null };\n            yield return new object[] { TaskScheduler.Default };\n            yield return new object[] { new Hangfire.Processing.BackgroundTaskScheduler(threadCount: 1) };\n        }\n#endif\n\n        private BackgroundJobPerformer CreatePerformer(IBackgroundJobPerformer inner = null)\n        {\n            return new BackgroundJobPerformer(_filterProvider.Object, inner ?? _innerPerformer.Object);\n        }\n\n        private Mock<T> CreateFilter<T>()\n            where T : class\n        {\n            var filter = new Mock<T>();\n            _filters.Add(filter.Object);\n\n            return filter;\n        }\n\n#if !NET452\n        private static readonly AsyncLocal<Guid> Identifier = new AsyncLocal<Guid>();\n\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        [SuppressMessage(\"ReSharper\", \"UnusedMethodReturnValue.Global\")]\n        public static Guid AsyncLocalSync()\n        {\n            return Identifier.Value;\n        }\n\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static Task<Guid> AsyncLocalSimpleAsync()\n        {\n            return Task.FromResult(Identifier.Value);\n        }\n\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously\n        public static async Task<Guid> AsyncLocalAsyncAwait()\n#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously\n        {\n            return Identifier.Value;\n        }\n\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static async Task<Guid> AsyncLocalAsyncAwaitContinuation()\n        {\n            await Task.Yield();\n            return Identifier.Value;\n        }\n\n        private sealed class AsyncLocalFilter : IServerFilter\n        {\n            private readonly Guid _identifier;\n\n            public AsyncLocalFilter(Guid identifier)\n            {\n                _identifier = identifier;\n            }\n\n            public void OnPerforming(PerformingContext context)\n            {\n                Identifier.Value = _identifier;\n            }\n\n            public void OnPerformed(PerformedContext context)\n            {\n                Assert.Equal(_identifier, Identifier.Value);\n            }\n        }\n#endif\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/BackgroundJobServerOptionsFacts.cs",
    "content": "﻿using System;\nusing System.Threading;\nusing Hangfire.States;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class BackgroundJobServerOptionsFacts\n    {\n        [Fact]\n        public void Ctor_InitializeProperties_WithCorrectValues()\n        {\n            var options = CreateOptions();\n\n            Assert.Equal(Math.Min(Environment.ProcessorCount * 5, 20), options.WorkerCount);\n            Assert.Equal(EnqueuedState.DefaultQueue, options.Queues[0]);\n            Assert.Equal(TimeSpan.FromSeconds(15), options.ShutdownTimeout);\n            Assert.Equal(TimeSpan.FromSeconds(15), options.SchedulePollingInterval);\n            Assert.Equal(TimeSpan.FromMinutes(5), options.ServerTimeout);\n            Assert.Equal(TimeSpan.FromMinutes(5), options.ServerCheckInterval);\n            Assert.Equal(TimeSpan.FromSeconds(30), options.HeartbeatInterval);\n        }\n\n        [Fact]\n        public void WorkerCount_ThrowsAnException_WhenValueIsEqualToZero()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.WorkerCount = 0);\n        }\n\n        [Fact]\n        public void WorkerCount_ThrowsAnException_WhenValueIsNegative()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.WorkerCount = -1);\n        }\n\n        [Fact]\n        public void Queues_ThrowsAnException_WhenValueIsNull()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentNullException>(\n                () => options.Queues = null);\n        }\n\n        [Fact]\n        public void Queues_ThrowsAnException_WhenGivenArrayIsEmpty()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentException>(\n                () => options.Queues = new string[0]);\n        }\n\n        [Fact]\n        public void ServerTimeout_ThrowsAnException_WhenValueIsTooLarge()\n        {\n            var options = CreateOptions();\n        \n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ServerTimeout = TimeSpan.FromHours(25));\n        }\n\n        [Fact]\n        public void ServerTimeout_ThrowsAnException_WhenValueIsNegative()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ServerTimeout = TimeSpan.FromHours(-1));\n        }\n\n        [Fact]\n        public void ServerTimeout_ThrowsAnException_WhenValueIsInfinite()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ServerTimeout = Timeout.InfiniteTimeSpan);\n        }\n\n        [Fact]\n        public void ServerCheckInterval_ThrowsAnException_WhenValueIsTooLarge()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ServerCheckInterval = TimeSpan.FromHours(25));\n        }\n\n        [Fact]\n        public void ServerCheckInterval_ThrowsAnException_WhenValueIsNegative()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ServerCheckInterval = TimeSpan.FromHours(-1));\n        }\n\n        [Fact]\n        public void ServerCheckInterval_ThrowsAnException_WhenValueIsInfinite()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ServerCheckInterval = Timeout.InfiniteTimeSpan);\n        }\n\n        [Fact]\n        public void HeartbeatInterval_ThrowsAnException_WhenValueIsTooLarge()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.HeartbeatInterval = TimeSpan.FromHours(25));\n        }\n\n        [Fact]\n        public void HeartbeatInterval_ThrowsAnException_WhenValueIsNegative()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.HeartbeatInterval = TimeSpan.FromHours(-1));\n        }\n\n        [Fact]\n        public void HeartbeatInterval_ThrowsAnException_WhenValueIsInfinite()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.HeartbeatInterval = Timeout.InfiniteTimeSpan);\n        }\n\n        [Fact]\n        public void ShutdownTimeout_ThrowsAnException_WhenValueIsTooLarge()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ShutdownTimeout = TimeSpan.FromMilliseconds((double)Int32.MaxValue+1));\n        }\n\n        [Fact]\n        public void ShutdownTimeout_ThrowsAnException_WhenValueIsNegative()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.ShutdownTimeout = TimeSpan.FromHours(-1));\n        }\n\n        [Fact]\n        public void ShutdownTimeout_DoesNotThrowAnException_WhenValueIsInfinite()\n        {\n            var options = CreateOptions();\n\n            options.ShutdownTimeout = Timeout.InfiniteTimeSpan;\n\n            Assert.Equal(Timeout.InfiniteTimeSpan, options.ShutdownTimeout);\n        }\n\n        [Fact]\n        public void SchedulePollingInterval_ThrowsAnException_WhenValueIsTooLarge()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.SchedulePollingInterval = TimeSpan.FromMilliseconds((double)Int32.MaxValue + 1));\n        }\n\n        [Fact]\n        public void SchedulePollingInterval_ThrowsAnException_WhenValueIsNegative()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.SchedulePollingInterval = TimeSpan.FromHours(-1));\n        }\n\n        [Fact]\n        public void SchedulePollingInterval_ThrowsAnException_WhenValueIsInfinite()\n        {\n            var options = CreateOptions();\n\n            Assert.Throws<ArgumentOutOfRangeException>(\n                () => options.SchedulePollingInterval = Timeout.InfiniteTimeSpan);\n        }\n\n        private static BackgroundJobServerOptions CreateOptions()\n        {\n            return new BackgroundJobServerOptions();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/BackgroundProcessContextFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Server;\nusing Moq;\nusing Xunit;\n#pragma warning disable 618\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class BackgroundProcessContextFacts\n    {\n        private readonly string _serverId = \"server\";\n        private readonly Mock<JobStorage> _storage;\n        private readonly CancellationTokenSource _cts;\n        private readonly Dictionary<string, object> _properties;\n\n        public BackgroundProcessContextFacts()\n        {\n            _storage = new Mock<JobStorage>();\n            _properties = new Dictionary<string, object> {{\"key\", \"value\"}};\n            _cts = new CancellationTokenSource();\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenServerIdIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundProcessContext(null, _storage.Object, _properties, _cts.Token));\n\n            Assert.Equal(\"serverId\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundProcessContext(_serverId, null, _properties, _cts.Token));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenPropertiesArgumentIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundProcessContext(_serverId, _storage.Object, null, _cts.Token));\n\n            Assert.Equal(\"properties\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_CorrectlyInitializes_AllTheProperties()\n        {\n            var context = new BackgroundProcessContext(_serverId, _storage.Object, _properties, _cts.Token);\n\n            Assert.Equal(_serverId, context.ServerId);\n            Assert.True(_properties.SequenceEqual(context.Properties));\n            Assert.Same(_storage.Object, context.Storage);\n            Assert.Equal(_cts.Token, context.CancellationToken);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/BackgroundProcessingServerFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class BackgroundProcessingServerFacts\n    {\n        private readonly string[] _queues = { \"queue\" };\n        private readonly Mock<JobStorage> _storage;\n        private readonly List<IBackgroundProcess> _processes;\n        private readonly Dictionary<string, object> _properties;\n        private readonly Mock<IStorageConnection> _connection;\n\n        public BackgroundProcessingServerFacts()\n        {\n            _storage = new Mock<JobStorage>();\n            _processes = new List<IBackgroundProcess>();\n            _properties = new Dictionary<string, object> { { \"Queues\", _queues } };\n\n            _connection = new Mock<IStorageConnection>();\n            _storage.Setup(x => x.GetConnection()).Returns(_connection.Object);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundProcessingServer(null, _processes, _properties));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenProcessesArgumentIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundProcessingServer(_storage.Object, null, _properties));\n\n            Assert.Equal(\"processes\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenPropertiesArgumentIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundProcessingServer(_storage.Object, _processes, null));\n            \n            Assert.Equal(\"properties\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_AnnouncesTheServer_AndRemovesIt()\n        {\n            using (CreateServer()) { Thread.Sleep(50); }\n\n            _connection.Verify(x => x.AnnounceServer(\n                It.IsNotNull<string>(),\n                It.Is<ServerContext>(y => y.Queues == _queues)));\n\n            _connection.Verify(x => x.RemoveServer(It.IsNotNull<string>()));\n        }\n\n        [Fact]\n        public void Execute_StartsAllTheProcesses_InLoop_AndWaitsForThem()\n        {\n            // Arrange\n            var component1Countdown = new CountdownEvent(5);\n            var component2Countdown = new CountdownEvent(5);\n\n            var component1 = CreateProcessMock<IBackgroundProcess>();\n            component1.Setup(x => x.Execute(It.IsAny<BackgroundProcessContext>())).Callback(() =>\n            {\n                component1Countdown.Signal();\n            });\n\n            var component2 = CreateProcessMock<IBackgroundProcess>();\n            component2.Setup(x => x.Execute(It.IsAny<BackgroundProcessContext>())).Callback(() =>\n            {\n                component2Countdown.Signal();\n            });\n\n            // Act\n            using (CreateServer())\n            {\n                WaitHandle.WaitAll(new[] { component1Countdown.WaitHandle, component2Countdown.WaitHandle });\n            }\n\n            // Assert\n            component1.Verify(x => x.Execute(It.IsAny<BackgroundProcessContext>()), Times.AtLeast(5));\n            component2.Verify(x => x.Execute(It.IsNotNull<BackgroundProcessContext>()), Times.AtLeast(5));\n        }\n\n        private BackgroundProcessingServer CreateServer()\n        {\n            return new BackgroundProcessingServer(_storage.Object, _processes, _properties);\n        }\n\n        private Mock<T> CreateProcessMock<T>()\n            where T : class, IBackgroundProcess\n        {\n            var mock = new Mock<T>();\n            _processes.Add(mock.Object);\n\n            return mock;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/CoreBackgroundJobPerformerFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Hangfire.Common;\nusing Hangfire.Core.Tests.Common;\nusing Hangfire.Server;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class CoreBackgroundJobPerformerFacts : IDisposable\n    {\n        private readonly Mock<JobActivator> _activator = new Mock<JobActivator>() { CallBase = true };\n        private readonly PerformContextMock _context = new PerformContextMock();\n\n        private static readonly DateTime SomeDateTime = new DateTime(2014, 5, 30, 12, 0, 0);\n        private static bool _methodInvoked;\n        private static bool _disposed;\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenActivatorIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                // ReSharper disable once AssignNullToNotNullAttribute\n                () => new CoreBackgroundJobPerformer(null, null));\n\n            Assert.Equal(\"activator\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_DoesNotThrowAnException_WhenTaskSchedulerIsNull()\n        {\n            var performer = new CoreBackgroundJobPerformer(_activator.Object, null);\n            Assert.NotNull(performer);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_CanInvokeStaticMethods()\n        {\n            _methodInvoked = false;\n            _context.BackgroundJob.Job = Job.FromExpression(() => StaticMethod());\n            var performer = CreatePerformer();\n\n            performer.Perform(_context.Object);\n\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_CanInvokeInstanceMethods()\n        {\n            _methodInvoked = false;\n            _context.BackgroundJob.Job = Job.FromExpression<CoreBackgroundJobPerformerFacts>(x => x.InstanceMethod());\n            var performer = CreatePerformer();\n\n            performer.Perform(_context.Object);\n\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_ActivatesJob_WithinAScope()\n        {\n            var performer = CreatePerformer();\n            _context.BackgroundJob.Job = Job.FromExpression<CoreBackgroundJobPerformerFacts>(x => x.InstanceMethod());\n\n            performer.Perform(_context.Object);\n\n            _activator.Verify(x => x.BeginScope(It.IsNotNull<JobActivatorContext>()), Times.Once);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_DisposesDisposableInstance_AfterPerformance()\n        {\n            _disposed = false;\n            _context.BackgroundJob.Job = Job.FromExpression<Disposable>(x => x.Method());\n            var performer = CreatePerformer();\n\n            performer.Perform(_context.Object);\n\n            Assert.True(_disposed);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_PassesArguments_ToACallingMethod()\n        {\n            // Arrange\n            _methodInvoked = false;\n            _context.BackgroundJob.Job = Job.FromExpression(() => MethodWithArguments(\"hello\", 5));\n            var performer = CreatePerformer();\n\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert - see the `MethodWithArguments` method.\n            Assert.True(_methodInvoked);\n        }\n\n#if !NETCOREAPP1_0\n        [Fact, StaticLock]\n        public void Perform_PassesCorrectDateTime_IfItWasSerialized_UsingTypeConverter()\n        {\n            // Arrange\n            _methodInvoked = false;\n            var typeConverter = TypeDescriptor.GetConverter(typeof(DateTime));\n            var convertedDate = typeConverter.ConvertToInvariantString(SomeDateTime);\n\n            var type = typeof(CoreBackgroundJobPerformerFacts);\n            var method = type.GetMethod(\"MethodWithDateTimeArgument\");\n\n#pragma warning disable CS0618 // Type or member is obsolete\n            _context.BackgroundJob.Job = new Job(type, method, new [] { convertedDate });\n#pragma warning restore CS0618 // Type or member is obsolete\n            var performer = CreatePerformer();\n\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert - see also the `MethodWithDateTimeArgument` method.\n            Assert.True(_methodInvoked);\n        }\n#endif\n\n        [Fact, StaticLock]\n        public void Perform_PassesCorrectDateTime_IfItWasSerialized_UsingOldFormat()\n        {\n            // Arrange\n            _methodInvoked = false;\n            var convertedDate = SomeDateTime.ToString(\"MM/dd/yyyy HH:mm:ss.ffff\");\n\n            var type = typeof(CoreBackgroundJobPerformerFacts);\n            var method = type.GetMethod(\"MethodWithDateTimeArgument\");\n\n#pragma warning disable CS0618 // Type or member is obsolete\n            _context.BackgroundJob.Job = new Job(type, method, new [] { convertedDate });\n#pragma warning restore CS0618 // Type or member is obsolete\n            var performer = CreatePerformer();\n\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert - see also the `MethodWithDateTimeArgument` method.\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_PassesCorrectDateTimeArguments()\n        {\n            // Arrange\n            _methodInvoked = false;\n            _context.BackgroundJob.Job = Job.FromExpression(() => MethodWithDateTimeArgument(SomeDateTime));\n            var performer = CreatePerformer();\n\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert - see also the `MethodWithDateTimeArgument` method.\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_WorksCorrectly_WithNullValues()\n        {\n            // Arrange\n            _methodInvoked = false;\n            _context.BackgroundJob.Job = Job.FromExpression(() => NullArgumentMethod(null));\n\n            var performer = CreatePerformer();\n            // Act\n            performer.Perform(_context.Object);\n\n            // Assert - see also `NullArgumentMethod` method.\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact]\n        public void Perform_ThrowsException_WhenActivatorThrowsAnException()\n        {\n            // Arrange\n            var exception = new InvalidOperationException();\n            _activator.Setup(x => x.ActivateJob(It.IsAny<Type>())).Throws(exception);\n\n            _context.BackgroundJob.Job = Job.FromExpression(() => InstanceMethod());\n            var performer = CreatePerformer();\n\n            // Act\n            Assert.Throws<InvalidOperationException>(\n                () => performer.Perform(_context.Object));\n        }\n\n        [Fact]\n        public void Perform_ThrowsPerformanceException_WhenActivatorReturnsNull()\n        {\n            _activator.Setup(x => x.ActivateJob(It.IsNotNull<Type>())).Returns(null);\n            _context.BackgroundJob.Job = Job.FromExpression(() => InstanceMethod());\n            var performer = CreatePerformer();\n\n            Assert.Throws<InvalidOperationException>(\n                () => performer.Perform(_context.Object));\n        }\n\n        [Fact]\n        public void Perform_ThrowsPerformanceException_OnArgumentsDeserializationFailure()\n        {\n            var type = typeof(JobFacts);\n            var method = type.GetMethod(\"MethodWithDateTimeArgument\");\n            _context.BackgroundJob.Job = new Job(type, method, \"sdfa\");\n            var performer = CreatePerformer();\n\n            var exception = Assert.Throws<JobPerformanceException>(\n                () => performer.Perform(_context.Object));\n\n            Assert.NotNull(exception.InnerException);\n        }\n\n        [Fact, StaticLock]\n        public void Perform_ThrowsPerformanceException_OnDisposalFailure()\n        {\n            _methodInvoked = false;\n            _context.BackgroundJob.Job = Job.FromExpression<BrokenDispose>(x => x.Method());\n            var performer = CreatePerformer();\n            \n            Assert.Throws<InvalidOperationException>(\n                () => performer.Perform(_context.Object));\n\n            Assert.True(_methodInvoked);\n        }\n\n        [Fact]\n        public void Perform_ThrowsPerformanceException_WithUnwrappedInnerException()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => ExceptionMethod());\n            var performer = CreatePerformer();\n\n            var thrownException = Assert.Throws<JobPerformanceException>(\n                () => performer.Perform(_context.Object));\n\n            Assert.IsType<InvalidOperationException>(thrownException.InnerException);\n            Assert.Equal(\"exception\", thrownException.InnerException.Message);\n        }\n\n        [Fact]\n        public void Run_ThrowsPerformanceException_WithUnwrappedInnerException_ForTasks()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => TaskExceptionMethod());\n            var performer = CreatePerformer();\n\n            var thrownException = Assert.Throws<JobPerformanceException>(\n                () => performer.Perform(_context.Object));\n\n            Assert.IsType<InvalidOperationException>(thrownException.InnerException);\n            Assert.Equal(\"exception\", thrownException.InnerException.Message);\n        }\n\n        [Fact]\n        public void Perform_ThrowsPerformanceException_WhenMethodThrownTaskCanceledException()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => TaskCanceledExceptionMethod());\n            var performer = CreatePerformer();\n\n            var thrownException = Assert.Throws<JobPerformanceException>(\n                () => performer.Perform(_context.Object));\n\n            Assert.IsType<TaskCanceledException>(thrownException.InnerException);\n        }\n\n        [Fact]\n        public void Perform_RethrowsOperationCanceledException_WhenShutdownTokenIsCanceled()\n        {\n            // Arrange\n            _context.BackgroundJob.Job = Job.FromExpression(() => CancelableJob(JobCancellationToken.Null));\n            _context.CancellationToken.Setup(x => x.ShutdownToken).Returns(new CancellationToken(true));\n            _context.CancellationToken.Setup(x => x.ThrowIfCancellationRequested()).Throws<OperationCanceledException>();\n\n            var performer = CreatePerformer();\n\n            // Act & Assert\n            Assert.Throws<OperationCanceledException>(() => performer.Perform(_context.Object));\n        }\n\n        [Fact]\n        public void Run_RethrowsTaskCanceledException_WhenShutdownTokenIsCanceled()\n        {\n            // Arrange\n            _context.BackgroundJob.Job = Job.FromExpression(() => CancelableJob(JobCancellationToken.Null));\n            _context.CancellationToken.Setup(x => x.ShutdownToken).Returns(new CancellationToken(true));\n            _context.CancellationToken.Setup(x => x.ThrowIfCancellationRequested()).Throws<TaskCanceledException>();\n\n            var performer = CreatePerformer();\n\n            // Act & Assert\n            Assert.Throws<TaskCanceledException>(() => performer.Perform(_context.Object));\n        }\n        \n        [Fact]\n        public void Run_RethrowsJobAbortedException()\n        {\n            // Arrange\n            _context.BackgroundJob.Job = Job.FromExpression(() => CancelableJob(JobCancellationToken.Null));\n            _context.CancellationToken.Setup(x => x.ShutdownToken).Returns(new CancellationToken(true));\n            _context.CancellationToken.Setup(x => x.ThrowIfCancellationRequested()).Throws<JobAbortedException>();\n\n            var performer = CreatePerformer();\n\n            // Act & Assert\n            Assert.Throws<JobAbortedException>(() => performer.Perform(_context.Object));\n        }\n\n        [Fact]\n        public void ThrowsJobPerformanceException_DoesInclude_JobId()\n        {\n\t        // Arrange\n\t        _context.BackgroundJob.Job = Job.FromExpression(() => CancelableJob(JobCancellationToken.Null));\n\t        _context.CancellationToken.Setup(x => x.ShutdownToken).Returns(CancellationToken.None);\n\t        _context.CancellationToken.Setup(x => x.ThrowIfCancellationRequested()).Throws<OperationCanceledException>();\n\n\t        var performer = CreatePerformer();\n\n\t        // Act & Assert\n\t        var exception = Assert.Throws<JobPerformanceException>(() => performer.Perform(_context.Object));\n\t        Assert.Equal(_context.BackgroundJob.Id, exception.JobId);\n        }\n\n        [Fact]\n        public void Run_ThrowsJobPerformanceException_InsteadOfOperationCanceled_WhenShutdownWasNOTInitiated()\n        {\n            // Arrange\n            _context.BackgroundJob.Job = Job.FromExpression(() => CancelableJob(JobCancellationToken.Null));\n            _context.CancellationToken.Setup(x => x.ShutdownToken).Returns(CancellationToken.None);\n            _context.CancellationToken.Setup(x => x.ThrowIfCancellationRequested()).Throws<OperationCanceledException>();\n\n            var performer = CreatePerformer();\n\n            // Act & Assert\n            Assert.Throws<JobPerformanceException>(() => performer.Perform(_context.Object));\n        }\n\n        [Fact]\n        public void Run_PassesStandardCancellationToken_IfThereIsCancellationTokenParameter()\n        {\n            // Arrange\n            _context.BackgroundJob.Job = Job.FromExpression(() => CancelableJob(default(CancellationToken)));\n            var source = new CancellationTokenSource();\n            _context.CancellationToken.Setup(x => x.ShutdownToken).Returns(source.Token);\n            var performer = CreatePerformer();\n\n            // Act & Assert\n            source.Cancel();\n            Assert.Throws<OperationCanceledException>(\n                () => performer.Perform(_context.Object));\n        }\n\n        [Fact]\n        public void Perform_ReturnsValue_WhenCallingFunctionReturningValue()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression<JobFacts.Instance>(x => x.FunctionReturningValue());\n            var performer = CreatePerformer();\n\n            var result = performer.Perform(_context.Object);\n\n            Assert.Equal(\"Return value\", result);\n        }\n\n        [Fact]\n        public void Run_DoesNotReturnValue_WhenCallingFunctionReturningPlainTask()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression<JobFacts.Instance>(x => x.FunctionReturningTask());\n            var performer = CreatePerformer();\n\n            var result = performer.Perform(_context.Object);\n\n            Assert.Null(result);\n        }\n\n        [Fact]\n        public void Run_DoesNotReturnValue_WhenCallingFunctionReturningValueTask()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression<JobFacts.Instance>(x => x.FunctionReturningValueTask());\n            var performer = CreatePerformer();\n\n            var result = performer.Perform(_context.Object);\n\n            Assert.Null(result);\n        }\n\n        [Theory]\n        [InlineData(true)]\n        [InlineData(false)]\n        public void Run_ReturnsTaskResult_WhenCallingFunctionReturningGenericTask(bool continueOnCapturedContext)\n        {\n            _context.BackgroundJob.Job = Job.FromExpression<JobFacts.Instance>(x => x.FunctionReturningTaskResultingInString(continueOnCapturedContext));\n            var performer = CreatePerformer();\n\n            var result = performer.Perform(_context.Object);\n\n            Assert.Equal(\"Return value\", result);\n        }\n\n        [Theory]\n        [InlineData(true)]\n        [InlineData(false)]\n        public void Run_ReturnsTaskResult_WhenCallingFunctionReturningValueTask(bool continueOnCapturedContext)\n        {\n            _context.BackgroundJob.Job = Job.FromExpression<JobFacts.Instance>(x => x.FunctionReturningValueTaskResultingInString(continueOnCapturedContext));\n            var performer = CreatePerformer();\n\n            var result = performer.Perform(_context.Object);\n\n            Assert.Equal(\"Return value\", result);\n        }\n\n        [Fact]\n        public void Perform_ExecutesAsyncMethod_AlwaysWithinTheSameThread()\n        {\n            SynchronizationContext.SetSynchronizationContext(null);\n            _context.BackgroundJob.Job = Job.FromExpression(() => AsyncMethod(Thread.CurrentThread.ManagedThreadId));\n            var performer = CreatePerformer();\n\n            var result = performer.Perform(_context.Object);\n\n            Assert.True((bool)result);\n        }\n\n        [Fact]\n        public void Perform_ExecutesAsyncMethod_OnCustomScheduler_WhenItIsSet()\n        {\n            SynchronizationContext.SetSynchronizationContext(null);\n            var scheduler = new MyCustomTaskScheduler();\n\n            _context.BackgroundJob.Job = Job.FromExpression(() => TaskExceptionMethod());\n            var performer = CreatePerformer(scheduler);\n\n            Assert.Throws<JobPerformanceException>(() => performer.Perform(_context.Object));\n            \n            Assert.True(scheduler.TasksPerformed > 1);\n        }\n\n#if !NET452\n        private static readonly AsyncLocal<string> AsyncLocal = new AsyncLocal<string>();\n        \n        [Fact]\n        public void Perform_FlowsExistingAsyncLocal_WhenInvokingSynchronousMethod()\n        {\n            InvokeJobMethodWithFlowOfAsyncLocal(Job.FromExpression(() => CheckSynchronousAsyncLocal()));\n        }\n\n        [SuppressMessage(\"ReSharper\", \"UnusedMember.Global\")]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void CheckSynchronousAsyncLocal()\n        {\n            Assert.Equal(\"world\", AsyncLocal.Value);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Perform_FlowsExistingAsyncLocal_WhenInvokingSimpleAsyncMethod(TaskScheduler scheduler)\n        {\n            InvokeJobMethodWithFlowOfAsyncLocal(Job.FromExpression(() => CheckSimpleAsyncLocal()), scheduler);\n        }\n\n        [SuppressMessage(\"ReSharper\", \"UnusedMember.Global\")]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static Task CheckSimpleAsyncLocal()\n        {\n            Assert.Equal(\"world\", AsyncLocal.Value);\n            return Task.CompletedTask;\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Perform_FlowsExistingAsyncLocal_WhenInvokingAsyncMethodWithAwait(TaskScheduler scheduler)\n        {\n            InvokeJobMethodWithFlowOfAsyncLocal(Job.FromExpression(() => CheckAsyncAwaitAsyncLocal()), scheduler);\n        }\n\n        [SuppressMessage(\"ReSharper\", \"UnusedMember.Global\")]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously\n        public static async Task CheckAsyncAwaitAsyncLocal()\n#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously\n        {\n            Assert.Equal(\"world\", AsyncLocal.Value);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Perform_FlowsExistingAsyncLocal_WhenInvokingAsyncMethodAndSettingAsyncLocalAfterAwait(TaskScheduler scheduler)\n        {\n            InvokeJobMethodWithFlowOfAsyncLocal(Job.FromExpression(() => CheckAsyncLocalAfterAwait()), scheduler);\n        }\n\n        [SuppressMessage(\"ReSharper\", \"UnusedMember.Global\")]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static async Task CheckAsyncLocalAfterAwait()\n        {\n            await Task.Yield();\n\n            Assert.Equal(\"world\", AsyncLocal.Value);\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Perform_FlowsExistingAsyncLocal_WhenInvokingAsyncMethodAndSettingAsyncLocalInTaskContinuation(TaskScheduler scheduler)\n        {\n            InvokeJobMethodWithFlowOfAsyncLocal(Job.FromExpression(() => CheckAsyncLocalInExplicitTaskContinuation()), scheduler);\n        }\n\n        [SuppressMessage(\"ReSharper\", \"UnusedMember.Global\")]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static Task CheckAsyncLocalInExplicitTaskContinuation()\n        {\n            return Task.FromResult(true).ContinueWith(_ =>\n            {\n                Assert.Equal(\"world\", AsyncLocal.Value);\n            });\n        }\n\n        private void InvokeJobMethodWithFlowOfAsyncLocal(Job job, TaskScheduler scheduler = null)\n        {\n            AsyncLocal.Value = \"world\";\n\n            var parallelism = Math.Max(10, Environment.ProcessorCount);\n            Parallel.For(0, parallelism, new ParallelOptions { MaxDegreeOfParallelism = parallelism }, _ =>\n            {\n                _context.BackgroundJob.Job = job;\n                var performer = CreatePerformer(scheduler);\n\n                performer.Perform(_context.Object);\n\n                Assert.Equal(\"world\", AsyncLocal.Value);\n            });\n\n            Assert.Equal(\"world\", AsyncLocal.Value);\n        }\n\n        [Fact]\n        public void Perform_DoesNotLeakAsyncLocal_WhenInvokingSynchronousMethod()\n        {\n            InvokeJobMethodAndAssertAsyncLocalIsNull(Job.FromExpression(() => SynchronousAsyncLocal()));\n        }\n\n        [SuppressMessage(\"ReSharper\", \"UnusedMember.Global\")]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void SynchronousAsyncLocal()\n        {\n            Assert.Null(AsyncLocal.Value);\n            AsyncLocal.Value = \"hello\";\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Perform_DoesNotLeakAsyncLocal_WhenInvokingSimpleAsyncMethod(TaskScheduler scheduler)\n        {\n            InvokeJobMethodAndAssertAsyncLocalIsNull(Job.FromExpression(() => SimpleAsyncLocal()), scheduler);\n        }\n\n        [SuppressMessage(\"ReSharper\", \"UnusedMember.Global\")]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static Task SimpleAsyncLocal()\n        {\n            Assert.Null(AsyncLocal.Value);\n            AsyncLocal.Value = \"hello\";\n            return Task.CompletedTask;\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Perform_DoesNotLeakAsyncLocal_WhenInvokingAsyncMethodWithAwait(TaskScheduler scheduler)\n        {\n            InvokeJobMethodAndAssertAsyncLocalIsNull(Job.FromExpression(() => AsyncAwaitAsyncLocal()), scheduler);\n        }\n\n        [SuppressMessage(\"ReSharper\", \"UnusedMember.Global\")]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously\n        public static async Task AsyncAwaitAsyncLocal()\n#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously\n        {\n            Assert.Null(AsyncLocal.Value);\n            AsyncLocal.Value = \"hello\";\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Perform_DoesNotLeakAsyncLocal_WhenInvokingAsyncMethodAndSettingAsyncLocalAfterAwait(TaskScheduler scheduler)\n        {\n            InvokeJobMethodAndAssertAsyncLocalIsNull(Job.FromExpression(() => AsyncLocalSetAfterAwait()), scheduler);\n        }\n\n        [SuppressMessage(\"ReSharper\", \"UnusedMember.Global\")]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static async Task AsyncLocalSetAfterAwait()\n        {\n            await Task.Yield();\n\n            Assert.Null(AsyncLocal.Value);\n            AsyncLocal.Value = \"hello\";\n        }\n\n        [Theory]\n        [MemberData(nameof(GetSchedulers))]\n        public void Perform_DoesNotLeakAsyncLocal_WhenInvokingAsyncMethodAndSettingAsyncLocalInTaskContinuation(TaskScheduler scheduler)\n        {\n            InvokeJobMethodAndAssertAsyncLocalIsNull(Job.FromExpression(() => AsyncLocalSetInExplicitTaskContinuation()), scheduler);\n        }\n\n        [SuppressMessage(\"ReSharper\", \"UnusedMember.Global\")]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static Task AsyncLocalSetInExplicitTaskContinuation()\n        {\n            return Task.FromResult(true).ContinueWith(_ =>\n            {\n                Assert.Null(AsyncLocal.Value);\n                AsyncLocal.Value = \"hello\";\n            });\n        }\n\n        private void InvokeJobMethodAndAssertAsyncLocalIsNull(Job job, TaskScheduler scheduler = null)\n        {\n            var parallelism = Math.Max(10, Environment.ProcessorCount);\n            Parallel.For(0, parallelism, new ParallelOptions { MaxDegreeOfParallelism = parallelism }, _ =>\n            {\n                _context.BackgroundJob.Job = job;\n                var performer = CreatePerformer(scheduler);\n\n                performer.Perform(_context.Object);\n\n                Assert.Null(AsyncLocal.Value);\n            });\n\n            Assert.Null(AsyncLocal.Value);\n        }\n\n        public static IEnumerable<object[]> GetSchedulers()\n        {\n            yield return new object[] { null };\n            yield return new object[] { TaskScheduler.Default };\n            yield return new object[] { new Hangfire.Processing.BackgroundTaskScheduler(threadCount: 1) };\n        }\n#endif\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        public void InstanceMethod()\n        {\n            _methodInvoked = true;\n        }\n\n        [UsedImplicitly]\n        public class Disposable : IDisposable\n        {\n            [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n            [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n            public void Method()\n            {\n                _methodInvoked = true;\n            }\n\n            public void Dispose()\n            {\n                _disposed = true;\n                GC.SuppressFinalize(this);\n            }\n        }\n\n        [UsedImplicitly]\n        public class BrokenDispose : IDisposable\n        {\n            [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n            [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n            public void Method()\n            {\n                _methodInvoked = true;\n            }\n\n            public void Dispose()\n            {\n                GC.SuppressFinalize(this);\n                throw new InvalidOperationException();\n            }\n        }\n\n        public void Dispose()\n        {\n            _disposed = true;\n            GC.SuppressFinalize(this);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void NullArgumentMethod(string[] argument)\n        {\n            _methodInvoked = true;\n            Assert.Null(argument);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void CancelableJob(IJobCancellationToken token)\n        {\n            token.ThrowIfCancellationRequested();\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void CancelableJob(CancellationToken token)\n        {\n            token.ThrowIfCancellationRequested();\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public void MethodWithDateTimeArgument(DateTime argument)\n        {\n            _methodInvoked = true;\n\n            Assert.Equal(SomeDateTime, argument);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void StaticMethod()\n        {\n            _methodInvoked = true;\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public void MethodWithArguments(string stringArg, int intArg)\n        {\n            _methodInvoked = true;\n\n            Assert.Equal(\"hello\", stringArg);\n            Assert.Equal(5, intArg);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void ExceptionMethod()\n        {\n            throw new InvalidOperationException(\"exception\");\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static async Task TaskExceptionMethod()\n        {\n            await Task.Yield();\n\n            throw new InvalidOperationException(\"exception\");\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void TaskCanceledExceptionMethod()\n        {\n            throw new TaskCanceledException();\n        }\n\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static async Task<bool> AsyncMethod(int threadId)\n        {\n            if (threadId != Thread.CurrentThread.ManagedThreadId)\n            {\n                throw new InvalidOperationException(\"Start\");\n            }\n\n            await Task.Yield();\n\n            if (threadId != Thread.CurrentThread.ManagedThreadId)\n            {\n                throw new InvalidOperationException(\"After Yield\");\n            }\n\n            await Task.Delay(1).ConfigureAwait(true);\n\n            if (threadId != Thread.CurrentThread.ManagedThreadId)\n            {\n                throw new InvalidOperationException(\"After Delay\");\n            }\n\n            Parallel.For(0, 4, new ParallelOptions { MaxDegreeOfParallelism = 4, TaskScheduler = TaskScheduler.Current },\n                i =>\n                {\n                    Thread.Sleep(TimeSpan.FromSeconds(1));\n                });\n\n            if (threadId != Thread.CurrentThread.ManagedThreadId)\n            {\n                throw new InvalidOperationException(\"After Parallel.For\");\n            }\n\n            await Task.Delay(1).ConfigureAwait(false);\n\n#if NETCOREAPP1_0\n            if (threadId == Thread.CurrentThread.ManagedThreadId)\n#else\n            if (!Thread.CurrentThread.IsThreadPoolThread)\n#endif\n            {\n                throw new InvalidOperationException(\"Not running on ThreadPool after ConfigureAwait(false)\");\n            }\n\n            return true;\n        }\n\n        private CoreBackgroundJobPerformer CreatePerformer(TaskScheduler taskScheduler = null)\n        {\n            return new CoreBackgroundJobPerformer(_activator.Object, taskScheduler);\n        }\n\n        private class MyCustomTaskScheduler : TaskScheduler\n        {\n            public int TasksPerformed { get; private set; }\n\n            protected override void QueueTask(Task task)\n            {\n                TryExecuteTask(task);\n                TasksPerformed++;\n            }\n\n            protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)\n            {\n                return false;\n            }\n\n            protected override IEnumerable<Task> GetScheduledTasks()\n            {\n                return Enumerable.Empty<Task>();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/DelayedJobSchedulerFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Server;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class DelayedJobSchedulerFacts\n    {\n        private const string JobId = \"id\";\n        private readonly Mock<JobStorageConnection> _connection;\n        private readonly Mock<IBackgroundJobStateChanger> _stateChanger;\n        private readonly BackgroundProcessContextMock _context;\n        private readonly Mock<IWriteOnlyTransaction> _transaction;\n        private readonly Mock<IDisposable> _distributedLock;\n        private readonly List<string> _schedule = new List<string>();\n\n        public DelayedJobSchedulerFacts()\n        {\n            _context = new BackgroundProcessContextMock();\n\n            _connection = new Mock<JobStorageConnection>();\n            _context.Storage.Setup(x => x.GetConnection()).Returns(_connection.Object);\n\n            _stateChanger = new Mock<IBackgroundJobStateChanger>();\n            _transaction = new Mock<IWriteOnlyTransaction>();\n            _connection.Setup(x => x.CreateWriteTransaction()).Returns(_transaction.Object);\n\n            _distributedLock = new Mock<IDisposable>();\n            _connection\n                .Setup(x => x.AcquireDistributedLock(\"locks:schedulepoller\", It.IsAny<TimeSpan>()))\n                .Returns(_distributedLock.Object);\n\n            _connection\n                .Setup(x => x.GetFirstByLowestScoreFromSet(\"schedule\", 0, It.Is<double>(time => time > 0)))\n                .Returns(_schedule.FirstOrDefault);\n            \n            _connection\n                .Setup(x => x.GetFirstByLowestScoreFromSet(\"schedule\", 0, It.Is<double>(time => time > 0), It.IsAny<int>()))\n                .Returns(_schedule.ToList);\n\n            _stateChanger\n                .Setup(x => x.ChangeState(It.IsNotNull<StateChangeContext>()))\n                .Callback<StateChangeContext>(ctx =>\n                {\n                    if (!(ctx.NewState is ScheduledState)) _schedule.Remove(ctx.BackgroundJobId);\n                });\n\n            _transaction\n                .Setup(x => x.RemoveFromSet(\"schedule\", It.IsNotNull<string>()))\n                .Callback<string, string>((key, value) => _schedule.Remove(value));\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStateChangerIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new DelayedJobScheduler(Timeout.InfiniteTimeSpan, null));\n\n            Assert.Equal(\"stateChanger\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Execute_MovesJobStateToEnqueued()\n        {\n            var scheduler = CreateScheduler();\n            _schedule.Add(JobId);\n            \n            scheduler.Execute(_context.Object);\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.BackgroundJobId == JobId &&\n                ctx.NewState is EnqueuedState &&\n                ctx.ExpectedStates.SequenceEqual(new[] { ScheduledState.StateName }) &&\n                ctx.DisableFilters == false)));\n\n            _connection.Verify(x => x.Dispose());\n        }\n\n        [Fact]\n        public void Execute_EnqueuesJobIdDirectly_AndRemovesItFromSchedule_WhenTargetQueueIsEncodedIntoTheSetEntry()\n        {\n            // Arrange\n            _schedule.Add(\"default:some-id\");\n            _schedule.Add(\"critical:another-id\");\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.AddToQueue(\"default\", \"some-id\"));\n            _transaction.Verify(x => x.RemoveFromSet(\"schedule\", \"default:some-id\"));\n            _transaction.Verify(x => x.AddToQueue(\"critical\", \"another-id\"));\n            _transaction.Verify(x => x.RemoveFromSet(\"schedule\", \"critical:another-id\"));\n\n            _transaction.Verify(x => x.Commit(), Times.Exactly(2));\n\n            _stateChanger.Verify(x => x.ChangeState(It.IsAny<StateChangeContext>()), Times.Never);\n        }\n\n        [Fact]\n        public void Execute_MovesJobStateToEnqueued_UsingBatching_WhenAvailable()\n        {\n            // Arrange\n            EnableBatching();\n\n            _schedule.Add(\"job-1\");\n            _schedule.Add(\"job-2\");\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.BackgroundJobId == \"job-1\" &&\n                ctx.NewState is EnqueuedState)));\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.BackgroundJobId == \"job-2\" &&\n                ctx.NewState is EnqueuedState)));\n        }\n\n        [Fact]\n        public void Execute_WithBatching_EnqueuesJobIdDirectly_AndRemovesItFromSchedule_WhenTargetQueueIsEncodedIntoTheSetEntry()\n        {\n            // Arrange\n            EnableBatching();\n\n            _schedule.Add(\"default:some-id\");\n            _schedule.Add(\"critical:another-id\");\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.AddToQueue(\"default\", \"some-id\"));\n            _transaction.Verify(x => x.AddToQueue(\"critical\", \"another-id\"));\n            _transaction.Verify(x => x.RemoveFromSet(\"schedule\", \"default:some-id\"));\n            _transaction.Verify(x => x.RemoveFromSet(\"schedule\", \"critical:another-id\"));\n\n            _transaction.Verify(x => x.Commit(), Times.Once);\n\n            _stateChanger.Verify(x => x.ChangeState(It.IsAny<StateChangeContext>()), Times.Never);\n        }\n\n        [Fact]\n        public void Execute_DoesNotUseBatching_WhenConnectionMethod_ThrowsAnException()\n        {\n            // Arrange\n            _connection\n                .Setup(x => x.GetFirstByLowestScoreFromSet(It.IsAny<string>(), It.IsAny<double>(), It.IsAny<double>(),It.IsAny<int>()))\n                .Throws<NotImplementedException>();\n            \n            _schedule.Add(\"job-1\");\n\n            _connection\n                .Setup(x => x.GetFirstByLowestScoreFromSet(\"schedule\", 0, It.Is<double>(time => time > 0)))\n                .Returns(_schedule.FirstOrDefault);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.BackgroundJobId == \"job-1\" &&\n                ctx.NewState is EnqueuedState)));\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_DoesNotCallStateChanger_IfThereAreNoJobsToEnqueue(bool batching)\n        {\n            if (batching) EnableBatching();\n            var scheduler = CreateScheduler();\n\n            scheduler.Execute(_context.Object);\n\n            _stateChanger.Verify(\n                x => x.ChangeState(It.IsAny<StateChangeContext>()),\n                Times.Never);\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_RemovesAJobIdentifierFromTheSet_WhenStateChangeFails(bool batching)\n        {\n            if (batching) EnableBatching();\n            _stateChanger\n                .Setup(x => x.ChangeState(It.IsAny<StateChangeContext>()))\n                .Returns<IState>(null);\n            _schedule.Add(JobId);\n\n            var scheduler = CreateScheduler();\n            \n            scheduler.Execute(_context.Object);\n\n            _transaction.Verify(x => x.RemoveFromSet(\"schedule\", JobId));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_MovesJobToTheFailedState_WithFiltersDisabled_WhenStateChangerThrowsAnException(bool batching)\n        {\n            // Arrange\n            if (batching) EnableBatching();\n\n            _schedule.Add(JobId);\n            _stateChanger\n                .Setup(x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.NewState is EnqueuedState)))\n                .Throws<InvalidOperationException>();\n\n            var scheduler = CreateScheduler();\n            scheduler.RetryDelayFunc = _ => TimeSpan.FromMilliseconds(50);\n\n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            _stateChanger.Verify(\n                x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.NewState is EnqueuedState)),\n                Times.AtLeast(3));\n            \n            _stateChanger.Verify(\n                x => x.ChangeState(It.Is<StateChangeContext>(ctx => \n                    ctx.BackgroundJobId == JobId &&\n                    ctx.NewState is FailedState &&\n                    ((FailedState)ctx.NewState).Exception.GetType() == typeof(InvalidOperationException) &&\n                    ctx.ExpectedStates.Contains(ScheduledState.StateName) &&\n                    ctx.DisableFilters == true)),\n                Times.Once);\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_AbleToProcessFurtherJobs_WhenStateChangerThrowsAnException_ForPreviousOnes(bool batching)\n        {\n            // Arrange\n            if (batching) EnableBatching();\n\n            _schedule.Add(JobId);\n            _schedule.Add(\"AnotherId\");\n\n            _stateChanger\n                .Setup(x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.BackgroundJobId == JobId && ctx.NewState is ScheduledState)))\n                .Throws<InvalidOperationException>();\n\n            var scheduler = CreateScheduler();\n            \n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            _stateChanger.Verify(\n                x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.BackgroundJobId == \"AnotherId\" && ctx.NewState is EnqueuedState)),\n                Times.Once);\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_ActsWithinADistributedLock(bool batching)\n        {\n            if (batching) EnableBatching();\n            var scheduler = CreateScheduler();\n\n            scheduler.Execute(_context.Object);\n\n            _connection.Verify(x => x.AcquireDistributedLock(It.IsAny<string>(), It.IsAny<TimeSpan>()));\n            _distributedLock.Verify(x => x.Dispose());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_DoesNotThrowDistributedLockTimeoutException(bool batching)\n        {\n            if (batching) EnableBatching();\n            _connection\n                .Setup(x => x.AcquireDistributedLock(\"locks:schedulepoller\", It.IsAny<TimeSpan>()))\n                .Throws(new DistributedLockTimeoutException(\"locks:schedulepoller\"));\n\n            var scheduler = CreateScheduler();\n\n            scheduler.Execute(_context.Object);\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_RemovesJobFromSchedule_WhenIdDoesNotExists(bool batching)\n        {\n            // Arrange\n            if (batching) EnableBatching();\n            _schedule.Add(JobId);\n\n            _connection.Setup(x => x.GetJobData(JobId)).Returns<JobData>(null);\n\n            _stateChanger\n                .Setup(x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.NewState is EnqueuedState)))\n                .Returns<IState>(null);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.IsAny<StateChangeContext>()), Times.Once);\n            _transaction.Verify(x => x.RemoveFromSet(\"schedule\", JobId));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_RemovesJobFromSchedule_WhenJobIsNotInScheduledState(bool batching)\n        {\n            // Arrange\n            if (batching) EnableBatching();\n            _schedule.Add(JobId);\n\n            _connection.Setup(x => x.GetJobData(JobId))\n                .Returns(new JobData { State = SucceededState.StateName });\n\n            _stateChanger\n                .Setup(x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.NewState is EnqueuedState)))\n                .Returns<IState>(null);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.IsAny<StateChangeContext>()), Times.Once);\n            _transaction.Verify(x => x.RemoveFromSet(\"schedule\", JobId));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_DoesNotRemoveJobFromSchedule_WhenJobIsInTheScheduledState(bool batching)\n        {\n            // Arrange\n            if (batching) EnableBatching();\n            _schedule.Add(JobId);\n\n            _connection.Setup(x => x.GetJobData(JobId))\n                .Returns(new JobData { State = ScheduledState.StateName });\n\n            _stateChanger\n                .SetupSequence(x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.NewState is EnqueuedState)))\n                .Returns((IState)null)\n                .Returns(() => { _schedule.Remove(JobId); return new EnqueuedState(); });\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.RemoveFromSet(\"schedule\", JobId), Times.Never);\n            _transaction.Verify(x => x.Commit(), Times.Never);\n        }\n\n        private DelayedJobScheduler CreateScheduler()\n        {\n            return new DelayedJobScheduler(TimeSpan.Zero, _stateChanger.Object);\n        }\n        \n        private void EnableBatching()\n        {\n            _connection\n                .Setup(x => x.GetFirstByLowestScoreFromSet(null, It.IsAny<double>(), It.IsAny<double>(), It.IsAny<int>()))\n                .Throws(new ArgumentNullException(\"key\"));\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/PerformContextFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class PerformContextFacts\n    {\n        private readonly Mock<JobStorage> _storage;\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly Mock<IJobCancellationToken> _cancellationToken;\n        private readonly BackgroundJobMock _backgroundJob;\n\n        public PerformContextFacts()\n        {\n            _storage = new Mock<JobStorage>();\n            _connection = new Mock<IStorageConnection>();\n            _backgroundJob = new BackgroundJobMock();\n            _cancellationToken = new Mock<IJobCancellationToken>();\n        }\n\n        [Fact]\n        public void Ctor_DoesNotThrowAnException_WhenStorageIsNull()\n        {\n            var context = new PerformContext(null, _connection.Object, _backgroundJob.Object, _cancellationToken.Object);\n            Assert.NotNull(context);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenConnectionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new PerformContext(_storage.Object, null, _backgroundJob.Object, _cancellationToken.Object));\n\n            Assert.Equal(\"connection\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenBackgroundJobIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new PerformContext(_storage.Object, _connection.Object, null, _cancellationToken.Object));\n\n            Assert.Equal(\"backgroundJob\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenCancellationTokenIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new PerformContext(_storage.Object, _connection.Object, _backgroundJob.Object, null));\n\n            Assert.Equal(\"cancellationToken\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_CorrectlySets_AllInstanceProperties()\n        {\n            var context = CreateContext();\n\n            Assert.Same(_storage.Object, context.Storage);\n            Assert.Equal(_backgroundJob.Object, context.BackgroundJob);\n            Assert.NotNull(context.Items);\n            Assert.Same(_connection.Object, context.Connection);\n            Assert.Same(_cancellationToken.Object, context.CancellationToken);\n        }\n\n        [Fact]\n        public void CopyCtor_ThrowsAnException_WhenContextIsNull()\n        {\n            Assert.Throws<NullReferenceException>(\n                () => new PerformContext(null));\n        }\n\n        [Fact]\n        public void CopyCtor_CopiesAllPropertyValues()\n        {\n            var context = CreateContext();\n            var contextCopy = new PerformContext(context);\n            \n            Assert.Same(context.Items, contextCopy.Items);\n            Assert.Same(context.Storage, contextCopy.Storage);\n            Assert.Same(context.Connection, contextCopy.Connection);\n            Assert.Same(context.BackgroundJob, contextCopy.BackgroundJob);\n            Assert.Same(context.CancellationToken, contextCopy.CancellationToken);\n        }\n\n        [Fact]\n        public void SetJobParameter_ThrowsAnException_WhenParameterNameIsNullOrEmpty()\n        {\n            var context = CreateContext();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => context.SetJobParameter(null, null));\n\n            Assert.Equal(\"name\", exception.ParamName);\n        }\n\n        [Fact]\n        public void SetJobParameter_ConvertsValueToJson_AndSetsItUsingConnection()\n        {\n            var context = CreateContext();\n            \n            context.SetJobParameter(\"name\", \"value\");\n\n            _connection.Verify(x => x.SetJobParameter(_backgroundJob.Id, \"name\", \"\\\"value\\\"\"));\n        }\n\n        [Fact]\n        public void GetJobParameter_ThrowsAnException_WhenNameIsNullOrEmpty()\n        {\n            var context = CreateContext();\n\n            Assert.Throws<ArgumentNullException>(\n                () => context.GetJobParameter<string>(null));\n        }\n\n        [Fact]\n        public void GetJobParameter_ThrowsAnException_WhenParameterCouldNotBeDeserialized()\n        {\n            _connection.Setup(x => x.GetJobParameter(_backgroundJob.Id, \"name\")).Returns(\"value\");\n            var context = CreateContext();\n\n            Assert.Throws<InvalidOperationException>(\n                () => context.GetJobParameter<int>(\"name\"));\n        }\n\n        private PerformContext CreateContext()\n        {\n            return new PerformContext(\n                _storage.Object, _connection.Object, _backgroundJob.Object, _cancellationToken.Object);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/RecurringJobSchedulerFacts.cs",
    "content": "﻿extern alias ReferencedCronos;\n\nusing System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Threading;\nusing ReferencedCronos::Cronos;\nusing Hangfire.Client;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class RecurringJobSchedulerFacts\n    {\n        private const string RecurringJobId = \"recurring-job-id\";\n\n        private readonly Mock<JobStorageConnection> _connection;\n        private readonly Mock<IWriteOnlyTransaction> _transaction;\n        private readonly Dictionary<string, string> _recurringJob;\n        private Func<DateTime> _nowInstantFactory;\n        private readonly Mock<ITimeZoneResolver> _timeZoneResolver;\n        private readonly BackgroundProcessContextMock _context;\n        private readonly Mock<IBackgroundJobFactory> _factory;\n        private readonly Mock<IStateMachine> _stateMachine;\n        private readonly BackgroundJobMock _backgroundJobMock;\n\n        private static readonly string _expressionString = \"* * * * *\";\n        private static readonly TimeSpan _delay = TimeSpan.FromMilliseconds(1);\n        private readonly CronExpression _cronExpression = CronExpression.Parse(_expressionString);\n        private readonly DateTime _nowInstant = new DateTime(2017, 03, 30, 15, 30, 0, DateTimeKind.Utc);\n        private readonly DateTime _nextInstant;\n        private readonly List<string> _schedule = new List<string> { RecurringJobId };\n\n        public RecurringJobSchedulerFacts()\n        {\n            _context = new BackgroundProcessContextMock();\n\n            // Setting up the successful path\n\n            var timeZone = TimeZoneInfo.Local;\n\n            _nowInstantFactory = () => _nowInstant;\n\n            _timeZoneResolver = new Mock<ITimeZoneResolver>();\n            _timeZoneResolver.Setup(x => x.GetTimeZoneById(It.IsAny<string>())).Throws<InvalidTimeZoneException>();\n            _timeZoneResolver.Setup(x => x.GetTimeZoneById(timeZone.Id)).Returns(timeZone);\n            _timeZoneResolver.Setup(x => x.GetTimeZoneById(\"UTC\")).Returns(TimeZoneInfo.Utc);\n\n            // ReSharper disable once PossibleInvalidOperationException\n            _nextInstant = _cronExpression.GetNextOccurrence(_nowInstant, timeZone).Value;\n\n            _recurringJob = new Dictionary<string, string>\n            {\n                { \"Cron\", _expressionString },\n                { \"Job\", InvocationData.SerializeJob(Job.FromExpression(() => Console.WriteLine())).SerializePayload() },\n                { \"TimeZoneId\", timeZone.Id }\n            };\n\n            _connection = new Mock<JobStorageConnection>();\n            _context.Storage.Setup(x => x.GetConnection()).Returns(_connection.Object);\n\n            _connection.Setup(x => x.GetFirstByLowestScoreFromSet(\"recurring-jobs\", 0, JobHelper.ToTimestamp(_nowInstant)))\n                .Returns(() => _schedule.FirstOrDefault());\n\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{RecurringJobId}\"))\n                .Returns(_recurringJob);\n\n            _connection.SetupSequence(x => x.GetFirstByLowestScoreFromSet(\"recurring-jobs\", 0, JobHelper.ToTimestamp(_nowInstant), It.IsAny<int>()))\n                .Returns(() => _schedule.ToList());\n\n            _transaction = new Mock<IWriteOnlyTransaction>();\n            _transaction\n                .Setup(x => x.RemoveFromSet(\"recurring-jobs\", It.IsNotNull<string>()))\n                .Callback<string, string>((key, value) => _schedule.Remove(value));\n            _transaction\n                .Setup(x => x.AddToSet(\"recurring-jobs\", It.IsNotNull<string>(), It.IsAny<double>()))\n                .Callback<string, string, double>((key, value, score) => _schedule.Remove(value));\n\n            _connection.Setup(x => x.CreateWriteTransaction()).Returns(_transaction.Object);\n\n            _backgroundJobMock = new BackgroundJobMock();\n\n            _factory = new Mock<IBackgroundJobFactory>();\n            _factory.Setup(x => x.Create(It.IsAny<CreateContext>())).Returns(_backgroundJobMock.Object);\n            \n            _stateMachine = new Mock<IStateMachine>();\n            _factory.SetupGet(x => x.StateMachine).Returns(_stateMachine.Object);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenJobFactoryIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n// ReSharper disable once AssignNullToNotNullAttribute\n                () => new RecurringJobScheduler(null, _delay, _timeZoneResolver.Object, _nowInstantFactory));\n\n            Assert.Equal(\"factory\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenTimeZoneResolverIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                // ReSharper disable once AssignNullToNotNullAttribute\n                () => new RecurringJobScheduler(_factory.Object, _delay, null, _nowInstantFactory));\n\n            Assert.Equal(\"timeZoneResolver\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenNowInstantFactoryIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                // ReSharper disable once AssignNullToNotNullAttribute\n                () => new RecurringJobScheduler(_factory.Object, _delay, _timeZoneResolver.Object, null));\n\n            Assert.Equal(\"nowFactory\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Execute_ThrowsAnException_WhenContextIsNull()\n        {\n            var scheduler = CreateScheduler();\n\n            // ReSharper disable once AssignNullToNotNullAttribute\n            var exception = Assert.Throws<ArgumentNullException>(() => scheduler.Execute(null));\n\n            Assert.Equal(\"context\", exception.ParamName);\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_EnqueuesAJob_WhenItIsTimeToRunIt(bool useJobStorageConnection)\n        {\n            SetupConnection(useJobStorageConnection);\n            var scheduler = CreateScheduler();\n\n            scheduler.Execute(_context.Object);\n\n            _factory.Verify(x => x.Create(It.IsNotNull<CreateContext>()));\n            _stateMachine.Verify(x => x.ApplyState(It.Is<ApplyStateContext>(ctx => ctx.NewState is EnqueuedState)));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_DoesNotHandleRecurringJobs_CreatedByNewerVersion(bool useJobStorageConnection)\n        {\n            SetupConnection(useJobStorageConnection);\n            _recurringJob[\"V\"] = \"3\";\n            var scheduler = CreateScheduler();\n\n            scheduler.Execute(_context.Object);\n\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_ReschedulesRecurringJobs_WithUnsupportedVersions_WhenSomeRetriesLeft(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n            _recurringJob[\"V\"] = \"3\";\n            var scheduler = CreateScheduler();\n            \n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict =>\n                dict[\"RetryAttempt\"] == \"1\")));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant + _delay)));\n            \n            _transaction.Verify(x => x.Commit());\n        }\n        \n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_DisablesRecurringJobs_WithUnsupportedVersions_WhenRetryAttemptsExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n            _recurringJob[\"V\"] = \"3\";\n            _recurringJob[\"RetryAttempt\"] = \"10\";\n            var scheduler = CreateScheduler();\n            \n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict =>\n                dict[\"Error\"].Contains(\"supported version\"))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, -1));\n            \n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_EnqueuesAJobToAGivenQueue_WhenItIsTimeToRunIt(bool useJobStorageConnection)\n        {\n            SetupConnection(useJobStorageConnection);\n            _recurringJob[\"Queue\"] = \"critical\";\n            var scheduler = CreateScheduler();\n\n            scheduler.Execute(_context.Object);\n\n            _stateMachine.Verify(x => x.ApplyState(\n                It.Is<ApplyStateContext>(ctx => ((EnqueuedState)ctx.NewState).Queue == \"critical\")));\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_UpdatesRecurringJobParameters_OnCompletion(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            var jobKey = $\"recurring-job:{RecurringJobId}\";\n\n            _transaction.Verify(x => x.SetRangeInHash(\n                jobKey,\n                It.Is<Dictionary<string, string>>(rj =>\n                    rj.ContainsKey(\"LastJobId\") && rj[\"LastJobId\"] == _backgroundJobMock.Id)));\n\n            _transaction.Verify(x => x.SetRangeInHash(\n                jobKey,\n                It.Is<Dictionary<string, string>>(rj =>\n                    rj.ContainsKey(\"LastExecution\") && rj[\"LastExecution\"]\n                        == JobHelper.SerializeDateTime(_nowInstant))));\n\n            _transaction.Verify(x => x.SetRangeInHash(\n                jobKey,\n                It.Is<Dictionary<string, string>>(rj =>\n                    rj.ContainsKey(\"NextExecution\") && rj[\"NextExecution\"]\n                        == JobHelper.SerializeDateTime(_nextInstant))));\n            \n            _transaction.Verify(x => x.SetRangeInHash(\n                jobKey,\n                It.Is<Dictionary<string, string>>(rj => !rj.ContainsKey(\"RetryAttempt\"))));\n            \n            _transaction.Verify(x => x.Commit());\n        }\n        \n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_DoesNotUpdateRetryAttempt_WhenItWasNotModified(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(_nowInstant);\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant);\n            _recurringJob[\"V\"] = \"2\";\n            _recurringJob[\"RetryAttempt\"] = \"0\";\n            \n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            \n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n            \n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{RecurringJobId}\",\n                It.Is<Dictionary<string, string>>(rj => !rj.ContainsKey(\"RetryAttempt\"))));\n            \n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.AddMinutes(1))));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_DoesNotEnqueueRecurringJob_AndDoesNotUpdateIt_ButNextExecution_WhenItIsNotATimeToRunIt(\n            bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            var scheduler = CreateScheduler(_nowInstant);\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()), Times.Never);\n\n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{RecurringJobId}\",\n                It.Is<Dictionary<string, string>>(rj =>\n                    rj.ContainsKey(\"NextExecution\") && rj[\"NextExecution\"]\n                        == JobHelper.SerializeDateTime(_nextInstant))));\n            \n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_TakesIntoConsideration_LastExecutionTime_ConvertedToLocalTimezone(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            var time = _nowInstant;\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(time);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()), Times.Never);\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_RemovesRecurringJobFromSchedule_WhenHashDoesNotExist(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            \n            _schedule.Clear();\n            _schedule.Add(\"non-existing-job\");\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.RemoveFromSet(\"recurring-jobs\", \"non-existing-job\"));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_HandlesJobLoadException(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            _recurringJob[\"Job\"] = JobHelper.ToJson(new InvocationData(\"SomeType\", \"SomeMethod\", \"Parameters\", \"arguments\"));\n\n            var scheduler = CreateScheduler();\n\n            // Act & Assert does not throw\n            scheduler.Execute(_context.Object);\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_GetsInstance_InAGivenTimeZone(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n\n            var timeZoneId = PlatformHelper.IsRunningOnWindows() ? \"Hawaiian Standard Time\" : \"Pacific/Honolulu\";\n            var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);\n            _recurringJob[\"TimeZoneId\"] = timeZone.Id;\n            var scheduler = CreateScheduler();\n\n            // Act & Assert does not throw\n            scheduler.Execute(_context.Object);\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_GetInstance_DoesNotCreateAJob_WhenGivenOneIsNotFound(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            _recurringJob[\"TimeZoneId\"] = \"Some garbage\";\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_UsesGivenCreatedAtTime(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            var createdAt = _nowInstant.AddHours(-3);\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(createdAt);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Once);\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_DoesNotFixCreatedAtField_IfItExists(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(DateTime.UtcNow);\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            _connection.Verify(\n                x => x.SetRangeInHash(\n                    $\"recurring-job:{RecurringJobId}\",\n                    It.Is<Dictionary<string, string>>(rj => rj.ContainsKey(\"CreatedAt\"))),\n                Times.Never);\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_FixedMissingCreatedAtField(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            _recurringJob.Remove(\"CreatedAt\");\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(\n                x => x.SetRangeInHash(\n                    $\"recurring-job:{RecurringJobId}\",\n                    It.Is<Dictionary<string, string>>(rj => rj.ContainsKey(\"CreatedAt\"))),\n                Times.Once);\n            \n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_UsesNextExecutionTime_WhenBothLastExecutionAndCreatedAtAreNotAvailable(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            var nextExecution = _nowInstant.AddHours(-10);\n            _recurringJob[\"NextExecution\"] = JobHelper.SerializeDateTime(nextExecution);\n            _recurringJob.Remove(\"CreatedAt\");\n            _recurringJob.Remove(\"LastExecution\");\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{RecurringJobId}\",\n                It.Is<Dictionary<string, string>>(rj =>\n                    rj.ContainsKey(\"LastExecution\") && rj[\"LastExecution\"]\n                    == JobHelper.SerializeDateTime(_nowInstant))));\n            \n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_DoesNotThrowDistributedLockTimeoutException(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            _connection\n                .Setup(x => x.AcquireDistributedLock(\"recurring-jobs:lock\", It.IsAny<TimeSpan>()))\n                .Throws(new DistributedLockTimeoutException(\"recurring-jobs:lock\"));\n\n            var scheduler = CreateScheduler();\n\n            // Act & Assert (Does Not Throw)\n            scheduler.Execute(_context.Object);\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_DoesNotEnqueueRecurringJob_WhenItIsCorrectAndItWasNotTriggered(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n\n            _recurringJob[\"NextExecution\"] = JobHelper.SerializeDateTime(_nowInstant.AddMinutes(1));\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(_nowInstant);\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()), Times.Never);\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_AcquiresDistributedLock_ForEachRecurringJob(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _connection.Verify(x => x.AcquireDistributedLock(\"lock:recurring-job:recurring-job-id\", It.IsAny<TimeSpan>()));\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_SchedulesNextExecution_AfterCreatingAJob(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()));\n\n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{RecurringJobId}\",\n                It.Is<Dictionary<string, string>>(rj =>\n                    rj.ContainsKey(\"NextExecution\") && \n                    rj[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.AddMinutes(1)))));\n\n            _transaction.Verify(x => x.AddToSet(\n                \"recurring-jobs\", \n                \"recurring-job-id\", \n                JobHelper.ToTimestamp(_nowInstant.AddMinutes(1))));\n\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_FixesNextExecution_WhenItsNotATimeToRunAJob(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(_nowInstant);\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()), Times.Never);\n\n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{RecurringJobId}\",\n                It.Is<Dictionary<string, string>>(rj =>\n                    rj.ContainsKey(\"NextExecution\") &&\n                    rj[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.AddMinutes(1)))));\n\n            _transaction.Verify(x => x.AddToSet(\n                \"recurring-jobs\",\n                \"recurring-job-id\",\n                JobHelper.ToTimestamp(_nowInstant.AddMinutes(1))));\n\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void Execute_DoesNotCycleImmediately_WhenItCantDeserializeEverything(bool useJobStorageConnection)\n        {\n            // Arrange\n            SetupConnection(useJobStorageConnection);\n\n            _factory.Setup(x => x.Create(It.IsAny<CreateContext>())).Throws<InvalidOperationException>();\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _connection.Verify(x => x.GetAllEntriesFromHash(It.IsAny<string>()), Times.Once);\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_UsesTimeZoneResolver_WhenCalculatingNextExecution(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            var timeZone = TimeZoneInfo.FindSystemTimeZoneById(PlatformHelper.IsRunningOnWindows()\n                ? \"Hawaiian Standard Time\"\n                : \"Pacific/Honolulu\");\n\n            _timeZoneResolver\n                .Setup(x => x.GetTimeZoneById(It.Is<string>(id => id == \"Hawaiian Standard Time\" || id == \"Pacific/Honolulu\")))\n                .Returns(timeZone);\n\n            // We are returning IANA time zone on Windows and Windows time zone on Linux.\n            _recurringJob[\"Cron\"] = \"0 0 * * *\";\n            _recurringJob[\"TimeZoneId\"] = PlatformHelper.IsRunningOnWindows() ? \"Pacific/Honolulu\" : \"Hawaiian Standard Time\";\n            _recurringJob[\"NextExecution\"] = JobHelper.SerializeDateTime(_nowInstant.AddHours(18).AddMinutes(30));\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{RecurringJobId}\", It.Is<Dictionary<string, string>>(dict =>\n                !dict.ContainsKey(\"NextExecution\"))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.AddHours(18).AddMinutes(30))));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_DoesNotScheduleRecurringJob_ToThePast(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(_nowInstant.AddMinutes(-2));\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{RecurringJobId}\", It.Is<Dictionary<string, string>>(dict =>\n                dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.AddMinutes(1)))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.AddMinutes(1))));\n            _transaction.Verify(x => x.Commit(), Times.Once);\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_CanHandleRecurringJob_WithCronThatNeverFires(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"Cron\"] = \"0 0 31 2 *\";\n            _recurringJob[\"NextExecution\"] = JobHelper.SerializeDateTime(_nowInstant);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _stateMachine.Verify(\n                x => x.ApplyState(It.IsAny<ApplyStateContext>()),\n                Times.Never);\n            \n            _transaction.Verify(x => x.SetRangeInHash(\n                $\"recurring-job:{RecurringJobId}\", \n                It.Is<Dictionary<string, string>>(dict =>\n                    dict.ContainsKey(\"NextExecution\") && dict[\"NextExecution\"] == String.Empty)));\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, -1.0D));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_TriggersRecurringJobOnce_WithMissedScheduleByDefault(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"Cron\"] = \"0 * * * *\";\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(_nowInstant.AddDays(-1));\n\n            var scheduler = CreateScheduler(delay: TimeSpan.FromMilliseconds(100));\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.Is<CreateContext>(ctx =>\n                (long)ctx.Parameters[\"Time\"] == JobHelper.ToTimestamp(_nowInstant))), Times.Once);\n\n            _stateMachine.Verify(x => x.ApplyState(It.IsNotNull<ApplyStateContext>()), Times.Once);\n\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{RecurringJobId}\", It.Is<Dictionary<string, string>>(dict =>\n                dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.AddMinutes(30)) &&\n                dict[\"LastExecution\"] == JobHelper.SerializeDateTime(_nowInstant))));\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.AddMinutes(30))));\n\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_TriggersRecurringJobMultipleTimes_WithMissedScheduleWhenStrictModeIsUsed(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"Cron\"] = \"0 * * * *\";\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(_nowInstant.AddHours(-3));\n            _recurringJob[\"Misfire\"] = MisfireHandlingMode.Strict.ToString(\"D\");\n\n            var scheduler = CreateScheduler(delay: TimeSpan.FromMilliseconds(100));\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.Is<CreateContext>(ctx =>\n                (long)ctx.Parameters[\"Time\"] == JobHelper.ToTimestamp(_nowInstant.AddMinutes(-30)))), Times.Once);\n\n            _factory.Verify(x => x.Create(It.Is<CreateContext>(ctx =>\n                (long)ctx.Parameters[\"Time\"] == JobHelper.ToTimestamp(_nowInstant.AddMinutes(-30).AddHours(-1)))), Times.Once);\n\n            _factory.Verify(x => x.Create(It.Is<CreateContext>(ctx =>\n                (long)ctx.Parameters[\"Time\"] == JobHelper.ToTimestamp(_nowInstant.AddMinutes(-30).AddHours(-2)))), Times.Once);\n\n            _stateMachine.Verify(\n                x => x.ApplyState(It.IsNotNull<ApplyStateContext>()),\n                Times.Exactly(3));\n\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{RecurringJobId}\", It.Is<Dictionary<string, string>>(dict =>\n                dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.AddMinutes(30)) &&\n                dict[\"LastExecution\"] == JobHelper.SerializeDateTime(_nowInstant.AddMinutes(-30)))));\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.AddMinutes(30))));\n\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_DoesNotTriggerRecurringJob_WithMissedScheduleWhenIgnorableModeIsUsed(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"Cron\"] = \"0 * * * *\";\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(_nowInstant.AddHours(-3));\n            _recurringJob[\"Misfire\"] = MisfireHandlingMode.Ignorable.ToString(\"D\");\n\n            var scheduler = CreateScheduler(delay: TimeSpan.FromMilliseconds(100));\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n            _stateMachine.Verify(x => x.ApplyState(It.IsAny<ApplyStateContext>()), Times.Never);\n\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{RecurringJobId}\", It.Is<Dictionary<string, string>>(dict =>\n                dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.AddMinutes(30)) &&\n                !dict.ContainsKey(\"LastExecution\"))));\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.AddMinutes(30))));\n\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_TriggersRecurringJob_WhenIgnorableModeIsUsed_AndErrorIsSlight(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _nowInstantFactory = () => _nowInstant.AddMilliseconds(123);\n            _recurringJob[\"Cron\"] = \"30 * * * *\";\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(_nowInstant.AddHours(-3));\n            _recurringJob[\"Misfire\"] = MisfireHandlingMode.Ignorable.ToString(\"D\");\n\n            var scheduler = CreateScheduler(delay: TimeSpan.FromMilliseconds(100));\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Once);\n\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{RecurringJobId}\",\n                It.Is<Dictionary<string, string>>(dict =>\n                    dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.AddHours(1)) &&\n                    dict[\"LastExecution\"] == JobHelper.SerializeDateTime(_nowInstant))));\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.AddHours(1))));\n\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false, MisfireHandlingMode.Relaxed), InlineData(true, MisfireHandlingMode.Relaxed)]\n        [InlineData(false, MisfireHandlingMode.Strict), InlineData(true, MisfireHandlingMode.Strict)]\n        [InlineData(false, MisfireHandlingMode.Ignorable), InlineData(true, MisfireHandlingMode.Ignorable)]\n        public void Execute_DoesNotMissCurrentExecution_ForAnyMisfireHandlingMode(bool batching, MisfireHandlingMode mode)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"Cron\"] = \"30 * * * *\";\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(_nowInstant.AddHours(-3));\n            _recurringJob[\"Misfire\"] = mode.ToString(\"D\");\n\n            var scheduler = CreateScheduler(delay: TimeSpan.FromMilliseconds(100));\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.Is<CreateContext>(ctx =>\n                (long)ctx.Parameters[\"Time\"] == JobHelper.ToTimestamp(_nowInstant))), Times.Once);\n\n            _transaction.Verify(x => x.SetRangeInHash($\"recurring-job:{RecurringJobId}\", It.Is<Dictionary<string, string>>(dict =>\n                dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.AddHours(1)) &&\n                dict[\"LastExecution\"] == JobHelper.SerializeDateTime(_nowInstant))));\n\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.AddHours(1))));\n\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void Execute_DoesNotUseBatchedMethod_WhenStorageConnectionThrowsAnException()\n        {\n            // Arrange\n            SetupConnection(true);\n            _connection\n                .Setup(x => x.GetFirstByLowestScoreFromSet(It.IsAny<string>(), It.IsAny<double>(), It.IsAny<double>(), It.IsAny<int>()))\n                .Throws<NotSupportedException>();\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.AddMinutes(1))));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_AlwaysUpdatesScoreForTheSetItem_EvenIfRecurringJobWasNotChanged(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant.AddMinutes(-1));\n            _recurringJob[\"LastExecution\"] = JobHelper.SerializeDateTime(_nowInstant);\n            _recurringJob[\"NextExecution\"] = JobHelper.SerializeDateTime(_nowInstant.AddMinutes(1));\n            _recurringJob[\"V\"] = \"2\";\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.IsAny<IEnumerable<KeyValuePair<string, string>>>()), Times.Never);\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.AddMinutes(1))));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_UsesUtcTimeZone_WhenCorrespondingFieldIsNullOrEmpty(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"TimeZoneId\"] = null;\n            _recurringJob[\"Cron\"] = \"0 30 15 30 03 *\";\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()));\n        }\n        \n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_ReschedulesRecurringJob_WhenCronExpressionIsInvalid_AndRetryAttemptsAreNotExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant.AddDays(-1));\n            _recurringJob[\"Cron\"] = \"some garbage\";\n            _recurringJob[\"V\"] = \"2\";\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            Assert.True(_delay > TimeSpan.Zero);\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict =>\n                dict.Count == 3 &&\n                dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.Add(_delay)) &&\n                dict[\"RetryAttempt\"] == \"1\" &&\n                dict.ContainsKey(\"Error\"))));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, JobHelper.ToTimestamp(_nowInstant.Add(_delay))));\n            _transaction.Verify(x => x.Commit());\n        }\n        \n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_DoesNotFailOnInvalidCronExpression_AndSimplySetsNextExecutionToNull_WhenRetryAttemptsExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"Cron\"] = \"some garbage\";\n            _recurringJob[\"RetryAttempt\"] = \"10\";\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, -1));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_ClearsLastError_AndRetryAttempts_AfterSuccessfulScheduling(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            var scheduler = CreateScheduler();\n\n            _recurringJob[\"Error\"] = \"Some error that previously happened\";\n            _recurringJob[\"RetryAttempt\"] = \"10\";\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _factory.Verify(x => x.Create(It.IsNotNull<CreateContext>()));\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict =>\n                dict[\"Error\"] == String.Empty && dict[\"RetryAttempt\"] == \"0\")));\n            _transaction.Verify(x => x.Commit());\n        }\n        \n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_ReschedulesRecurringJob_WhenThereAreIssuesWithJobLoading_AndRetryAttemptsAreNotExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant.AddDays(-1));\n            _recurringJob[\"Job\"] = null;\n            _recurringJob[\"V\"] = \"2\";\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            Assert.True(_delay > TimeSpan.Zero);\n            \n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Never);\n            \n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict =>\n                dict.Count == 3 &&\n                dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.Add(_delay)) &&\n                dict[\"RetryAttempt\"] == \"1\" &&\n                dict.ContainsKey(\"Error\"))));\n            \n            _transaction.Verify(x => x.AddToSet(\n                \"recurring-jobs\",\n                RecurringJobId,\n                JobHelper.ToTimestamp(_nowInstant.Add(_delay))));\n            \n            _transaction.Verify(x => x.Commit());\n        }\n        \n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_ReschedulesRecurringJob_WithIncreasedAttemptNumber_WhenThereAreIssuesWithJobLoading_AndRetryAttemptsAreNotExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"Job\"] = null;\n            _recurringJob[\"RetryAttempt\"] = \"1\";\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict =>\n                dict.ContainsKey(\"RetryAttempt\") && dict[\"RetryAttempt\"] == \"2\")));\n            \n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_HidesRecurringJob_FromScheduler_WhenJobCanNotBeLoaded_AndRetryAttemptsExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"RetryAttempt\"] = \"10\";\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant.AddDays(-1));\n            _recurringJob[\"NextExecution\"] = JobHelper.SerializeDateTime(_nowInstant);\n            _recurringJob[\"Job\"] = InvocationData.SerializeJob(\n                Job.FromExpression(() => Console.WriteLine())).SerializePayload().Replace(\"Console\", \"SomeNonExistingClass\");\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict => \n                dict.Count == 3 &&\n                dict[\"NextExecution\"] == String.Empty &&\n                dict[\"Error\"].Contains(\"JobLoadException\") &&\n                dict[\"V\"] == \"2\")));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, -1));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_HidesRecurringJob_FromScheduler_WhenJobCanNotBeDeserialized_AndRetryAttemptsExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"RetryAttempt\"] = \"10\";\n            _recurringJob[\"Job\"] = \"Some garbage\";\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant.AddDays(-1));\n            _recurringJob[\"NextExecution\"] = JobHelper.SerializeDateTime(_nowInstant);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict => \n                dict.Count == 3 &&\n                dict[\"NextExecution\"] == String.Empty &&\n                dict[\"Error\"].Contains(\"JsonReaderException\") &&\n                dict[\"V\"] == \"2\")));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, -1));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_HidesRecurringJob_FromScheduler_WhenJobIsNull_AndRetryAttemptsExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"RetryAttempt\"] = \"10\";\n            _recurringJob[\"Job\"] = null;\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant.AddDays(-1));\n            _recurringJob[\"NextExecution\"] = JobHelper.SerializeDateTime(_nowInstant);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict => \n                dict.Count == 3 &&\n                dict[\"NextExecution\"] == String.Empty &&\n                dict[\"Error\"].Contains(\"The 'Job' field has a null\") &&\n                dict[\"V\"] == \"2\")));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, -1));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_HidesRecurringJob_FromScheduler_WhenTimeZoneCanNotBeResolved_AndRetryAttemptsExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n\n            _recurringJob[\"RetryAttempt\"] = \"10\";\n            _recurringJob[\"TimeZoneId\"] = \"Non-existing time zone\";\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant.AddDays(-1));\n            _recurringJob[\"NextExecution\"] = JobHelper.SerializeDateTime(_nowInstant);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict => \n                dict.Count == 3 &&\n                dict[\"NextExecution\"] == String.Empty &&\n                dict[\"Error\"].Contains(\"System.InvalidTimeZoneException\") &&\n                dict[\"V\"] == \"2\")));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, -1));\n            _transaction.Verify(x => x.Commit());\n        }\n        \n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_ReschedulesRecurringJob_WhenFactoryThrowsAnException_AndRetryAttemptsAreNotExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant.AddDays(-1));\n            _recurringJob[\"V\"] = \"2\";\n            _factory.Setup(x => x.Create(It.IsAny<CreateContext>())).Throws<InvalidOperationException>();\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            Assert.True(_delay > TimeSpan.Zero);\n            \n            _factory.Verify(x => x.Create(It.IsAny<CreateContext>()), Times.Once);\n            \n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict =>\n                dict.Count == 3 &&\n                dict[\"NextExecution\"] == JobHelper.SerializeDateTime(_nowInstant.Add(_delay)) &&\n                dict[\"RetryAttempt\"] == \"1\" &&\n                dict.ContainsKey(\"Error\"))));\n            \n            _transaction.Verify(x => x.AddToSet(\n                \"recurring-jobs\",\n                RecurringJobId,\n                JobHelper.ToTimestamp(_nowInstant.Add(_delay))));\n            \n            _transaction.Verify(x => x.Commit());\n        }\n        \n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_HidesRecurringJob_FromScheduler_WhenFactoryThrowsAnException_AndRetryAttemptsExceeded(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n            _factory.Setup(x => x.Create(It.IsAny<CreateContext>())).Throws(new InvalidOperationException(\"Invalid operation\"));\n            _recurringJob[\"RetryAttempt\"] = \"10\";\n            _recurringJob[\"CreatedAt\"] = JobHelper.SerializeDateTime(_nowInstant.AddDays(-1));\n            _recurringJob[\"NextExecution\"] = JobHelper.SerializeDateTime(_nowInstant);\n\n            var scheduler = CreateScheduler();\n\n            // Act\n            scheduler.Execute(_context.Object);\n\n            // Assert\n            _transaction.Verify(x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict => \n                dict.Count == 3 &&\n                dict[\"NextExecution\"] == String.Empty &&\n                dict[\"Error\"].StartsWith(\"System.InvalidOperationException\") &&\n                dict[\"V\"] == \"2\")));\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, -1));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_AbleToProcessFurtherJobs_WhenStateChangerThrowsAnException_ForPreviousOnes(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n            _schedule.Add(\"AnotherId\");\n            _connection.Setup(x => x.GetAllEntriesFromHash(\"recurring-job:AnotherId\"))\n                .Returns(_recurringJob);\n\n            _factory\n                .Setup(x => x.Create(It.Is<CreateContext>(ctx => (string)ctx.Parameters[\"RecurringJobId\"] == RecurringJobId)))\n                .Throws<InvalidOperationException>();\n\n            var scheduler = CreateScheduler();\n            \n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            _factory.Verify(\n                x => x.Create(It.Is<CreateContext>(ctx => (string)ctx.Parameters[\"RecurringJobId\"] == \"AnotherId\")),\n                Times.Once);\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_RemovesNonExistingRecurringJobFromSet_AndDoesNotStopPipelineImmediatelyInThisCase(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n            _schedule.Add(\"AnotherId\");\n            _connection.Setup(x => x.GetAllEntriesFromHash(\"recurring-job:AnotherId\")).Returns(_recurringJob);\n            _connection.Setup(x => x.GetAllEntriesFromHash($\"recurring-job:{RecurringJobId}\")).Returns<Dictionary<string, string>>(null);\n\n            var scheduler = CreateScheduler();\n            \n            // Act\n            scheduler.Execute(_context.Object);\n            \n            // Assert\n            _transaction.Verify(x => x.RemoveFromSet(\"recurring-jobs\", RecurringJobId));\n            _factory.Verify(\n                x => x.Create(It.Is<CreateContext>(ctx => (string)ctx.Parameters[\"RecurringJobId\"] == \"AnotherId\")),\n                Times.Once);\n        }\n        \n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Execute_DoesNotRescheduleRecurringJob_WhenExceptionRaisedFromTransactionCommit(bool batching)\n        {\n            // Arrange\n            SetupConnection(batching);\n            _recurringJob[\"RetryAttempt\"] = \"10\";\n            _transaction.SetupSequence(x => x.Commit())\n                .Throws<InvalidOperationException>()\n                .Pass();\n\n            var scheduler = CreateScheduler();\n            \n            // Act\n            Assert.Throws<InvalidOperationException>(() => scheduler.Execute(_context.Object));\n            \n            // Assert\n            _transaction.Verify(\n                x => x.SetRangeInHash(It.IsAny<string>(), It.Is<Dictionary<string, string>>(dict => \n                    dict.ContainsKey(\"Error\") &&\n                    dict[\"NextExecution\"] == String.Empty)),\n                Times.Never);\n            _transaction.Verify(x => x.AddToSet(\"recurring-jobs\", RecurringJobId, -1), Times.Never);\n            _transaction.Verify(x => x.Commit(), Times.Once);\n        }\n\n        private void SetupConnection(bool useJobStorageConnection)\n        {\n            if (useJobStorageConnection) EnableBatching();\n        }\n        \n        private void EnableBatching()\n        {\n            _connection\n                .Setup(x => x.GetFirstByLowestScoreFromSet(null, It.IsAny<double>(), It.IsAny<double>(), It.IsAny<int>()))\n                .Throws(new ArgumentNullException(\"key\"));\n        }\n\n        private RecurringJobScheduler CreateScheduler(DateTime? lastExecution = null, TimeSpan? delay = null)\n        {\n            var scheduler = new RecurringJobScheduler(\n                _factory.Object,\n                delay ?? _delay,\n                _timeZoneResolver.Object,\n                _nowInstantFactory);\n\n            if (lastExecution.HasValue)\n            {\n                _recurringJob.Add(\"LastExecution\", JobHelper.SerializeDateTime(lastExecution.Value));\n            }\n\n            return scheduler;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/ServerJobCancellationTokenFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Hangfire.Server;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class ServerJobCancellationTokenFacts : IDisposable\n    {\n        private const string ServerId = \"some-server\";\n        private const string WorkerId = \"1\";\n        private const string JobId = \"my-job\";\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly StateData _stateData;\n        private readonly CancellationTokenSource _shutdownCts;\n        private readonly CancellationTokenSource _cts;\n\n        public ServerJobCancellationTokenFacts()\n        {\n            _stateData = new StateData\n            {\n                Name = ProcessingState.StateName,\n                Data = new Dictionary<string, string>\n                {\n                    { \"ServerId\", ServerId },\n                    { \"WorkerId\", WorkerId },\n                }\n            };\n\n            _connection = new Mock<IStorageConnection>();\n            _connection.Setup(x => x.GetStateData(JobId)).Returns(_stateData);\n\n            _cts = new CancellationTokenSource();\n            _shutdownCts = new CancellationTokenSource();\n\n            ServerJobCancellationToken.AddServer(ServerId);\n        }\n\n        public void Dispose()\n        {\n            ServerJobCancellationToken.RemoveServer(ServerId);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenConnectionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new ServerJobCancellationToken(\n                    null, JobId, ServerId, WorkerId, _shutdownCts.Token));\n\n            Assert.Equal(\"connection\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenJobIdIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new ServerJobCancellationToken(\n                    _connection.Object, null, ServerId, WorkerId, _shutdownCts.Token));\n\n            Assert.Equal(\"jobId\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenServerIdIsIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new ServerJobCancellationToken(\n                    _connection.Object, JobId, null, WorkerId, _shutdownCts.Token));\n\n            Assert.Equal(\"serverId\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenWorkerIdIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new ServerJobCancellationToken(\n                    _connection.Object, JobId, ServerId, null, _shutdownCts.Token));\n\n            Assert.Equal(\"workerId\", exception.ParamName);\n        }\n\n        [Fact]\n        public void ShutdownTokenProperty_PointsToValue_LinkedWithShutdownToken()\n        {\n            var token = CreateToken();\n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n            _shutdownCts.Cancel();\n            Assert.True(token.ShutdownToken.IsCancellationRequested);\n        }\n\n        [Fact]\n        public void ThrowIfCancellationRequested_DoesNotThrowOnProcessingJob_IfNoShutdownRequested()\n        {\n            var token = CreateToken();\n\n            // Does not throw\n            token.ThrowIfCancellationRequested();\n        }\n\n        [Fact]\n        public void ThrowIfCancellationRequested_ThrowsOperationCanceled_OnShutdownRequest()\n        {\n            _shutdownCts.Cancel();\n            var token = CreateToken();\n\n            Assert.Throws<OperationCanceledException>(\n                () => token.ThrowIfCancellationRequested());\n        }\n\n        [Fact]\n        public void ThrowIfCancellationRequested_Throws_IfStateDataDoesNotExist()\n        {\n            _connection.Setup(x => x.GetStateData(It.IsAny<string>())).Returns((StateData)null);\n            var token = CreateToken();\n\n            Assert.Throws<JobAbortedException>(() => token.ThrowIfCancellationRequested());\n        }\n\n        [Fact]\n        public void ThrowIfCancellationRequested_ThrowsJobAborted_IfJobIsNotInProcessingState()\n        {\n            _stateData.Name = \"NotProcessing\";\n            var token = CreateToken();\n\n            Assert.Throws<JobAbortedException>(\n                () => token.ThrowIfCancellationRequested());\n        }\n\n        [Fact]\n        public void ThrowIfCancellationRequested_ThrowsJobAborted_IfServerIdWasChanged()\n        {\n            _stateData.Data[\"ServerId\"] = \"another-server\";\n            var token = CreateToken();\n\n            Assert.Throws<JobAbortedException>(\n                () => token.ThrowIfCancellationRequested());\n        }\n\n        [Fact]\n        public void ThrowIfCancellationRequested_ThrowsJobAborted_IfWorkerIdWasChanged()\n        {\n            _stateData.Data[\"WorkerId\"] = \"999\";\n            var token = CreateToken();\n\n            Assert.Throws<JobAbortedException>(\n                () => token.ThrowIfCancellationRequested());\n        }\n\n        [Fact]\n        public void CancellationToken_IsInitializedAsNotCancelled()\n        {\n            var token = CreateToken();\n            \n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n        }\n\n        [Fact]\n        public void CheckAllCancellationTokens_DoesNotAbortCancellationToken_IfNothingChanged()\n        {\n            var token = CreateToken();\n\n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n            ServerJobCancellationToken.CheckAllCancellationTokens(ServerId, _connection.Object, _cts.Token);\n\n            Assert.False(token.IsAborted);\n            token.ShutdownToken.ThrowIfCancellationRequested(); // does not throw\n        }\n\n        [Fact]\n        public void CheckAllCancellationTokens_AbortsCancellationToken_IfStateDataDoesNotExist()\n        {\n            _connection.Setup(x => x.GetStateData(It.IsAny<string>())).Returns((StateData)null);\n            var token = CreateToken();\n\n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n            ServerJobCancellationToken.CheckAllCancellationTokens(ServerId, _connection.Object, _cts.Token);\n\n            Assert.Throws<OperationCanceledException>(\n                () => token.ShutdownToken.ThrowIfCancellationRequested());\n            Assert.True(token.IsAborted);\n        }\n        \n        [Fact]\n        public void CheckAllCancellationTokens_AbortsCancellationToken_IfJobIsNotInProcessingState()\n        {\n            _stateData.Name = \"NotProcessing\";\n            var token = CreateToken();\n\n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n            ServerJobCancellationToken.CheckAllCancellationTokens(ServerId, _connection.Object, _cts.Token);\n\n            Assert.Throws<OperationCanceledException>(\n                () => token.ShutdownToken.ThrowIfCancellationRequested());\n            Assert.True(token.IsAborted);\n        }\n        \n        [Fact]\n        public void CheckAllCancellationTokens_AbortsCancellationToken_IfServerIdWasChanged()\n        {\n            _stateData.Data[\"ServerId\"] = \"another-server\";\n            var token = CreateToken();\n\n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n            ServerJobCancellationToken.CheckAllCancellationTokens(ServerId, _connection.Object, _cts.Token);\n\n            Assert.Throws<OperationCanceledException>(\n                () => token.ShutdownToken.ThrowIfCancellationRequested());\n            Assert.True(token.IsAborted);\n        }\n\n        [Fact]\n        public void CheckAllCancellationTokens_AbortsCancellationToken_IfWorkerIdWasChanged()\n        {\n            _stateData.Data[\"WorkerId\"] = \"999\";\n            var token = CreateToken();\n\n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n            ServerJobCancellationToken.CheckAllCancellationTokens(ServerId, _connection.Object, _cts.Token);\n\n            Assert.Throws<OperationCanceledException>(\n                () => token.ShutdownToken.ThrowIfCancellationRequested());\n            Assert.True(token.IsAborted);\n        }\n\n        [Fact]\n        public void CheckAllCancellationTokens_DoesNotAbortJobsFromOtherServers()\n        {\n            _stateData.Name = \"NotProcessing\";\n            var token = CreateToken();\n\n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n            ServerJobCancellationToken.CheckAllCancellationTokens(\"another-id\", _connection.Object, _cts.Token);\n\n            token.ShutdownToken.ThrowIfCancellationRequested();\n            Assert.False(token.IsAborted);\n        }\n\n        [Fact]\n        public void CheckAllCancellationTokens_DoesNotPerformChecks_WhenShutdownTokenWasNotInitialized()\n        {\n            _stateData.Name = \"NotProcessing\";\n            var token = CreateToken();\n\n            ServerJobCancellationToken.CheckAllCancellationTokens(ServerId, _connection.Object, _cts.Token);\n\n            Assert.False(token.IsAborted);\n            _connection.Verify(x => x.GetStateData(It.IsAny<string>()), Times.Never);\n        }\n\n        [Fact]\n        public void CheckAllCancellationTokens_DoesNotPerformChecks_WhenJobIsAlreadyAborted()\n        {\n            _stateData.Name = \"NotProcessing\";\n            var token = CreateToken();\n\n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n\n            ServerJobCancellationToken.CheckAllCancellationTokens(ServerId, _connection.Object, _cts.Token);\n            Assert.True(token.IsAborted);\n\n            ServerJobCancellationToken.CheckAllCancellationTokens(ServerId, _connection.Object, _cts.Token);\n\n            Assert.True(token.IsAborted);\n            _connection.Verify(x => x.GetStateData(It.IsAny<string>()), Times.Once);\n        }\n\n        [Fact]\n        public void CheckAllCancellationTokens_PerformsAdditionalChecks_WhenPriorOnesDidNotLeadToAbort()\n        {\n            var token = CreateToken();\n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n\n            ServerJobCancellationToken.CheckAllCancellationTokens(ServerId, _connection.Object, _cts.Token);\n            Assert.False(token.IsAborted);\n\n            _stateData.Name = \"NotProcessing\";\n            ServerJobCancellationToken.CheckAllCancellationTokens(ServerId, _connection.Object, _cts.Token);\n\n            Assert.True(token.IsAborted);\n            _connection.Verify(x => x.GetStateData(It.IsAny<string>()), Times.Exactly(2));\n        }\n\n        private ServerJobCancellationToken CreateToken(string serverId = null)\n        {\n            return new ServerJobCancellationToken(_connection.Object, JobId, serverId ?? ServerId, WorkerId, _shutdownCts.Token);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/ServerJobCancellationWatcherFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Threading;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class ServerJobCancellationWatcherFacts : IDisposable\n    {\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly BackgroundProcessContextMock _context;\n        private readonly TimeSpan _checkInterval;\n\n        public ServerJobCancellationWatcherFacts()\n        {\n            _checkInterval = Timeout.InfiniteTimeSpan;\n\n            _context = new BackgroundProcessContextMock();\n            _context.StoppingTokenSource.Cancel();\n\n            _connection = new Mock<IStorageConnection>();\n            _context.Storage.Setup(x => x.GetConnection()).Returns(_connection.Object);\n\n            ServerJobCancellationToken.AddServer(_context.ServerId);\n        }\n\n        public void Dispose()\n        {\n            ServerJobCancellationToken.RemoveServer(_context.ServerId);\n        }\n\n        [Fact]\n        public void Execute_DelegatesCancellationToServerJobCancellationToken()\n        {\n            var token = new ServerJobCancellationToken(_connection.Object, \"job-id\", _context.ServerId, \"1\", _context.StoppedTokenSource.Token);\n            Assert.False(token.ShutdownToken.IsCancellationRequested);\n\n            _connection.Setup(x => x.GetStateData(It.IsAny<string>())).Returns((StateData)null);\n            var watchdog = new ServerJobCancellationWatcher(_checkInterval);\n\n            Assert.Throws<OperationCanceledException>(() => watchdog.Execute(_context.Object));\n\n            _connection.Verify(x => x.GetStateData(\"job-id\"));\n            _connection.Verify(x => x.Dispose(), Times.Once);\n            Assert.True(token.IsAborted);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/ServerWatchdogFacts.cs",
    "content": "﻿using System;\nusing System.Threading;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class ServerWatchdogFacts\n    {\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly BackgroundProcessContextMock _context;\n        private readonly TimeSpan _checkInterval;\n        private readonly TimeSpan _serverTimeout;\n\n        public ServerWatchdogFacts()\n        {\n            _checkInterval = Timeout.InfiniteTimeSpan;\n            _serverTimeout = TimeSpan.FromSeconds(5);\n\n            _context = new BackgroundProcessContextMock();\n            _context.StoppingTokenSource.Cancel();\n\n            _connection = new Mock<IStorageConnection>();\n            _context.Storage.Setup(x => x.GetConnection()).Returns(_connection.Object);\n        }\n\n        [Fact]\n        public void Execute_DelegatesRemovalToStorageConnection()\n        {\n            _connection.Setup(x => x.RemoveTimedOutServers(It.IsAny<TimeSpan>())).Returns(1);\n            var watchdog = new ServerWatchdog(_checkInterval, _serverTimeout);\n\n            Assert.Throws<OperationCanceledException>(() => watchdog.Execute(_context.Object));\n\n            _connection.Verify(x => x.RemoveTimedOutServers(_serverTimeout));\n            _connection.Verify(x => x.Dispose(), Times.Once);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Server/WorkerFacts.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Moq.Sequences;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.Server\n{\n    public class WorkerFacts\n    {\n        private const string JobId = \"my-job\";\n\n        private readonly string[] _queues;\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly Mock<IBackgroundJobStateChanger> _stateChanger;\n        private readonly Mock<IFetchedJob> _fetchedJob;\n        private readonly Mock<IBackgroundJobPerformer> _performer;\n        private readonly BackgroundProcessContextMock _context;\n\n        public WorkerFacts()\n        {\n            _context = new BackgroundProcessContextMock();\n            _queues = new[] {\"critical\"};\n            _performer = new Mock<IBackgroundJobPerformer>();\n\n            _connection = new Mock<IStorageConnection>();\n            _context.Storage.Setup(x => x.GetConnection()).Returns(_connection.Object);\n\n            _fetchedJob = new Mock<IFetchedJob>();\n            _fetchedJob.Setup(x => x.JobId).Returns(JobId);\n\n            _connection\n                .Setup(x => x.FetchNextJob(_queues, It.IsNotNull<CancellationToken>()))\n                .Returns(_fetchedJob.Object);\n\n            _connection.Setup(x => x.GetJobData(JobId))\n                .Returns(new JobData\n                {\n                    Job = Job.FromExpression(() => Method()),\n                });\n\n            _stateChanger = new Mock<IBackgroundJobStateChanger>();\n            _stateChanger.Setup(x => x.ChangeState(It.IsAny<StateChangeContext>()))\n                .Returns<StateChangeContext>(ctx => ctx.NewState);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenQueuesCollectionNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new Worker(null, _performer.Object, _stateChanger.Object));\n\n            Assert.Equal(\"queues\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenPerformanceProcessIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new Worker(_queues, null, _stateChanger.Object));\n\n            Assert.Equal(\"performer\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStateChangeProcess_IsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new Worker(_queues, _performer.Object, null));\n\n            Assert.Equal(\"stateChanger\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Execute_TakesConnectionAndReleasesIt()\n        {\n            var worker = CreateWorker();\n\n            worker.Execute(_context.Object);\n\n            _context.Storage.Verify(x => x.GetConnection(), Times.Once);\n            _connection.Verify(x => x.Dispose(), Times.Once);\n        }\n\n        [Fact]\n        public void Execute_FetchesAJobAndRemovesItFromQueue()\n        {\n            var worker = CreateWorker();\n\n            worker.Execute(_context.Object);\n\n            _connection.Verify(\n                x => x.FetchNextJob(_queues, _context.StoppingTokenSource.Token),\n                Times.Once);\n\n            _fetchedJob.Verify(x => x.RemoveFromQueue());\n        }\n\n        [Fact]\n        public void Execute_RequeuesAJob_WhenThereWasAnException()\n        {\n            _stateChanger\n                .Setup(x => x.ChangeState(It.IsAny<StateChangeContext>()))\n                .Throws<InvalidOperationException>();\n\n            var worker = CreateWorker(1);\n\n            Assert.Throws<InvalidOperationException>(\n                () => worker.Execute(_context.Object));\n\n            _fetchedJob.Verify(x => x.RemoveFromQueue(), Times.Never);\n            _fetchedJob.Verify(x => x.Requeue());\n        }\n\n        [Fact]\n        public void Execute_MovesAJobToTheFailedState_WithFiltersDisabled_WhenStateChangerThrowsAnException()\n        {\n            _stateChanger\n                .Setup(x => x.ChangeState(It.Is<StateChangeContext>(y => y.NewState.Name != FailedState.StateName)))\n                .Throws<InvalidOperationException>();\n\n            var worker = CreateWorker(1);\n\n            worker.Execute(_context.Object);\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(y =>\n                y.NewState.Name == FailedState.StateName &&\n                y.DisableFilters == true)));\n\n            _fetchedJob.Verify(x => x.RemoveFromQueue(), Times.Once);\n            _fetchedJob.Verify(x => x.Requeue(), Times.Never);\n        }\n\n        [Fact, Sequence]\n        public void Execute_ExecutesDefaultWorkflow_WhenJobIsCorrect()\n        {\n            // Arrange\n            _stateChanger\n                .Setup(x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.BackgroundJobId == JobId && ctx.NewState is ProcessingState)))\n                .InSequence()\n                .Returns<StateChangeContext>(ctx => ctx.NewState);\n\n            _performer.Setup(x => x.Perform(It.IsAny<PerformContext>()))\n                .InSequence();\n\n            _stateChanger\n                .Setup(x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.BackgroundJobId == JobId && ctx.NewState is SucceededState)))\n                .InSequence()\n                .Returns<StateChangeContext>(context => context.NewState);\n\n            var worker = CreateWorker();\n\n            // Act\n            worker.Execute(_context.Object);\n\n            // Assert - see the `SequenceAttribute` class.\n        }\n\n        [Fact]\n        public void Execute_SetsCurrentServer_ToProcessingState()\n        {\n            var worker = CreateWorker();\n\n            worker.Execute(_context.Object);\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.NewState is ProcessingState && (((ProcessingState) ctx.NewState).ServerId == _context.ServerId))));\n        }\n\n        [Fact]\n        public void Execute_ProcessesOnlyJobs_InEnqueued_Scheduled_AndProcessingStates()\n        {\n            var worker = CreateWorker();\n\n            worker.Execute(_context.Object);\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.NewState is ProcessingState &&\n                ctx.ExpectedStates.ElementAt(0) == EnqueuedState.StateName &&\n                ctx.ExpectedStates.ElementAt(1) == ScheduledState.StateName &&\n                ctx.ExpectedStates.ElementAt(2) == ProcessingState.StateName &&\n                ctx.ExpectedStates.Count() == 3)));\n        }\n\n        [Fact]\n        public void Execute_DoesNotDisableFilters_DuringNormalOperation()\n        {\n            var worker = CreateWorker();\n\n            worker.Execute(_context.Object);\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.DisableFilters == false)));\n        }\n\n        [Fact]\n        public void Execute_DoesNotRun_PerformanceProcess_IfTransitionToProcessingStateFailed()\n        {\n            // Arrange\n            _stateChanger\n                .Setup(x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.NewState is ProcessingState)))\n                .Returns<IState>(null);\n\n            var worker = CreateWorker();\n\n            // Act\n            worker.Execute(_context.Object);\n\n            // Assert\n            _performer.Verify(x => x.Perform(It.IsAny<PerformContext>()), Times.Never);\n        }\n\n        [Fact]\n        public void Execute_Runs_PerformanceProcess()\n        {\n            var worker = CreateWorker();\n\n            worker.Execute(_context.Object);\n\n            _performer.Verify(x => x.Perform(It.Is<PerformContext>(ctx =>\n                ctx.BackgroundJob.Id == JobId &&\n                ctx.ServerId == _context.ServerId)));\n        }\n\n        [Fact]\n        public void Execute_DoesNotMoveAJob_ToTheFailedState_ButRequeuesIt_WhenProcessThrowsOperationCanceled_DuringShutdownOnly()\n        {\n            // Arrange\n            var cts = new CancellationTokenSource();\n            _context.StoppedTokenSource = cts;\n\n            _performer.Setup(x => x.Perform(It.IsAny<PerformContext>()))\n                .Callback(() => cts.Cancel())\n                .Throws<OperationCanceledException>();\n\n            var worker = CreateWorker();\n\n            // Act\n            Assert.Throws<OperationCanceledException>(() => worker.Execute(_context.Object));\n\n            // Assert\n            _stateChanger.Verify(\n                x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.NewState is FailedState)),\n                Times.Never);\n            _fetchedJob.Verify(x => x.Requeue());\n        }\n\n        [Fact]\n        public void Execute_MovesAJob_ToTheFailedState_AndNotRequeuesIt_WhenProcessThrowsOperationCanceled_WhenShutdownWasNotRequested()\n        {\n            // Arrange\n            _performer.Setup(x => x.Perform(It.IsAny<PerformContext>()))\n                .Throws<OperationCanceledException>();\n\n            var worker = CreateWorker();\n\n            // Act\n            worker.Execute(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(\n                x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.NewState is FailedState)),\n                Times.Once);\n            _fetchedJob.Verify(x => x.Requeue(), Times.Never);\n        }\n\n        [Fact]\n        public void Execute_DoesNotMoveAJobToFailedState_AndRemovesJobFromQueue_WhenProcessThrowsJobAbortedException()\n        {\n            // Arrange\n            _performer.Setup(x => x.Perform(It.IsAny<PerformContext>()))\n                .Throws<JobAbortedException>();\n\n            var worker = CreateWorker();\n\n            // Act\n            worker.Execute(_context.Object);\n\n            _stateChanger.Verify(\n                x => x.ChangeState(It.Is<StateChangeContext>(ctx => ctx.NewState is FailedState)),\n                Times.Never);\n            _fetchedJob.Verify(x => x.RemoveFromQueue());\n            _fetchedJob.Verify(x => x.Requeue(), Times.Never);\n        }\n\n        [Fact]\n        public void Execute_MovesJob_ToSuccessfulState_OnlyIfItIsInProcessingState()\n        {\n            var worker = CreateWorker();\n\n            worker.Execute(_context.Object);\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.NewState is SucceededState &&\n                ctx.ExpectedStates.ElementAt(0) == ProcessingState.StateName)));\n        }\n\n        [Fact]\n        public void Execute_PassesCustomData_BetweenContexts_OnSucceededStateTransition()\n        {\n            var worker = CreateWorker();\n            _performer.Setup(x => x.Perform(It.IsNotNull<PerformContext>()))\n                .Callback<PerformContext>(ctx => ctx.Items.Add(\"Key\", \"Value\"));\n\n            worker.Execute(_context.Object);\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.NewState is SucceededState &&\n                ctx.CustomData[\"Key\"].Equals(\"Value\"))));\n        }\n\n        [Fact]\n        public void Execute_MovesJob_ToFailedState_IfThereWasInternalException()\n        {\n            // Arrange\n            var exception = new InvalidOperationException();\n            _performer\n                .Setup(x => x.Perform(It.IsAny<PerformContext>()))\n                .Throws(exception);\n\n            var worker = CreateWorker();\n\n            // Act\n            worker.Execute(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.BackgroundJobId == JobId &&\n                ctx.NewState is FailedState &&\n                ((FailedState) ctx.NewState).Exception == exception &&\n                ctx.DisableFilters == false)));\n        }\n\n        [Fact]\n        public void Execute_DoesNotPassCustomData_BetweenContexts_OnFailedStateTransition()\n        {\n            var worker = CreateWorker();\n            _performer.Setup(x => x.Perform(It.IsNotNull<PerformContext>()))\n                .Callback<PerformContext>(ctx => ctx.Items.Add(\"Key\", \"Value\"))\n                .Throws<Exception>();\n\n            worker.Execute(_context.Object);\n\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.NewState is FailedState &&\n                ctx.CustomData == null)));\n        }\n\n        [Fact]\n        public void Execute_MovesJob_ToFailedState_IfThereWasUserException()\n        {\n            // Arrange\n            var exception = new InvalidOperationException();\n            _performer\n                .Setup(x => x.Perform(It.IsAny<PerformContext>()))\n                .Throws(new JobPerformanceException(\"hello\", exception));\n\n            var worker = CreateWorker();\n\n            // Act\n            worker.Execute(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.BackgroundJobId == JobId &&\n                ctx.NewState is FailedState &&\n                ctx.DisableFilters == false)));\n        }\n\n        [Fact]\n        public void Execute_MovesJob_ToFailedState_IfThereWasJobLoadException()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetJobData(JobId))\n                .Returns(new JobData { LoadException = new JobLoadException(\"asd\", new Exception()) });\n\n            var worker = CreateWorker();\n\n            // Act\n            worker.Execute(_context.Object);\n\n            // Assert\n            _stateChanger.Verify(x => x.ChangeState(It.Is<StateChangeContext>(ctx =>\n                ctx.NewState is FailedState &&\n                ctx.DisableFilters == false)));\n        }\n\n        private Worker CreateWorker(int maxStateChangeAttempts = 10)\n        {\n            return new Worker(_queues, _performer.Object, _stateChanger.Object, TimeSpan.FromSeconds(5), maxStateChangeAttempts);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Method() { }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/ApplyStateContextFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Hangfire.Profiling;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class ApplyStateContextFacts\n    {\n        private const string OldState = \"SomeState\";\n        private const string NewState = \"NewState\";\n\n        private readonly Mock<IState> _newState;\n        private readonly Mock<JobStorage> _storage;\n        private readonly BackgroundJobMock _backgroundJob;\n        private readonly Mock<IWriteOnlyTransaction> _transaction;\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly Mock<IProfiler> _profiler;\n        private readonly Mock<IStateMachine> _stateMachine;\n        private readonly Mock<IReadOnlyDictionary<string, object>> _customData;\n\n        public ApplyStateContextFacts()\n        {\n            _storage = new Mock<JobStorage>();\n            _connection = new Mock<IStorageConnection>();\n            _transaction = new Mock<IWriteOnlyTransaction>();\n            _backgroundJob = new BackgroundJobMock();\n            _newState = new Mock<IState>();\n            _newState.Setup(x => x.Name).Returns(NewState);\n            _profiler = new Mock<IProfiler>();\n            _stateMachine = new Mock<IStateMachine>();\n            _customData = new Mock<IReadOnlyDictionary<string, object>>();\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new ApplyStateContext(null, _connection.Object, _transaction.Object, _backgroundJob.Object, _newState.Object, OldState));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenConnectionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () =>\n                    new ApplyStateContext(_storage.Object, null, _transaction.Object, _backgroundJob.Object,\n                        _newState.Object, OldState));\n\n            Assert.Equal(\"connection\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenTransactionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () =>\n                    new ApplyStateContext(_storage.Object, _connection.Object, null, _backgroundJob.Object, _newState.Object, OldState));\n\n            Assert.Equal(\"transaction\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenBackgroundJobIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new ApplyStateContext(_storage.Object, _connection.Object, _transaction.Object, null, _newState.Object, OldState));\n\n            Assert.Equal(\"backgroundJob\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenNewStateIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new ApplyStateContext(_storage.Object, _connection.Object, _transaction.Object, _backgroundJob.Object, null, OldState));\n\n            Assert.Equal(\"newState\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenProfilerIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new ApplyStateContext(\n                    _storage.Object,\n                    _connection.Object,\n                    _transaction.Object,\n                    _backgroundJob.Object,\n                    _newState.Object,\n                    OldState,\n                    null,\n                    _stateMachine.Object));\n\n            Assert.Equal(\"profiler\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ShouldSetPropertiesCorrectly()\n        {\n            var context = new ApplyStateContext(\n                _storage.Object,\n                _connection.Object,\n                _transaction.Object,\n                _backgroundJob.Object,\n                _newState.Object,\n                OldState);\n\n            Assert.Same(_storage.Object, context.Storage);\n            Assert.Same(_connection.Object, context.Connection);\n            Assert.Same(_transaction.Object, context.Transaction);\n            Assert.Same(_backgroundJob.Object, context.BackgroundJob);\n            Assert.Equal(OldState, context.OldStateName);\n            Assert.Same(_newState.Object, context.NewState);\n            Assert.Equal(_storage.Object.JobExpirationTimeout, context.JobExpirationTimeout);\n        }\n\n        [Fact]\n        public void InternalCtor_CorrectlySetsAllTheProperties()\n        {\n            var context = new ApplyStateContext(\n                _storage.Object,\n                _connection.Object,\n                _transaction.Object,\n                _backgroundJob.Object,\n                _newState.Object,\n                OldState,\n                _profiler.Object,\n                _stateMachine.Object,\n                _customData.Object);\n\n            Assert.Same(_storage.Object, context.Storage);\n            Assert.Same(_connection.Object, context.Connection);\n            Assert.Same(_transaction.Object, context.Transaction);\n            Assert.Same(_backgroundJob.Object, context.BackgroundJob);\n            Assert.Equal(OldState, context.OldStateName);\n            Assert.Same(_newState.Object, context.NewState);\n            Assert.Equal(_storage.Object.JobExpirationTimeout, context.JobExpirationTimeout);\n            Assert.Same(_stateMachine.Object, context.StateMachine);\n            Assert.Same(_customData.Object, context.CustomData);\n        }\n\n        [Fact]\n        public void CopyCtor_ForElectStateContext_CorrectlySetsAllTheProperties()\n        {\n            var electContext = new ElectStateContextMock();\n            var context = new ApplyStateContext(_transaction.Object, electContext.Object);\n\n            Assert.Same(electContext.Object.Storage, context.Storage);\n            Assert.Same(electContext.Object.Connection, context.Connection);\n            Assert.Same(_transaction.Object, context.Transaction);\n            Assert.Same(electContext.Object.BackgroundJob, context.BackgroundJob);\n            Assert.Equal(electContext.Object.CurrentState, context.OldStateName);\n            Assert.Same(electContext.Object.CandidateState, context.NewState);\n            Assert.Null(context.CustomData);\n        }\n\n        [Fact]\n        public void CopyCtor_ForElectStateContext_CopiesCustomData_ToAnotherDictionary()\n        {\n            // Arrange\n            var dictionary = new Dictionary<string, object> { { \"lalala\", new object() } };\n            var electContext = new ElectStateContextMock { ApplyContext = { CustomData = dictionary } };\n\n            // Act\n            var context = new ApplyStateContext(_transaction.Object, electContext.Object);\n\n            // Assert\n            Assert.Equal(electContext.Object.CustomData, context.CustomData);\n            Assert.NotSame(electContext.Object.CustomData, context.CustomData);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/AwaitingStateFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class AwaitingStateFacts\n    {\n        [Fact]\n        public void StateName_IsEqualToAwaiting()\n        {\n            Assert.Equal(\"Awaiting\", AwaitingState.StateName);\n        }\n\n        [Fact]\n        public void NameProperty_ReturnsStateName()\n        {\n            var state = CreateState();\n            Assert.Equal(AwaitingState.StateName, state.Name);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void SerializeData_ReturnsCorrectData_Before170()\n        {\n            var state = CreateState();\n\n            var data = state.SerializeData();\n\n            Assert.Equal(state.ParentId, data[\"ParentId\"]);\n            Assert.Equal(\"{\\\"$type\\\":\\\"Hangfire.States.EnqueuedState, Hangfire.Core\\\",\\\"Queue\\\":\\\"default\\\",\\\"Reason\\\":null}\", data[\"NextState\"]);\n            Assert.Equal(state.Options.ToString(\"G\"), data[\"Options\"]);\n            Assert.Equal(state.Expiration.ToString(), data[\"Expiration\"]);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializeData_ReturnsCorrectData_After170()\n        {\n            var state = CreateState();\n\n            var data = state.SerializeData();\n\n            Assert.Equal(state.ParentId, data[\"ParentId\"]);\n            Assert.Equal(\"{\\\"$type\\\":\\\"Hangfire.States.EnqueuedState, Hangfire.Core\\\",\\\"Queue\\\":\\\"default\\\"}\", data[\"NextState\"]);\n            Assert.Equal(state.Options.ToString(\"D\"), data[\"Options\"]);\n            Assert.False(data.ContainsKey(\"Expiration\"));\n        }\n\n        [Fact]\n        public void IsFinal_ReturnsFalse()\n        {\n            var state = CreateState();\n            Assert.False(state.IsFinal);\n        }\n\n        [Fact]\n        public void IgnoreExceptions_ReturnsFalse()\n        {\n            var state = CreateState();\n            Assert.False(state.IgnoreJobLoadException);\n        }\n\n        [Fact, CleanSerializerSettings]\n        public void SerializeData_HandlesChangingProcessOfInternalDataSerialization()\n        {\n            SerializationHelper.SetUserSerializerSettings(SerializerSettingsHelper.DangerousSettings);\n\n            var nextStateSerialized = SerializationHelper.Serialize(new EnqueuedState(), SerializationOption.User);\n\n            var nextState = SerializationHelper.Deserialize<IState>(nextStateSerialized, SerializationOption.TypedInternal) as EnqueuedState;\n            Assert.NotNull(nextState);\n            Assert.NotEqual(default(DateTime), nextState.EnqueuedAt);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_Before170()\n        {\n            var state = new AwaitingState(\"parent\");\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.AwaitingState, Hangfire.Core\\\",\\\"ParentId\\\":\\\"parent\\\",\\\"NextState\\\":{\\\"$type\\\":\\\"Hangfire.States.EnqueuedState, Hangfire.Core\\\",\\\"Queue\\\":\\\"default\\\",\\\"Reason\\\":null},\\\"Options\\\":0,\\\"Reason\\\":null}\",\n                serialized);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_After170()\n        {\n            var state = new AwaitingState(\"parent\");\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.AwaitingState, Hangfire.Core\\\",\\\"ParentId\\\":\\\"parent\\\",\\\"NextState\\\":{\\\"$type\\\":\\\"Hangfire.States.EnqueuedState, Hangfire.Core\\\",\\\"Queue\\\":\\\"default\\\"}}\",\n                serialized);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void JsonDeserialize_CanHandlePreviousFormat()\n        {\n            var json = \"{\\\"$type\\\":\\\"Hangfire.States.AwaitingState, Hangfire.Core\\\",\\\"ParentId\\\":\\\"parent\\\",\\\"NextState\\\":{\\\"$type\\\":\\\"Hangfire.States.EnqueuedState, Hangfire.Core\\\",\\\"Queue\\\":\\\"default\\\"},\\\"Options\\\":1,\\\"Name\\\":\\\"Awaiting\\\"}\";\n            var state = SerializationHelper.Deserialize<AwaitingState>(json, SerializationOption.TypedInternal);\n\n            Assert.Equal(\"parent\", state.ParentId);\n            Assert.Equal(\"Enqueued\", state.NextState.Name);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void JsonDeserialize_CanHandleNewFormat()\n        {\n            var json = \"{\\\"$type\\\":\\\"Hangfire.States.AwaitingState, Hangfire.Core\\\",\\\"ParentId\\\":\\\"parent\\\",\\\"NextState\\\":{\\\"$type\\\":\\\"Hangfire.States.EnqueuedState, Hangfire.Core\\\",\\\"Queue\\\":\\\"default\\\"}}\";\n            var state = SerializationHelper.Deserialize<AwaitingState>(json, SerializationOption.TypedInternal);\n\n            Assert.Equal(\"parent\", state.ParentId);\n            Assert.Null(state.Reason);\n            Assert.Equal(\"Enqueued\", state.NextState.Name);\n        }\n\n        private static AwaitingState CreateState()\n        {\n            return new AwaitingState(\"1\", new EnqueuedState(), JobContinuationOptions.OnlyOnSucceededState, TimeSpan.FromDays(1));\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/AwaitingStateHandlerFacts.cs",
    "content": "﻿using Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class AwaitingStateHandlerFacts\n    {\n        private readonly ApplyStateContextMock _context;\n        private readonly Mock<IWriteOnlyTransaction> _transactionMock\n            = new Mock<IWriteOnlyTransaction>();\n\n        public AwaitingStateHandlerFacts()\n        {\n            _context = new ApplyStateContextMock();\n        }\n\n        [Fact]\n        public void ShouldWorkOnlyWithAwaitingState()\n        {\n            var handler = new AwaitingState.Handler();\n            Assert.Equal(AwaitingState.StateName, handler.StateName);\n        }\n\n        [Fact]\n        public void Apply_ShouldAddToSet_Awaiting()\n        {\n            var handler = new AwaitingState.Handler();\n            handler.Apply(_context.Object, _transactionMock.Object);\n\n            _transactionMock.Verify(x => x.AddToSet(\"awaiting\", \"JobId\", It.IsAny<double>()), Times.Once);\n        }\n\n        [Fact]\n        public void Unapply_ShouldRemoveFromSet_Awaiting()\n        {\n            var handler = new AwaitingState.Handler();\n            handler.Unapply(_context.Object, _transactionMock.Object);\n\n            _transactionMock.Verify(x => x.RemoveFromSet(\"awaiting\", \"JobId\"), Times.Once);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/BackgroundJobStateChangerFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class BackgroundJobStateChangerFacts\n    {\n        private const string StateName = \"State\";\n        private const string JobId = \"1\";\n        private const string OldStateName = \"Old\";\n        private static readonly string[] FromOldState = { OldStateName };\n\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly Job _job;\n        private readonly Mock<IState> _state;\n        private readonly Mock<IJobFilterProvider> _filterProvider;\n        private readonly Mock<IStateMachine> _stateMachine;\n        private readonly Mock<IDisposable> _distributedLock;\n        private readonly Mock<IWriteOnlyTransaction> _transaction;\n        private readonly CancellationTokenSource _cts;\n        private readonly StateChangeContextMock _context;\n\n        public BackgroundJobStateChangerFacts()\n        {\n            _stateMachine = new Mock<IStateMachine>();\n            _filterProvider = new Mock<IJobFilterProvider>();\n            _filterProvider.Setup(x => x.GetFilters(It.IsAny<Job>())).Returns(Enumerable.Empty<JobFilter>());\n\n            _job = Job.FromExpression(() => Console.WriteLine());\n            _state = new Mock<IState>();\n            _state.Setup(x => x.Name).Returns(StateName);\n            \n            _connection = new Mock<IStorageConnection>();\n            _transaction = new Mock<IWriteOnlyTransaction>();\n\n            _connection.Setup(x => x.CreateWriteTransaction()).Returns(_transaction.Object);\n\n            _connection.Setup(x => x.CreateExpiredJob(\n                It.IsAny<Job>(),\n                It.IsAny<IDictionary<string, string>>(),\n                It.IsAny<DateTime>(),\n                It.IsAny<TimeSpan>())).Returns(JobId);\n\n            _connection.Setup(x => x.GetJobData(JobId))\n                .Returns(new JobData\n                {\n                    State = OldStateName,\n                    Job = _job\n                });\n\n            _distributedLock = new Mock<IDisposable>();\n            _connection\n                .Setup(x => x.AcquireDistributedLock($\"job:{JobId}:state-lock\", It.IsAny<TimeSpan>()))\n                .Returns(_distributedLock.Object);\n\n            _cts = new CancellationTokenSource(TimeSpan.FromSeconds(1));\n            _context = new StateChangeContextMock\n            {\n                BackgroundJobId = JobId,\n                Connection = _connection,\n                CancellationToken = _cts.Token,\n                NewState = _state,\n                ExpectedStates = FromOldState,\n                CustomData = new Dictionary<string, object>\n                {\n                    { \"Key\", \"Value\" }\n                }\n            };\n\n            _stateMachine.Setup(x => x.ApplyState(It.IsNotNull<ApplyStateContext>()))\n                .Returns(_context.NewState.Object);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenFilterProviderIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobStateChanger((IJobFilterProvider)null));\n\n            Assert.Equal(\"filterProvider\", exception.ParamName);\n        }\n        \n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStateMachineNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new BackgroundJobStateChanger(_filterProvider.Object, null));\n\n            Assert.Equal(\"stateMachine\", exception.ParamName);\n        }\n        \n        [Fact]\n        public void ChangeState_WorksWithinAJobLock()\n        {\n            var stateChanger = CreateStateChanger();\n\n            stateChanger.ChangeState(_context.Object);\n\n            _distributedLock.Verify(x => x.Dispose());\n        }\n\n        [Fact]\n        public void TryToChangeState_ChangesTheStateOfTheJob()\n        {\n            // Arrange\n            var stateChanger = CreateStateChanger();\n\n            // Act\n            var result = stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(\n                It.Is<ApplyStateContext>(sc => sc.BackgroundJob.Id == JobId && sc.BackgroundJob.Job.Type.Name.Equals(\"Console\")\n                    && sc.NewState == _state.Object && sc.OldStateName == OldStateName)));\n\n            Assert.NotNull(result);\n            Assert.Equal(_state.Object.Name, result.Name);\n        }\n\n        [Fact]\n        public void ChangeState_PassesCustomData_ToApplyStateContext()\n        {\n            var stateChanger = CreateStateChanger();\n\n            stateChanger.ChangeState(_context.Object);\n\n            _stateMachine.Verify(x => x.ApplyState(It.Is<ApplyStateContext>(\n                sc => sc.CustomData[\"Key\"].Equals(\"Value\"))));\n        }\n\n        [Fact]\n        public void ChangeState_ChangesTheStateOfTheJob_WhenFromStatesIsNull()\n        {\n            // Arrange\n            var stateChanger = CreateStateChanger();\n            _context.ExpectedStates = null;\n\n            // Act\n            stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(\n                It.Is<ApplyStateContext>(ctx => ctx.NewState == _state.Object && ctx.OldStateName == OldStateName)));\n        }\n\n        [Fact]\n        public void ChangeState_ReturnsNull_WhenJobIsNotFound()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetJobData(It.IsAny<string>()))\n                .Returns((JobData)null);\n\n            var stateChanger = CreateStateChanger();\n\n            // Act\n            var result = stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            Assert.Null(result);\n            _connection.Verify(x => x.GetJobData(JobId));\n\n            _stateMachine.Verify(\n                x => x.ApplyState(It.IsAny<ApplyStateContext>()),\n                Times.Never);\n        }\n\n        [Fact]\n        public void ChangeState_DoesNotDoAnything_WhenStateIsNull_AndCancellationTokenIsCancelled()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetJobData(It.IsAny<string>())).Returns(new JobData\n            {\n                Job = _job,\n                State = null\n            });\n\n            var stateChanger = CreateStateChanger();\n            _cts.Cancel();\n\n            // Act\n            var result = stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            Assert.Null(result);\n\n            _stateMachine.Verify(\n                x => x.ApplyState(It.IsAny<ApplyStateContext>()),\n                Times.Never);\n        }\n\n        [Fact]\n        public void ChangeState_WaitsFor_NonNullJobDataAndStateValue()\n        {\n            // Arrange\n            var results = new Queue<JobData>();\n            results.Enqueue(null);\n            results.Enqueue(new JobData { Job = _job, State = null });\n            results.Enqueue(new JobData { Job = _job, State = OldStateName });\n\n            _connection.Setup(x => x.GetJobData(It.IsAny<string>()))\n                .Returns(results.Dequeue);\n\n            var stateChanger = CreateStateChanger();\n\n            // Act\n            var result = stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            Assert.Empty(results);\n            Assert.NotNull(result);\n            Assert.Equal(_state.Object.Name, result.Name);\n        }\n\n        [Fact]\n        public void ChangeState_ReturnsNull_WhenFromStatesArgumentDoesNotContainCurrentState()\n        {\n            // Arrange\n            var stateChanger = CreateStateChanger();\n            _context.ExpectedStates = new[] { \"AnotherState\" };\n\n            // Act\n            var result = stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            Assert.Null(result);\n\n            _stateMachine.Verify(\n                x => x.ApplyState(It.IsAny<ApplyStateContext>()),\n                Times.Never);\n        }\n\n        [Fact]\n        public void ChangeState_ThrowsAnException_WhenApplyStateThrowsException()\n        {\n            // Arrange\n            _stateMachine.Setup(x => x.ApplyState(It.IsAny<ApplyStateContext>()))\n                .Throws(new FieldAccessException());\n\n            var stateChanger = CreateStateChanger();\n\n            // Act & Assert\n            Assert.Throws<FieldAccessException>(\n                () => stateChanger.ChangeState(_context.Object));\n        }\n\n        [Fact]\n        public void ChangeState_MoveJobToTheFailedState_IfMethodDataCouldNotBeResolved()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetJobData(JobId))\n                .Returns(new JobData\n                {\n                    State = OldStateName,\n                    Job = null,\n                    LoadException = new JobLoadException(\"asd\", new InvalidOperationException())\n                });\n\n            var stateChanger = CreateStateChanger();\n\n            // Act\n            stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(\n                It.Is<ApplyStateContext>(ctx => ctx.BackgroundJob.Id == JobId && \n                ctx.BackgroundJob.Job == null && ctx.NewState is FailedState)));\n        }\n\n        [Fact]\n        public void ChangeState_MoveJobToTheGivenState_IfStateIgnoresThisException_AndMethodDataCouldNotBeResolved()\n        {\n            // Arrange\n            _connection.Setup(x => x.GetJobData(JobId))\n                .Returns(new JobData\n                {\n                    State = OldStateName,\n                    Job = null,\n                    LoadException = new JobLoadException(\"asd\", new Exception())\n                });\n\n            _state.Setup(x => x.IgnoreJobLoadException).Returns(true);\n\n            var stateChanger = CreateStateChanger();\n\n            // Act\n            var result = stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(\n                It.Is<ApplyStateContext>(ctx => ctx.NewState == _state.Object)));\n\n            Assert.NotNull(result);\n            Assert.Equal(_state.Object.Name, result.Name);\n        }\n\n        [Fact]\n        public void ChangeState_CommitsTheNewState_AndReturnsAppliedState()\n        {\n            // Arrange\n            var stateChanger = CreateStateChanger();\n            _context.ExpectedStates = new[] { OldStateName };\n\n            // Act\n            var result = stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            _stateMachine.Verify(x => x.ApplyState(\n                It.Is<ApplyStateContext>(ctx => ctx.NewState == _state.Object && ctx.OldStateName == OldStateName\n                    && ctx.BackgroundJob.Job == _job && ctx.BackgroundJob.Id == JobId)));\n\n            _transaction.Verify(x => x.Commit());\n\n            Assert.NotNull(result);\n            Assert.Equal(_state.Object.Name, result.Name);\n        }\n\n        [Fact]\n        public void ChangeState_ReturnsState_ReturnedByAStateMachine()\n        {\n            // Arrange\n            var anotherState = new Mock<IState>();\n\n            _stateMachine.Setup(x => x.ApplyState(It.IsNotNull<ApplyStateContext>()))\n                .Returns(anotherState.Object);\n\n            var stateChanger = CreateStateChanger();\n\n            // Act\n            var result = stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            Assert.Same(result, anotherState.Object);\n        }\n\n        [Fact]\n        public void ChangeState_RethrowsFilterException_AndDoesNotCommitAnything_WhenNoCancellationToken()\n        {\n            // Arrange\n            _stateMachine\n                .Setup(x => x.ApplyState(It.Is<ApplyStateContext>(context => context.NewState == _state.Object)))\n                .Throws<Exception>();\n\n            var stateChanger = CreateStateChanger();\n\n            // Act & Assert\n            Assert.Throws<Exception>(() => stateChanger.ChangeState(_context.Object));\n\n            _transaction.Verify(x => x.Commit(), Times.Never);\n\n            _stateMachine.Verify(\n                x => x.ApplyState(It.Is<ApplyStateContext>(context => context.NewState == _state.Object)),\n                Times.Once);\n        }\n\n        [Fact]\n        public void ChangeState_SimplyRethrowsAnException_WithoutRetriesAndFailedState()\n        {\n            // Arrange\n            _stateMachine\n                .Setup(x => x.ApplyState(It.Is<ApplyStateContext>(context => context.NewState == _state.Object)))\n                .Throws<Exception>();\n\n            var stateChanger = CreateStateChanger();\n\n            // Act & Assert\n            Assert.Throws<Exception>(() => stateChanger.ChangeState(_context.Object));\n\n            _connection.Verify(x => x.GetJobData(JobId), Times.Once);\n            _connection.Verify(x => x.AcquireDistributedLock($\"job:{JobId}:state-lock\", It.IsAny<TimeSpan>()), Times.Once);\n\n            _transaction.Verify(x => x.Commit(), Times.Never);\n\n            _stateMachine.Verify(\n                x => x.ApplyState(It.Is<ApplyStateContext>(context => context.NewState == _state.Object)),\n                Times.Once);\n        }\n\n        [Fact]\n        public void ChangeState_DoesNotApplyAnything_AndRethrowsExceptions_ThrownByElectStateFilters()\n        {\n            // Arrange\n            var electStateFilter = new Mock<IElectStateFilter>();\n\n            electStateFilter\n                .Setup(x => x.OnStateElection(It.IsAny<ElectStateContext>()))\n                .Throws<NotSupportedException>();\n\n            _filterProvider\n                .Setup(x => x.GetFilters(It.IsAny<Job>()))\n                .Returns(new [] { new JobFilter(electStateFilter.Object, JobFilterScope.Method, 0) });\n\n            var stateChanger = CreateStateChanger();\n\n            // Act\n            Assert.Throws<NotSupportedException>(() => stateChanger.ChangeState(_context.Object));\n\n            // Assert\n            electStateFilter.Verify(x => x.OnStateElection(It.IsNotNull<ElectStateContext>()), Times.Once);\n            _transaction.Verify(x => x.Commit(), Times.Never);\n        }\n\n        [Fact]\n        public void ChangeState_DoesNotApplyAnything_AndRethrowsAndException_ThrownByApplyStateFilters()\n        {\n            // Arrange\n            var applyStateFilter = new Mock<IApplyStateFilter>();\n\n            applyStateFilter\n                .Setup(x => x.OnStateApplied(It.IsAny<ApplyStateContext>(), It.IsAny<IWriteOnlyTransaction>()))\n                .Throws<NotSupportedException>();\n\n            _filterProvider\n                .Setup(x => x.GetFilters(It.IsAny<Job>()))\n                .Returns(new [] { new JobFilter(applyStateFilter.Object, JobFilterScope.Method, 0) });\n\n            var stateChanger = CreateStateChanger();\n\n            // Act\n            Assert.Throws<NotSupportedException>(() => stateChanger.ChangeState(_context.Object));\n\n            // Assert\n            applyStateFilter.Verify(\n                x => x.OnStateApplied(It.IsNotNull<ApplyStateContext>(), It.IsNotNull<IWriteOnlyTransaction>()),\n                Times.Once);\n\n            _transaction.Verify(x => x.Commit(), Times.Never);\n        }\n\n        [Fact]\n        public void ChangeState_DoesNotInvokeElectStateFilters_WhenFiltersDisabled()\n        {\n            // Arrange\n            var electStateFilter = new Mock<IElectStateFilter>();\n\n            electStateFilter\n                .Setup(x => x.OnStateElection(It.IsAny<ElectStateContext>()))\n                .Throws<NotSupportedException>();\n\n            _filterProvider\n                .Setup(x => x.GetFilters(It.IsAny<Job>()))\n                .Returns(new [] { new JobFilter(electStateFilter.Object, JobFilterScope.Method, 0) });\n\n            _context.DisableFilters = true;\n\n            var stateChanger = CreateStateChanger();\n\n            // Act\n            stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            electStateFilter.Verify(\n                x => x.OnStateElection(It.IsAny<ElectStateContext>()),\n                Times.Never);\n\n            _stateMachine.Verify(x => x.ApplyState(It.IsNotNull<ApplyStateContext>()));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void ChangeState_DoesNotInvokeApplyStateFilters_WhenFiltersDisabled()\n        {\n            // Arrange\n            var applyStateFilter = new Mock<IApplyStateFilter>();\n\n            applyStateFilter\n                .Setup(x => x.OnStateApplied(It.IsAny<ApplyStateContext>(), It.IsAny<IWriteOnlyTransaction>()))\n                .Throws<NotSupportedException>();\n\n            _filterProvider\n                .Setup(x => x.GetFilters(It.IsAny<Job>()))\n                .Returns(new [] { new JobFilter(applyStateFilter.Object, JobFilterScope.Method, 0) });\n\n            _context.DisableFilters = true;\n\n            var stateChanger = CreateStateChanger();\n\n            // Act\n            stateChanger.ChangeState(_context.Object);\n\n            // Assert\n            applyStateFilter.Verify(\n                x => x.OnStateApplied(It.IsAny<ApplyStateContext>(), It.IsAny<IWriteOnlyTransaction>()),\n                Times.Never);\n\n            _stateMachine.Verify(x => x.ApplyState(It.IsNotNull<ApplyStateContext>()));\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void ChangeState_WithTransaction_PassesTheGivenTransaction_AndDoesNotCommitTheImplicitOne()\n        {\n            _context.Transaction = new Mock<JobStorageTransaction>();\n            var stateChanger = CreateStateChanger();\n\n            stateChanger.ChangeState(_context.Object);\n\n            _stateMachine.Verify(x => x.ApplyState(It.Is<ApplyStateContext>(\n                ctx => ctx.Transaction == _context.Transaction.Object)));\n\n            _connection.Verify(x => x.CreateWriteTransaction(), Times.Never);\n            _transaction.Verify(x => x.Commit(), Times.Never);\n        }\n\n        [Fact]\n        public void ChangeState_WithTransaction_DoesNotCommitAndDoesNotDisposeTheExplicitTransaction()\n        {\n            _context.Transaction = new Mock<JobStorageTransaction>();\n            var stateChanger = CreateStateChanger();\n\n            stateChanger.ChangeState(_context.Object);\n\n            _context.Transaction.Verify(x => x.Commit(), Times.Never);\n            _context.Transaction.Verify(x => x.Dispose(), Times.Never);\n        }\n\n        [Fact]\n        public void ChangeState_WithTransaction_AcquiresATransactionLevelLockInstead()\n        {\n            _context.Transaction = new Mock<JobStorageTransaction>();\n            var stateChanger = CreateStateChanger();\n\n            stateChanger.ChangeState(_context.Object);\n\n            _context.Transaction.Verify(x => x.AcquireDistributedLock($\"job:{JobId}:state-lock\", It.IsAny<TimeSpan>()));\n            _connection.Verify(x => x.AcquireDistributedLock(It.IsAny<string>(), It.IsAny<TimeSpan>()), Times.Never);\n        }\n\n        private BackgroundJobStateChanger CreateStateChanger()\n        {\n            return new BackgroundJobStateChanger(_filterProvider.Object, _stateMachine.Object);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/CoreStateMachineFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Moq.Sequences;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class CoreStateMachineFacts\n    {\n        private const string OldStateName = \"OldState\";\n        private const string StateName = \"State\";\n        private const string JobId = \"job\";\n\n        private readonly Dictionary<string, List<IStateHandler>> _handlers = new Dictionary<string, List<IStateHandler>>();\n        private readonly Func<JobStorage, string, CoreStateMachine.StateHandlersCollection> _stateHandlersThunk;\n        \n        private readonly ApplyStateContextMock _applyContext;\n\n        public CoreStateMachineFacts()\n        {\n            _stateHandlersThunk = (storage, stateName) => new CoreStateMachine.StateHandlersCollection(\n                _handlers.TryGetValue(stateName, out var handlers) ? handlers.ToList() : new List<IStateHandler>(),\n                Enumerable.Empty<IStateHandler>(),\n                stateName);\n\n            var backgroundJob = new BackgroundJobMock { Id = JobId };\n            _applyContext = new ApplyStateContextMock\n            {\n                BackgroundJob = backgroundJob,\n                OldStateName = OldStateName\n            };\n\n            _applyContext.NewState.Setup(x => x.Name).Returns(StateName);\n        }\n\n        [Fact]\n        public void ApplyState_SetsTheNewState_ForABackgroundJob()\n        {\n            var stateMachine = CreateStateMachine();\n\n            stateMachine.ApplyState(_applyContext.Object);\n\n            _applyContext.Transaction.Verify(x => x.SetJobState(JobId, _applyContext.NewState.Object));\n        }\n\n        [Fact]\n        public void ApplyState_SetsJobExpiration_IfStateIsFinal()\n        {\n            _applyContext.NewState.Setup(x => x.IsFinal).Returns(true);\n            var stateMachine = CreateStateMachine();\n\n            stateMachine.ApplyState(_applyContext.Object);\n\n            _applyContext.Transaction.Verify(x => x.ExpireJob(JobId, _applyContext.JobExpirationTimeout));\n        }\n\n        [Fact]\n        public void ApplyState_PersistTheJob_IfStateIsNotFinal()\n        {\n            _applyContext.NewState.Setup(x => x.IsFinal).Returns(false);\n            var stateMachine = CreateStateMachine();\n\n            stateMachine.ApplyState(_applyContext.Object);\n\n            _applyContext.Transaction.Verify(x => x.PersistJob(JobId));\n        }\n\n        [Fact, Sequence]\n        public void ApplyState_CallsUnapplyHandlers_BeforeSettingTheState()\n        {\n            // Arrange\n            var handler1 = CreateStateHandler(OldStateName);\n            var handler2 = CreateStateHandler(OldStateName);\n\n            handler1\n                .Setup(x => x.Unapply(_applyContext.Object, _applyContext.Transaction.Object))\n                .InSequence();\n\n            handler2\n                .Setup(x => x.Unapply(_applyContext.Object, _applyContext.Transaction.Object))\n                .InSequence();\n\n            var stateMachine = CreateStateMachine();\n\n            // Act\n            stateMachine.ApplyState(_applyContext.Object);\n\n            // Assert - Sequence\n        }\n\n        [Fact]\n        public void ApplyState_DoesNotCallUnapplyHandlers_ForDifferentStates()\n        {\n            // Arrange\n            var handler = CreateStateHandler(StateName);\n            var stateMachine = CreateStateMachine();\n\n            // Act\n            stateMachine.ApplyState(_applyContext.Object);\n\n            // Assert\n            handler.Verify(\n                x => x.Unapply(It.IsAny<ApplyStateContext>(), It.IsAny<IWriteOnlyTransaction>()),\n                Times.Never);\n        }\n\n        [Fact, Sequence]\n        public void ApplyState_ShouldCallApplyHandlers_AfterSettingTheState()\n        {\n            // Arrange\n            var handler1 = CreateStateHandler(StateName);\n            var handler2 = CreateStateHandler(StateName);\n\n            _applyContext.Transaction\n                .Setup(x => x.SetJobState(It.IsAny<string>(), It.IsAny<IState>()))\n                .InSequence();\n\n            handler1.Setup(x => x.Apply(_applyContext.Object, _applyContext.Transaction.Object))\n                .InSequence();\n            handler2.Setup(x => x.Apply(_applyContext.Object, _applyContext.Transaction.Object))\n                .InSequence();\n\n            var stateMachine = CreateStateMachine();\n\n            // Act\n            stateMachine.ApplyState(_applyContext.Object);\n\n            // Assert - Sequence\n        }\n\n        [Fact]\n        public void ApplyState_DoesNotCallApplyHandlers_ForDifferentStates()\n        {\n            // Arrange\n            var handler = CreateStateHandler(OldStateName);\n            var stateMachine = CreateStateMachine();\n\n            // Act\n            stateMachine.ApplyState(_applyContext.Object);\n\n            // Assert\n            handler.Verify(\n                x => x.Apply(It.IsAny<ApplyStateContext>(), It.IsAny<IWriteOnlyTransaction>()),\n                Times.Never);\n        }\n\n        private CoreStateMachine CreateStateMachine()\n        {\n            return new CoreStateMachine(_stateHandlersThunk);\n        }\n\n        private Mock<IStateHandler> CreateStateHandler(string stateName)\n        {\n            var handler = new Mock<IStateHandler>();\n            handler.Setup(x => x.StateName).Returns(stateName);\n\n            if (!_handlers.ContainsKey(stateName)) _handlers.Add(stateName, new List<IStateHandler>());\n            _handlers[stateName].Add(handler.Object);\n            return handler;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/DeletedStateFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class DeletedStateFacts\n    {\n        [Fact]\n        public void StateName_ReturnsDeleted()\n        {\n            var result = DeletedState.StateName;\n            Assert.Equal(\"Deleted\", result);\n        }\n\n        [Fact]\n        public void NameProperty_ReturnsStateName()\n        {\n            var state = CreateState();\n\n            var result = state.Name;\n\n            Assert.Equal(DeletedState.StateName, result);\n        }\n\n        [Fact]\n        public void IsFinalProperty_ReturnsTrue()\n        {\n            var state = CreateState();\n\n            var result = state.IsFinal;\n\n            Assert.True(result);\n        }\n\n        [Fact]\n        public void IgnoreExceptions_ReturnsTrue()\n        {\n            var state = CreateState();\n\n            var result = state.IgnoreJobLoadException;\n\n            Assert.True(result);\n        }\n\n        [Fact]\n        public void DeletedAtProperty_ReturnsCurrentUtcDate()\n        {\n            var state = CreateState();\n\n            Assert.True(DateTime.UtcNow.AddMinutes(-1) < state.DeletedAt);\n            Assert.True(state.DeletedAt < DateTime.UtcNow.AddMinutes(1));\n        }\n\n        [Fact]\n        public void SerializeData_ReturnsSerializedStateData()\n        {\n            var state = CreateState();\n\n            var data = state.SerializeData();\n\n            Assert.Single(data);\n            Assert.True(JobHelper.DeserializeDateTime(data[\"DeletedAt\"]) != default(DateTime));\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_Before170()\n        {\n            var state = new DeletedState();\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.DeletedState, Hangfire.Core\\\",\\\"Reason\\\":null}\",\n                serialized);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_After170()\n        {\n            var state = new DeletedState();\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.DeletedState, Hangfire.Core\\\"}\",\n                serialized);\n        }\n\n        private static DeletedState CreateState()\n        {\n            return new DeletedState();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/DeletedStateHandlerFacts.cs",
    "content": "﻿using Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class DeletedStateHandlerFacts\n    {\n        private readonly ApplyStateContextMock _context;\n        private readonly Mock<IWriteOnlyTransaction> _transactionMock\n            = new Mock<IWriteOnlyTransaction>();\n\n        public DeletedStateHandlerFacts()\n        {\n            _context = new ApplyStateContextMock();\n        }\n\n        [Fact]\n        public void ShouldWorkOnlyWithDeletedState()\n        {\n            var handler = new DeletedState.Handler();\n            Assert.Equal(DeletedState.StateName, handler.StateName);\n        }\n\n        [Fact]\n        public void Apply_ShouldIncrease_DeletedCounter()\n        {\n            var handler = new DeletedState.Handler();\n            handler.Apply(_context.Object, _transactionMock.Object);\n\n            _transactionMock.Verify(x => x.IncrementCounter(\"stats:deleted\"), Times.Once);\n        }\n\n        [Fact]\n        public void Unapply_ShouldDecrementStatistics()\n        {\n            var handler = new DeletedState.Handler();\n            handler.Unapply(_context.Object, _transactionMock.Object);\n\n            _transactionMock.Verify(x => x.DecrementCounter(\"stats:deleted\"), Times.Once);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/ElectStateContextFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Moq;\nusing Xunit;\n#pragma warning disable 618\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class ElectStateContextFacts\n    {\n        private readonly ApplyStateContextMock _applyContext;\n\n        public ElectStateContextFacts()\n        {\n            _applyContext = new ApplyStateContextMock { OldStateName = \"State\" };\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenApplyContextIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new ElectStateContext(null));\n\n            Assert.Equal(\"applyContext\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_CorrectlySetAllProperties()\n        {\n            var context = CreateContext();\n            \n            Assert.Same(_applyContext.Connection.Object, context.Connection);\n            Assert.Same(_applyContext.Transaction.Object, context.Transaction);\n            Assert.Same(_applyContext.BackgroundJob.Object, context.BackgroundJob);\n            Assert.Same(_applyContext.NewState.Object, context.CandidateState);\n            Assert.Equal(\"State\", context.CurrentState);\n            Assert.Empty(context.TraversedStates);\n            Assert.Null(context.CustomData);\n        }\n\n        [Fact]\n        public void Ctor_CopiesTheCustomDataDictionary_ToAnotherInstance()\n        {\n            // Arrange\n            _applyContext.CustomData = new Dictionary<string, object>\n            {\n                { \"lalala\", new object() }\n            };\n\n            // Act\n            var context = CreateContext();\n\n            // Assert\n            Assert.Equal(_applyContext.CustomData, context.CustomData);\n            Assert.NotSame(_applyContext.CustomData, context.CustomData);\n        }\n\n        [Fact]\n        public void SetCandidateState_ThrowsAnException_WhenValueIsNull()\n        {\n            var context = CreateContext();\n\n            Assert.Throws<ArgumentNullException>(() => context.CandidateState = null);\n        }\n\n        [Fact]\n        public void SetCandidateState_SetsTheGivenValue()\n        {\n            var context = CreateContext();\n            var newState = new Mock<IState>();\n\n            context.CandidateState = newState.Object;\n\n            Assert.Same(newState.Object, context.CandidateState);\n        }\n\n        [Fact]\n        public void SetCandidateState_AddsPreviousCandidateState_ToTraversedStatesList()\n        {\n            var context = CreateContext();\n            var state = new Mock<IState>();\n\n            context.CandidateState = state.Object;\n\n            Assert.Contains(_applyContext.NewState.Object, context.TraversedStates);\n        }\n\n        [Fact]\n        public void SetJobParameter_CallsTheCorrespondingMethod_WithJsonEncodedValue()\n        {\n            var context = CreateContext();\n\n            context.SetJobParameter(\"Name\", \"Value\");\n\n            _applyContext.Connection.Verify(x => x.SetJobParameter(\n                _applyContext.BackgroundJob.Id, \"Name\", JobHelper.ToJson(\"Value\")));\n        }\n\n        [Fact]\n        public void SetJobParameter_CanReceiveNullValue()\n        {\n            var context = CreateContext();\n\n            context.SetJobParameter(\"Name\", (string)null);\n\n            _applyContext.Connection.Verify(x => x.SetJobParameter(\n                _applyContext.BackgroundJob.Id, \"Name\", JobHelper.ToJson(null)));\n        }\n\n        [Fact]\n        public void GetJobParameter_CallsTheCorrespondingMethod_WithJsonDecodedValue()\n        {\n            var context = CreateContext();\n            _applyContext.Connection.Setup(x => x.GetJobParameter(_applyContext.BackgroundJob.Id, \"Name\"))\n                .Returns(JobHelper.ToJson(\"Value\"));\n\n            var value = context.GetJobParameter<string>(\"Name\");\n\n            Assert.Equal(\"Value\", value);\n        }\n\n        [Fact]\n        public void GetJobParameter_ReturnsDefaultValue_WhenNoValueProvided()\n        {\n            var context = CreateContext();\n            _applyContext.Connection.Setup(x => x.GetJobParameter(\"1\", \"Value\"))\n                .Returns(JobHelper.ToJson(null));\n\n            var value = context.GetJobParameter<int>(\"Name\");\n\n            Assert.Equal(default(int), value);\n        }\n\n        private ElectStateContext CreateContext()\n        {\n            return new ElectStateContext(_applyContext.Object);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/EnqueuedStateFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class EnqueuedStateFacts\n    {\n        [Fact]\n        public void StateName_IsCorrect()\n        {\n            var state = new EnqueuedState();\n            Assert.Equal(EnqueuedState.StateName, state.Name);\n        }\n\n        [Fact]\n        public void Ctor_ShouldSetQueue_WhenItWasGiven()\n        {\n            var state = new EnqueuedState(\"critical\");\n            Assert.Equal(\"critical\", state.Queue);\n        }\n\n        [Fact]\n        public void SetQueue_ThrowsAnException_WhenQueueValueIsEmpty()\n        {\n            var state = new EnqueuedState();\n            Assert.Throws<ArgumentNullException>(() => state.Queue = String.Empty);\n        }\n\n        [Fact]\n        public void SetQueue_ThrowsAnException_WhenValueIsNotInAGivenFormat()\n        {\n            var state = new EnqueuedState();\n\n            Assert.Throws<ArgumentException>(() => state.Queue = \"UppercaseLetters\");\n            Assert.Throws<ArgumentException>(() => state.Queue = \"punctuation:un-allowed\");\n            Assert.Throws<ArgumentException>(() => state.Queue = \"моя_твоя_непонимать\");\n        }\n\n        [Fact]\n        public void SetQueue_DoesNotThrowException_WhenValueIsInACorrectFormat()\n        {\n            var state = new EnqueuedState();\n\n            // Does not throw\n            state.Queue = \"lowercasedcharacters\";\n            state.Queue = \"underscores_allowed\";\n            state.Queue = \"1234567890_allowed\";\n        }\n\n        [Fact]\n        public void SerializeData_ReturnsCorrectData()\n        {\n            var state = new EnqueuedState();\n\n            var serializedData = state.SerializeData();\n\n            Assert.Equal(state.Queue, serializedData[\"Queue\"]);\n            Assert.Equal(JobHelper.SerializeDateTime(state.EnqueuedAt), serializedData[\"EnqueuedAt\"]);\n        }\n\n        [Fact]\n        public void IsFinal_ReturnsFalse()\n        {\n            var state = new EnqueuedState();\n\n            Assert.False(state.IsFinal);\n        }\n\n        [Fact]\n        public void IgnoreExceptions_ReturnsFalse()\n        {\n            var state = new EnqueuedState();\n\n            Assert.False(state.IgnoreJobLoadException);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_Before170()\n        {\n            var state = new EnqueuedState(\"default\");\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.EnqueuedState, Hangfire.Core\\\",\\\"Queue\\\":\\\"default\\\",\\\"Reason\\\":null}\",\n                serialized);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_After170()\n        {\n            var state = new EnqueuedState(\"default\");\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.EnqueuedState, Hangfire.Core\\\",\\\"Queue\\\":\\\"default\\\"}\",\n                serialized);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void JsonDeserialize_CanHandlePreviousFormat()\n        {\n            var json = \"{\\\"Queue\\\":\\\"critical\\\",\\\"EnqueuedAt\\\":\\\"2012-04-02T11:22:33.0000000Z\\\",\\\"Name\\\":\\\"Enqueued\\\",\\\"Reason\\\":\\\"hello\\\",\\\"IsFinal\\\":false,\\\"IgnoreJobLoadException\\\":false}\";\n            var state = SerializationHelper.Deserialize<EnqueuedState>(json);\n\n            Assert.Equal(\"critical\", state.Queue);\n            Assert.Equal(\"hello\", state.Reason);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void JsonDeserialize_CanHandleNewFormat()\n        {\n            var json = \"{\\\"$type\\\":\\\"Hangfire.States.EnqueuedState, Hangfire.Core\\\",\\\"Queue\\\":\\\"default\\\"}\";\n            var state = SerializationHelper.Deserialize<EnqueuedState>(json);\n\n            Assert.Equal(\"default\", state.Queue);\n            Assert.Null(state.Reason);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void JsonDeserialize_CanHandle170Beta1Format_WithDefaultValueForQueue()\n        {\n            var json = \"{\\\"$type\\\":\\\"Hangfire.States.EnqueuedState, Hangfire.Core\\\"}\";\n            var state = SerializationHelper.Deserialize<EnqueuedState>(json);\n\n            Assert.Equal(\"default\", state.Queue);\n            Assert.Null(state.Reason);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/EnqueuedStateHandlerFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class EnqueuedStateHandlerFacts\n    {\n        private const string Queue = \"critical\";\n\n        private readonly ApplyStateContextMock _context;\n        private readonly Mock<IWriteOnlyTransaction> _transaction;\n        private readonly EnqueuedState _enqueuedState;\n\n        public EnqueuedStateHandlerFacts()\n        {\n            _enqueuedState = new EnqueuedState { Queue = Queue };\n            _context = new ApplyStateContextMock\n            {\n                NewStateObject = _enqueuedState\n            };\n\n            _transaction = new Mock<IWriteOnlyTransaction>();\n        }\n\n        [Fact]\n        public void HandlerShouldBeRegistered_ForTheEnqueuedState()\n        {\n            var handler = new EnqueuedState.Handler();\n            Assert.Equal(EnqueuedState.StateName, handler.StateName);\n        }\n\n        [Fact]\n        public void Apply_AddsJob_ToTheSpecifiedQueue()\n        {\n            var handler = new EnqueuedState.Handler();\n\n            handler.Apply(_context.Object, _transaction.Object);\n\n            _transaction.Verify(x => x.AddToQueue(Queue, _context.BackgroundJob.Id));\n        }\n\n        [Fact]\n        public void Apply_ThrowsAnException_WhenOtherThanEnqueuedStateGiven()\n        {\n            var handler = new EnqueuedState.Handler();\n            _context.NewStateObject = null;\n            _context.NewState = new Mock<IState>();\n\n            Assert.Throws<InvalidOperationException>(\n                () => handler.Apply(_context.Object, _transaction.Object));\n        }\n\n        [Fact]\n        public void Apply_WithJobAndQueueSpecified_ThrowsAnException_WhenRequiredFeatureNotSupported()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => BackgroundJobMock.SomeMethod(), \"critical\");\n            var handler = new EnqueuedState.Handler();\n\n            Assert.Throws<NotSupportedException>(\n                () => handler.Apply(_context.Object, _transaction.Object));\n        }\n\n        [Fact]\n        public void Apply_AddsJob_ToTheJobTargetQueue_WhenEnqueuedState_HasTheDefaultQueue()\n        {\n            _context.Storage.Setup(x => x.HasFeature(\"Job.Queue\")).Returns(true);\n            _context.BackgroundJob.Job = Job.FromExpression(() => BackgroundJobMock.SomeMethod(), \"myqueue\");\n            _enqueuedState.Queue = \"default\";\n            var handler = new EnqueuedState.Handler();\n\n            handler.Apply(_context.Object, _transaction.Object);\n\n            _transaction.Verify(x => x.AddToQueue(\"myqueue\", _context.BackgroundJob.Id));\n        }\n\n        [Fact]\n        public void Apply_AddsJobToTheOverridenQueue_WhenTheJobTargetQueuePresent_ButEnqueuedStateQueueIsNotDefault()\n        {\n            _context.Storage.Setup(x => x.HasFeature(\"Job.Queue\")).Returns(true);\n            _context.BackgroundJob.Job = Job.FromExpression(() => BackgroundJobMock.SomeMethod(), \"myqueue\");\n            _enqueuedState.Queue = \"otherqueue\";\n            var handler = new EnqueuedState.Handler();\n\n            handler.Apply(_context.Object, _transaction.Object);\n\n            _transaction.Verify(x => x.AddToQueue(\"otherqueue\", _context.BackgroundJob.Id));\n        }\n\n        [Fact]\n        public void Unapply_DoesNotDoAnything()\n        {\n            var handler = new EnqueuedState.Handler();\n\n            // Does not throw\n            handler.Unapply(null, null);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/EnqueuedStateValidationFacts.cs",
    "content": "﻿using System;\nusing Hangfire.States;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class EnqueuedStateValidationFacts\n    {\n        [Fact]\n        public void ValidateQueueName_ThrowsAnException_WhenQueueNameIsEmpty()\n        {\n            Assert.Throws<ArgumentNullException>(() => EnqueuedState.ValidateQueueName(\"queue\", string.Empty));\n        }\n\n        [Fact]\n        public void ValidateQueueName_ThrowsAnException_WhenQueueNameIsNull()\n        {\n            Assert.Throws<ArgumentNullException>(() => EnqueuedState.ValidateQueueName(\"queue\", null));\n        }\n\n        [Fact]\n        public void ValidateQueueName_ThrowsAnException_WhenQueueNameHasUpperCaseLetters()\n        {\n            Assert.Throws<ArgumentException>(() => EnqueuedState.ValidateQueueName(\"queue\", \"UppercaseLetters\"));\n        }\n\n        [Fact]\n        public void ValidateQueueName_ThrowsAnException_WhenQueueNameHasWhitespaces()\n        {\n            Assert.Throws<ArgumentException>(() => EnqueuedState.ValidateQueueName(\"queue\", \"test test\"));\n        }\n\n        [Fact]\n        public void ValidateQueueName_DoesntThrowAnException_WhenQueueNameHasOnlyLowerCaseLetters()\n        {\n            // Does not throw\n            EnqueuedState.ValidateQueueName(\"queue\", \"valid\");\n        }\n\n        [Fact]\n        public void ValidateQueueName_DoesntThrowAnException_WhenQueueNameHasUnderscores()\n        {\n            // Does not throw\n            EnqueuedState.ValidateQueueName(\"queue\", \"a_b_c\");\n        }\n\n        [Fact]\n        public void ValidateQueueName_DoesntThrowAnException_WhenValueHasOnlyDigits()\n        {\n            // Does not throw\n            EnqueuedState.ValidateQueueName(\"queue\", \"363463\");\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/FailedStateFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class FailedStateFacts\n    {\n        [Fact]\n        public void Ctor_ThrowsAnException_IfExceptionParameterIsNull()\n        {\n            Assert.Throws<ArgumentNullException>(\n                () => new FailedState(null));\n        }\n\n        [Fact]\n        public void StateName_IsCorrect()\n        {\n            var state = new FailedState(new Exception());\n            Assert.Equal(FailedState.StateName, state.Name);\n        }\n\n        [Fact]\n        public void SerializeData_ReturnsCorrectData()\n        {\n            var state = new FailedState(new Exception(\"Message\"));\n\n            var serializedData = state.SerializeData();\n\n            Assert.Equal(JobHelper.SerializeDateTime(state.FailedAt), serializedData[\"FailedAt\"]);\n            Assert.Equal(\"System.Exception\", serializedData[\"ExceptionType\"]);\n            Assert.Equal(\"Message\", serializedData[\"ExceptionMessage\"]);\n            Assert.Equal(state.Exception.ToString(), serializedData[\"ExceptionDetails\"]);\n        }\n\n        [Fact]\n        public void IsFinal_ReturnsFalse()\n        {\n            var state = new FailedState(new Exception());\n\n            Assert.False(state.IsFinal);\n        }\n\n        [Fact]\n        public void IgnoreExceptions_ReturnsFalse()\n        {\n            var state = new FailedState(new Exception());\n            Assert.False(state.IgnoreJobLoadException);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/ProcessingStateFacts.cs",
    "content": "using System;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class ProcessingStateFacts\n    {\n        private const string WorkerId = \"1\";\n        private const string ServerId = \"Server1:4231\";\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenServerNameIsNull()\n        {\n            Assert.Throws<ArgumentNullException>(\n                () => new ProcessingState(null, WorkerId));\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenServerNameIsEmpty()\n        {\n            Assert.Throws<ArgumentNullException>(\n                () => new ProcessingState(String.Empty, WorkerId));\n        }\n\n        [Fact]\n        public void StateName_IsCorrect()\n        {\n            var state = CreateState();\n            Assert.Equal(ProcessingState.StateName, state.Name);\n        }\n\n        [Fact]\n        public void SerializeData_ReturnsCorrectData()\n        {\n            var state = CreateState();\n\n            var data = state.SerializeData();\n\n            Assert.Equal(JobHelper.SerializeDateTime(state.StartedAt), data[\"StartedAt\"]);\n            Assert.Equal(ServerId, data[\"ServerId\"]);\n            Assert.Equal(WorkerId.ToString(), data[\"WorkerId\"]);\n        }\n\n        [Fact]\n        public void IsFinal_ReturnsFalse()\n        {\n            var state = CreateState();\n\n            Assert.False(state.IsFinal);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_Before170()\n        {\n            var state = new ProcessingState(\"server1\", \"worker1\");\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.ProcessingState, Hangfire.Core\\\",\\\"ServerId\\\":\\\"server1\\\",\" +\n                \"\\\"WorkerId\\\":\\\"worker1\\\",\\\"Reason\\\":null}\",\n                serialized);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_After170()\n        {\n            var state = new ProcessingState(\"server1\", \"worker1\");\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.ProcessingState, Hangfire.Core\\\",\\\"ServerId\\\":\\\"server1\\\",\" +\n                \"\\\"WorkerId\\\":\\\"worker1\\\"}\",\n                serialized);\n        }\n\n        [Fact]\n        public void IgnoreExceptions_ReturnsFalse()\n        {\n            var state = CreateState();\n\n            Assert.False(state.IgnoreJobLoadException);\n        }\n\n        private ProcessingState CreateState()\n        {\n            return new ProcessingState(ServerId, WorkerId);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/ScheduledStateFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Newtonsoft.Json;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class ScheduledStateFacts\n    {\n        [Fact]\n        public void StateName_IsCorrect()\n        {\n            var state = new ScheduledState(DateTime.UtcNow);\n            Assert.Equal(ScheduledState.StateName, state.Name);\n        }\n\n        [Fact]\n        public void Ctor_SetsTheCorrectData_WhenDateIsPassed()\n        {\n            var date = new DateTime(2012, 12, 12);\n            var state = new ScheduledState(date);\n            Assert.Equal(date, state.EnqueueAt);\n        }\n\n        [Fact]\n        public void Ctor_SetsTheCorrectDate_WhenTimeSpanIsPassed()\n        {\n            var state = new ScheduledState(TimeSpan.FromDays(1));\n            Assert.True(DateTime.UtcNow.AddDays(1).AddMinutes(-1) < state.EnqueueAt);\n            Assert.True(state.EnqueueAt < DateTime.UtcNow.AddDays(1).AddMinutes(1));\n        }\n\n        [Fact]\n        public void SerializeData_ReturnsCorrectData()\n        {\n            var state = new ScheduledState(new DateTime(2012, 12, 12));\n\n            var data = state.SerializeData();\n\n            Assert.Equal(JobHelper.SerializeDateTime(state.EnqueueAt), data[\"EnqueueAt\"]);\n            Assert.Equal(JobHelper.SerializeDateTime(state.ScheduledAt), data[\"ScheduledAt\"]);\n        }\n\n        [Fact]\n        public void SerializeData_DoesNotContainQueueKey_WhenItIsNotSet()\n        {\n            var state = new ScheduledState(TimeSpan.Zero);\n\n            var data = state.SerializeData();\n\n            Assert.DoesNotContain(\"Queue\", data.Keys);\n        }\n\n        [Fact]\n        public void IsFinal_ReturnsFalse()\n        {\n            var state = new ScheduledState(DateTime.UtcNow);\n\n            Assert.False(state.IsFinal);\n        }\n\n        [Fact]\n        public void IgnoreExceptions_ReturnsFalse()\n        {\n            var state = new ScheduledState(DateTime.UtcNow);\n            Assert.False(state.IgnoreJobLoadException);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_Before170()\n        {\n            var dateTime = DateTime.UtcNow;\n            var state = new ScheduledState(dateTime);\n            var convertedDateTime = JsonConvert.SerializeObject(dateTime);\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.ScheduledState, Hangfire.Core\\\",\\\"EnqueueAt\\\":\" + convertedDateTime + \",\\\"Reason\\\":null}\",\n                serialized);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_After170()\n        {\n            var dateTime = DateTime.UtcNow;\n            var state = new ScheduledState(dateTime);\n            var convertedDateTime = JsonConvert.SerializeObject(dateTime);\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.ScheduledState, Hangfire.Core\\\",\\\"EnqueueAt\\\":\" + convertedDateTime + \"}\",\n                serialized);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/ScheduledStateHandlerFacts.cs",
    "content": "﻿using System;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class ScheduledStateHandlerFacts\n    {\n        private readonly ApplyStateContextMock _context;\n        private readonly Mock<IWriteOnlyTransaction> _transaction;\n\n        private const string JobId = \"1\";\n        private readonly DateTime _enqueueAt = DateTime.UtcNow.AddDays(1);\n\n        public ScheduledStateHandlerFacts()\n        {\n            _context = new ApplyStateContextMock\n            {\n                BackgroundJob = { Id = JobId },\n                NewStateObject = new ScheduledState(_enqueueAt)\n            };\n\n            _transaction = new Mock<IWriteOnlyTransaction>();\n        }\n\n        [Fact]\n        public void StateName_ShouldBeEqualToScheduledState()\n        {\n            var handler = new ScheduledState.Handler();\n            Assert.Equal(ScheduledState.StateName, handler.StateName);\n        }\n\n        [Fact]\n        public void Apply_ShouldAddTheJob_ToTheScheduleSet_WithTheCorrectScore()\n        {\n            var handler = new ScheduledState.Handler();\n            handler.Apply(_context.Object, _transaction.Object);\n\n            _transaction.Verify(x => x.AddToSet(\n                \"schedule\", JobId, JobHelper.ToTimestamp(_enqueueAt)));\n        }\n\n        [Fact]\n        public void Apply_WithJobAndQueueSpecified_ThrowsAnException_WhenRequiredFeatureNotSupported()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => BackgroundJobMock.SomeMethod(), \"critical\");\n            var handler = new ScheduledState.Handler();\n\n            Assert.Throws<NotSupportedException>(\n                () => handler.Apply(_context.Object, _transaction.Object));\n        }\n\n        [Fact]\n        public void Apply_ShouldAddJob_WithQueueSpecified_ToTheScheduleSet_WithQueuePrepended()\n        {\n            _context.Storage.Setup(x => x.HasFeature(\"Job.Queue\")).Returns(true);\n            _context.BackgroundJob.Job = Job.FromExpression(() => BackgroundJobMock.SomeMethod(), \"critical\");\n            var handler = new ScheduledState.Handler();\n\n            handler.Apply(_context.Object, _transaction.Object);\n\n            _transaction.Verify(x => x.AddToSet(\n                \"schedule\",\n                \"critical:\" + JobId,\n                It.IsAny<double>()));\n        }\n\n        [Fact]\n        public void Unapply_ShouldRemoveTheJobId_FromTheScheduledSet()\n        {\n            var handler = new ScheduledState.Handler();\n            handler.Unapply(_context.Object, _transaction.Object);\n\n            _transaction.Verify(x => x.RemoveFromSet(\"schedule\", JobId));\n        }\n\n        [Fact]\n        public void Unapply_WithJobAndQueueSpecified_ThrowsAnException_WhenRequiredFeatureNotSupported()\n        {\n            _context.BackgroundJob.Job = Job.FromExpression(() => BackgroundJobMock.SomeMethod(), \"critical\");\n            var handler = new ScheduledState.Handler();\n\n            Assert.Throws<NotSupportedException>(\n                () => handler.Unapply(_context.Object, _transaction.Object));\n        }\n\n        [Fact]\n        public void Unapply_WithJob_WithQueueSpecified_ShouldRemoveTheJobId_FromTheScheduleSet_PrependedWithQueueName()\n        {\n            _context.Storage.Setup(x => x.HasFeature(\"Job.Queue\")).Returns(true);\n            _context.BackgroundJob.Job = Job.FromExpression(() => BackgroundJobMock.SomeMethod(), \"critical\");\n            var handler = new ScheduledState.Handler();\n\n            handler.Unapply(_context.Object, _transaction.Object);\n\n            _transaction.Verify(x => x.RemoveFromSet(\"schedule\", $\"critical:{JobId}\"));\n        }\n\n        [Fact]\n        public void Apply_ThrowsAnException_WhenGivenStateIsNotScheduledState()\n        {\n            var handler = new ScheduledState.Handler();\n            _context.NewStateObject = null;\n            _context.NewState = new Mock<IState>();\n\n            Assert.Throws<InvalidOperationException>(\n                () => handler.Apply(_context.Object, _transaction.Object));\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/StateChangeContextFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing Hangfire.Profiling;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class StateChangeContextFacts\n    {\n        private const string JobId = \"SomeJob\";\n\n        private readonly Mock<JobStorage> _storage;\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly Mock<IState> _newState;\n        private readonly string[] _expectedStates;\n        private readonly CancellationToken _token;\n        private readonly Mock<IProfiler> _profiler;\n        private readonly Dictionary<string, object> _customData;\n\n        public StateChangeContextFacts()\n        {\n            _storage = new Mock<JobStorage>();\n            _connection = new Mock<IStorageConnection>();\n            _newState = new Mock<IState>();\n            _expectedStates = new[] { \"Succeeded\", \"Failed\" };\n            _token = new CancellationToken(true);\n            _profiler = new Mock<IProfiler>();\n            _customData = new Dictionary<string, object>();\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new StateChangeContext(\n                    null,\n                    _connection.Object,\n                    JobId,\n                    _newState.Object));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenConnectionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new StateChangeContext(\n                    _storage.Object,\n                    null,\n                    JobId,\n                    _newState.Object));\n\n            Assert.Equal(\"connection\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenBackgroundJobIdIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new StateChangeContext(\n                    _storage.Object,\n                    _connection.Object,\n                    null,\n                    _newState.Object));\n\n            Assert.Equal(\"backgroundJobId\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenNewStateIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new StateChangeContext(\n                    _storage.Object,\n                    _connection.Object,\n                    JobId,\n                    null));\n\n            Assert.Equal(\"newState\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenProfilerIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new StateChangeContext(\n                    _storage.Object,\n                    _connection.Object,\n                    JobId,\n                    _newState.Object,\n                    null,\n                    false,\n                    CancellationToken.None,\n                    null,\n                    null));\n\n            Assert.Equal(\"profiler\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor1_CorrectlySets_AllTheProperties()\n        {\n            var context = new StateChangeContext(\n                _storage.Object,\n                _connection.Object,\n                JobId,\n                _newState.Object);\n\n            Assert.Same(_storage.Object, context.Storage);\n            Assert.Same(_connection.Object, context.Connection);\n            Assert.Equal(JobId, context.BackgroundJobId);\n            Assert.Same(_newState.Object, context.NewState);\n            Assert.Null(context.ExpectedStates);\n            Assert.Equal(CancellationToken.None, context.CancellationToken);\n            Assert.NotNull(context.Profiler);\n            Assert.Null(context.CustomData);\n        }\n\n        [Fact]\n        public void Ctor2_CorrectlySets_AllTheProperties()\n        {\n            var context = new StateChangeContext(\n                _storage.Object,\n                _connection.Object,\n                JobId,\n                _newState.Object,\n                _expectedStates);\n\n            Assert.Same(_storage.Object, context.Storage);\n            Assert.Same(_connection.Object, context.Connection);\n            Assert.Equal(JobId, context.BackgroundJobId);\n            Assert.Same(_newState.Object, context.NewState);\n            Assert.Equal(_expectedStates, context.ExpectedStates);\n            Assert.Equal(CancellationToken.None, context.CancellationToken);\n            Assert.NotNull(context.Profiler);\n            Assert.Null(context.CustomData);\n        }\n\n        [Fact]\n        public void Ctor3_CorrectlySets_AllTheProperties()\n        {\n            var context = new StateChangeContext(\n                _storage.Object,\n                _connection.Object,\n                JobId,\n                _newState.Object,\n                _expectedStates,\n                _token);\n\n            Assert.Same(_storage.Object, context.Storage);\n            Assert.Same(_connection.Object, context.Connection);\n            Assert.Equal(JobId, context.BackgroundJobId);\n            Assert.Same(_newState.Object, context.NewState);\n            Assert.Equal(_expectedStates, context.ExpectedStates);\n            Assert.Equal(_token, context.CancellationToken);\n            Assert.NotNull(context.Profiler);\n            Assert.Null(context.CustomData);\n        }\n\n        [Fact]\n        public void InternalCtor_CorrectlySets_AllTheProperties()\n        {\n            var context = new StateChangeContext(\n                _storage.Object,\n                _connection.Object,\n                JobId,\n                _newState.Object,\n                _expectedStates,\n                false,\n                _token,\n                _profiler.Object,\n                \"some-server\",\n                _customData);\n\n            Assert.Same(_storage.Object, context.Storage);\n            Assert.Same(_connection.Object, context.Connection);\n            Assert.Equal(JobId, context.BackgroundJobId);\n            Assert.Same(_newState.Object, context.NewState);\n            Assert.Equal(_expectedStates, context.ExpectedStates);\n            Assert.False(context.DisableFilters);\n            Assert.Equal(_token, context.CancellationToken);\n            Assert.Same(_profiler.Object, context.Profiler);\n            Assert.Equal(\"some-server\", context.ServerId);\n            Assert.Same(_customData, context.CustomData);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/StateHandlerCollectionFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.States;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class StateHandlerCollectionFacts\n    {\n        private readonly StateHandlerCollection _collection;\n\n        public StateHandlerCollectionFacts()\n        {\n            _collection = new StateHandlerCollection();\n        }\n\n        [Fact]\n        public void AddHandler_ThrowsAnException_WhenHandlerIsNull()\n        {\n            Assert.Throws<ArgumentNullException>(\n                () => _collection.AddHandler(null));\n        }\n\n        [Fact]\n        public void AddHandler_ThrowsAnException_WhenStateNameOfTheGivenHandlerIsNull()\n        {\n            var handler = new Mock<IStateHandler>();\n            handler.Setup(x => x.StateName).Returns((string)null);\n\n            var exception = Assert.Throws<ArgumentException>(\n                () => _collection.AddHandler(handler.Object));\n\n            Assert.Contains(\"StateName\", exception.Message);\n        }\n\n        [Fact]\n        public void GetHandlers_ReturnsEmptyCollection_WhenHandlersWereNotAddedForTheState()\n        {\n            var handlers = _collection.GetHandlers(\"State\");\n            Assert.Empty(handlers);\n        }\n\n        [Fact]\n        public void GetHandlers_ReturnsEmptyCollection_WhenStateNameIsNull()\n        {\n            var handlers = _collection.GetHandlers(null);\n            Assert.Empty(handlers);\n        }\n\n        [Fact]\n        public void GetHandlers_ReturnsAllRegisteredHandlersForTheState()\n        {\n            var handler1Mock = new Mock<IStateHandler>();\n            handler1Mock.Setup(x => x.StateName).Returns(\"State\");\n\n            var handler2Mock = new Mock<IStateHandler>();\n            handler2Mock.Setup(x => x.StateName).Returns(\"State\");\n\n            _collection.AddHandler(handler1Mock.Object);\n            _collection.AddHandler(handler2Mock.Object);\n\n            var handlers = _collection.GetHandlers(\"State\").ToArray();\n\n            Assert.Contains(handler1Mock.Object, handlers);\n            Assert.Contains(handler2Mock.Object, handlers);\n        }\n\n        [Fact]\n        public void GetHandlers_ReturnsOnlyHandlersOfASpecifiedState()\n        {\n            var anotherStateHandlerMock = new Mock<IStateHandler>();\n            anotherStateHandlerMock.Setup(x => x.StateName).Returns(\"AnotherState\");\n\n            _collection.AddHandler(anotherStateHandlerMock.Object);\n            var handlers = _collection.GetHandlers(\"State\");\n\n            Assert.Empty(handlers);\n        }\n\n        [Fact]\n        public void AddRange_ThrowsAnException_WhenEnumerationIsNull()\n        {\n            Assert.Throws<ArgumentNullException>(\n                () => _collection.AddRange(null));\n        }\n\n        [Fact]\n        public void AddRange_AddsHandlers_FromEnumeration()\n        {\n            // Arrange\n            var handler1 = new Mock<IStateHandler>();\n            handler1.Setup(x => x.StateName).Returns(\"State1\");\n\n            var handler2 = new Mock<IStateHandler>();\n            handler2.Setup(x => x.StateName).Returns(\"State2\");\n\n            var handlers = new List<IStateHandler> { handler1.Object, handler2.Object };\n\n            // Act\n            _collection.AddRange(handlers);\n\n            // Assert\n            Assert.Same(handler1.Object, _collection.GetHandlers(\"State1\").Single());\n            Assert.Same(handler2.Object, _collection.GetHandlers(\"State2\").Single());\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/StateMachineFacts.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Moq.Sequences;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class StateMachineFacts\n    {\n        private const string OldStateName = \"OldState\";\n        private const string JobId = \"job\";\n\n        private readonly List<object> _filters = new List<object>();\n        \n        private readonly Mock<IWriteOnlyTransaction> _transaction;\n        private readonly ApplyStateContextMock _context;\n        private readonly Mock<IJobFilterProvider> _filterProvider;\n        \n        private readonly Mock<IStateMachine> _innerMachine;\n\n        public StateMachineFacts()\n        {\n            var connection = new Mock<IStorageConnection>();\n            _transaction = new Mock<IWriteOnlyTransaction>();\n            connection.Setup(x => x.CreateWriteTransaction()).Returns(_transaction.Object);\n\n            var backgroundJob = new BackgroundJobMock { Id = JobId };\n            _context = new ApplyStateContextMock\n            {\n                BackgroundJob = backgroundJob,\n                OldStateName = OldStateName,\n                Transaction = _transaction\n            };\n\n            _filterProvider = new Mock<IJobFilterProvider>();\n            _filterProvider.Setup(x => x.GetFilters(It.IsNotNull<Job>())).Returns(\n                _filters.Select(f => new JobFilter(f, JobFilterScope.Type, null)));\n            \n            _innerMachine = new Mock<IStateMachine>();\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenFilterProviderIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new StateMachine(null, _innerMachine.Object));\n\n            Assert.Equal(\"filterProvider\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenInnerStateMachineIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new StateMachine(_filterProvider.Object, null));\n\n            Assert.Equal(\"innerStateMachine\", exception.ParamName);\n        }\n\n        [Fact]\n        public void ApplyState_CallsElectionFilterWithCorrectProperties()\n        {\n            // Arrange\n            var filter = CreateFilter<IElectStateFilter>();\n            \n            var stateMachine = CreateStateMachine();\n\n            // Act\n            stateMachine.ApplyState(_context.Object);\n\n            filter.Verify(x => x.OnStateElection(It.Is<ElectStateContext>(context =>\n                context.Storage == _context.Storage.Object &&\n                context.Connection == _context.Connection.Object &&\n                context.BackgroundJob == _context.BackgroundJob.Object &&\n                context.CandidateState == _context.NewState.Object &&\n                context.CurrentState == _context.OldStateName)));\n        }\n\n        [Fact, SequenceAttribute]\n        public void ApplyState_CallsElectionFilters()\n        {\n            // Arrange\n            var filter1 = CreateFilter<IElectStateFilter>();\n            var filter2 = CreateFilter<IElectStateFilter>();\n\n            filter1.Setup(x => x.OnStateElection(It.IsAny<ElectStateContext>()))\n                .InSequence();\n            filter2.Setup(x => x.OnStateElection(It.IsAny<ElectStateContext>()))\n                .InSequence();\n\n            var stateMachine = CreateStateMachine();\n\n            // Act\n            stateMachine.ApplyState(_context.Object);\n\n            // Assert - Sequence\n        }\n\n        [Fact]\n        public void ApplyState_AddsJobHistory_ForTraversedStates()\n        {\n            // Arrange\n            var anotherState = new Mock<IState>();\n            var filter = CreateFilter<IElectStateFilter>();\n            filter.Setup(x => x.OnStateElection(It.IsNotNull<ElectStateContext>()))\n                .Callback<ElectStateContext>(context => context.CandidateState = anotherState.Object);\n\n            var stateMachine = CreateStateMachine();\n\n            // Act\n            stateMachine.ApplyState(_context.Object);\n\n            // Assert\n            _context.Transaction.Verify(x => x.AddJobState(JobId, _context.NewState.Object));\n        }\n\n        [Fact, Sequence]\n        public void ApplyState_CallsStateUnappliedFilters_BeforeCallingInnerStateMachine()\n        {\n            // Arrange\n            var filter1 = CreateFilter<IApplyStateFilter>();\n            var filter2 = CreateFilter<IApplyStateFilter>();\n\n            filter1.Setup(x => x.OnStateUnapplied(It.IsNotNull<ApplyStateContext>(), _transaction.Object))\n                .InSequence();\n            filter2.Setup(x => x.OnStateUnapplied(It.IsNotNull<ApplyStateContext>(), _transaction.Object))\n                .InSequence();\n            _innerMachine\n                .Setup(x => x.ApplyState(It.IsAny<ApplyStateContext>()))\n                .InSequence();\n\n            var stateMachine = CreateStateMachine();\n\n            // Act\n            stateMachine.ApplyState(_context.Object);\n\n            // Assert - Sequence\n        }\n\n        [Fact, Sequence]\n        public void ApplyState_CallsStateAppliedFilters_AfterSettingTheState()\n        {\n            // Arrange\n            var filter1 = CreateFilter<IApplyStateFilter>();\n            var filter2 = CreateFilter<IApplyStateFilter>();\n\n            filter1.Setup(x => x.OnStateApplied(It.IsNotNull<ApplyStateContext>(), _transaction.Object))\n                .InSequence();\n            filter2.Setup(x => x.OnStateApplied(It.IsNotNull<ApplyStateContext>(), _transaction.Object))\n                .InSequence();\n\n            var stateMachine = CreateStateMachine();\n\n            // Act\n            stateMachine.ApplyState(_context.Object);\n\n            // Assert - Sequence\n        }\n        \n        private StateMachine CreateStateMachine()\n        {\n            return new StateMachine(_filterProvider.Object, _innerMachine.Object);\n        }\n\n        private Mock<T> CreateFilter<T>() where T : class\n        {\n            var filter = new Mock<T>();\n            _filters.Add(filter.Object);\n\n            return filter;\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/SucceededStateFacts.cs",
    "content": "using System.Diagnostics;\nusing Hangfire.Common;\nusing Hangfire.States;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class SucceededStateFacts\n    {\n        [Fact]\n        public void StateName_IsEqualToSucceeded()\n        {\n            Assert.Equal(\"Succeeded\", SucceededState.StateName);\n        }\n\n        [Fact]\n        public void NameProperty_ReturnsStateName()\n        {\n            var state = CreateState();\n            Assert.Equal(SucceededState.StateName, state.Name);\n        }\n\n        [Fact]\n        public void SerializeData_ReturnsCorrectData()\n        {\n            var state = CreateState();\n\n            var data = state.SerializeData();\n\n            Assert.Equal(\"\\\"Returned  value\\\"\", data[\"Result\"]);\n            Assert.Equal(JobHelper.SerializeDateTime(state.SucceededAt), data[\"SucceededAt\"]);\n            Assert.Equal(\"123\", data[\"PerformanceDuration\"]);\n            Assert.Equal(\"11\", data[\"Latency\"]);\n        }\n\n        [Fact]\n        public void SerializeData_DoesNotAppendEntry_ForNullResult()\n        {\n            var state = new SucceededState(null, 0, 0);\n\n            var data = state.SerializeData();\n\n            Assert.False(data.ContainsKey(\"Result\"));\n        }\n\n        [Fact]\n        public void SerializeData_CorrectlyHandlesResults_ThatCantBeSerialized()\n        {\n            var process = new Process();\n            var state = new SucceededState(process, 0, 0);\n\n            var data = state.SerializeData();\n\n            Assert.Contains(\"Can not serialize\", data[\"Result\"]);\n        }\n\n        [Fact]\n        public void IsFinal_ReturnsTrue()\n        {\n            var state = CreateState();\n            Assert.True(state.IsFinal);\n        }\n\n        [Fact]\n        public void IgnoreExceptions_ReturnsFalse()\n        {\n            var state = CreateState();\n            Assert.False(state.IgnoreJobLoadException);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_Before170()\n        {\n            var state = new SucceededState(null, 1, 2);\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.SucceededState, Hangfire.Core\\\",\\\"Result\\\":null,\\\"Latency\\\":1,\\\"PerformanceDuration\\\":2,\\\"Reason\\\":null}\",\n                serialized);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void JsonSerialize_ReturnsCorrectString_After170()\n        {\n            var state = new SucceededState(null, 1, 2);\n\n            var serialized = SerializationHelper.Serialize<IState>(state, SerializationOption.TypedInternal);\n\n            Assert.Equal(\n                \"{\\\"$type\\\":\\\"Hangfire.States.SucceededState, Hangfire.Core\\\",\\\"Latency\\\":1,\\\"PerformanceDuration\\\":2}\",\n                serialized);\n        }\n\n        private static SucceededState CreateState()\n        {\n            return new SucceededState(\"Returned  value\", 11, 123);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/States/SucceededStateHandlerFacts.cs",
    "content": "﻿using Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.States\n{\n    public class SucceededStateHandlerFacts\n    {\n        private readonly ApplyStateContextMock _context;\n        private readonly Mock<IWriteOnlyTransaction> _transactionMock\n            = new Mock<IWriteOnlyTransaction>();\n\n        public SucceededStateHandlerFacts()\n        {\n            _context = new ApplyStateContextMock();\n        }\n\n        [Fact]\n        public void ShouldWorkOnlyWithSucceededState()\n        {\n            var handler = new SucceededState.Handler();\n            Assert.Equal(SucceededState.StateName, handler.StateName);\n        }\n\n        [Fact]\n        public void Apply_ShouldIncrease_SucceededCounter()\n        {\n            var handler = new SucceededState.Handler();\n            handler.Apply(_context.Object, _transactionMock.Object);\n\n            _transactionMock.Verify(x => x.IncrementCounter(\"stats:succeeded\"), Times.Once);\n        }\n\n        [Fact]\n        public void Unapply_ShouldDecrementStatistics()\n        {\n            var handler = new SucceededState.Handler();\n            handler.Unapply(_context.Object, _transactionMock.Object);\n\n            _transactionMock.Verify(x => x.DecrementCounter(\"stats:succeeded\"), Times.Once);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/StatisticsHistoryAttributeFacts.cs",
    "content": "﻿using System;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests\n{\n    public class StatisticsHistoryAttributeFacts\n    {\n        private readonly Mock<IStorageConnection> _connection;\n        private readonly StatisticsHistoryAttribute _filter;\n        private readonly Mock<IWriteOnlyTransaction> _transaction;\n        private readonly ElectStateContextMock _context;\n\n        public StatisticsHistoryAttributeFacts()\n        {\n            _connection = new Mock<IStorageConnection>();\n\n            _transaction = new Mock<IWriteOnlyTransaction>();\n            _connection.Setup(x => x.CreateWriteTransaction()).Returns(_transaction.Object);\n\n            _filter = new StatisticsHistoryAttribute();\n\n            _context = new ElectStateContextMock\n            {\n                ApplyContext =\n                {\n                    Connection = _connection,\n                    NewStateObject = new SucceededState(null, 11, 123),\n                    Transaction = _transaction\n                }\n            };\n        }\n\n        [Fact]\n        public void StatisticsHistoryFilter_ActsBefore_RetryFilter()\n        {\n            var statisticsHistoryFilter = new StatisticsHistoryAttribute();\n            var retryFilter = new AutomaticRetryAttribute();\n\n            Assert.True(statisticsHistoryFilter.Order > retryFilter.Order);\n        }\n\n        [Fact]\n        public void OnStateElection_IncrementsCounters_ForSucceededState()\n        {\n            _filter.OnStateElection(_context.Object);\n\n            VerifyCountersIncremented(\"stats:succeeded:\");\n        }\n\n        [Fact]\n        public void OnStateElection_IncrementsCounters_ForFailedState()\n        {\n            _context.ApplyContext.NewStateObject = new FailedState(new InvalidOperationException());\n\n            _filter.OnStateElection(_context.Object);\n\n            VerifyCountersIncremented(\"stats:failed:\");\n        }\n\n        [Fact]\n        public void OnStateElection_DoesNotCreateTransaction_ForUnsuitableState()\n        {\n            _context.ApplyContext.NewStateObject = new ProcessingState(\"server\", \"1\");\n\n            _filter.OnStateElection(_context.Object);\n\n            _connection.Verify(x => x.CreateWriteTransaction(), Times.Never);\n        }\n\n        private void VerifyCountersIncremented(string prefix)\n        {\n            var thisDay = DateTime.UtcNow.ToString(\"yyyy-MM-dd\");\n            var prevDay = DateTime.UtcNow.AddDays(-1).ToString(\"yyyy-MM-dd\");\n\n            var thisHour = DateTime.UtcNow.ToString(\"yyyy-MM-dd-HH\");\n            var prevHour = DateTime.UtcNow.AddHours(1).ToString(\"yyyy-MM-dd-HH\");\n\n            _transaction.Verify(x => x.IncrementCounter(\n                It.Is<string>(key => key == prefix + thisDay || key == prefix + prevDay),\n                It.Is<TimeSpan>(expire => expire.TotalDays >= 27)));\n\n            _transaction.Verify(x => x.IncrementCounter(\n                It.Is<string>(date => date == prefix + thisHour || date == prefix + prevHour),\n                TimeSpan.FromDays(1)));\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Storage/InvocationDataFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.Specialized;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Reflection;\nusing Hangfire.Common;\nusing Hangfire.Storage;\nusing Newtonsoft.Json;\nusing Xunit;\nusing System.Globalization;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing Newtonsoft.Json.Serialization;\n\n#pragma warning disable 618\n\nnamespace Hangfire.Core.Tests.Storage\n{\n    public class InvocationDataFacts\n    {\n        [Fact]\n        public void Ctor_InitializesAllTheGivenProperties()\n        {\n            var invocationData = new InvocationData(\"type\", \"method\", \"parameterTypes\", \"arguments\");\n\n            Assert.Equal(\"type\", invocationData.Type);\n            Assert.Equal(\"method\", invocationData.Method);\n            Assert.Equal(\"parameterTypes\", invocationData.ParameterTypes);\n            Assert.Equal(\"arguments\", invocationData.Arguments);\n            Assert.Null(invocationData.Queue);\n        }\n\n        [Fact]\n        public void Ctor_WithQueue_InitializesAllTheGivenProperties()\n        {\n            var invocationData = new InvocationData(\"type\", \"method\", \"parameterTypes\", \"arguments\", \"critical\");\n\n            Assert.Equal(\"type\", invocationData.Type);\n            Assert.Equal(\"method\", invocationData.Method);\n            Assert.Equal(\"parameterTypes\", invocationData.ParameterTypes);\n            Assert.Equal(\"arguments\", invocationData.Arguments);\n            Assert.Equal(\"critical\", invocationData.Queue);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_AllTheData()\n        {\n            var type = typeof(InvocationDataFacts);\n            var methodInfo = type.GetMethod(\"Sample\");\n\n            var serializedData = new InvocationData(\n                type.AssemblyQualifiedName,\n                // ReSharper disable once PossibleNullReferenceException\n                methodInfo.Name,\n                JobHelper.ToJson(new [] { typeof(string) }),\n                JobHelper.ToJson(new [] { JobHelper.ToJson(\"Hello\") }));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(type, job.Type);\n            Assert.Equal(methodInfo, job.Method);\n            Assert.Equal(\"Hello\", job.Args[0]);\n            Assert.Null(job.Queue);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_QueueProperty()\n        {\n            var serializedData = new InvocationData(\n                \"Hangfire.JobStorage, Hangfire.Core\",\n                \"GetConnection\",\n                String.Empty,\n                null,\n                \"critical\");\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(\"critical\", job.Queue);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesNullOrEmpty_ParameterTypesAndArguments()\n        {\n            var serializedData = new InvocationData(\n                \"Hangfire.JobStorage, Hangfire.Core\",\n                \"GetConnection\",\n                String.Empty,\n                null);\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(typeof(JobStorage), job.Type);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesTypesWithoutAssemblyName_FromMscorlibAssembly()\n        {\n            var serializedData = new InvocationData(\n                \"System.DateTime\",\n                \"IsLeapYear\",\n                \"[\\\"System.Int32\\\"]\",\n                \"[\\\"1\\\"]\");\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(typeof(DateTime), job.Type);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesPartialAssemblyNames()\n        {\n            var serializedData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"Empty\",\n                null,\n                null);\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(typeof(InvocationDataFacts), job.Type);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesFullAssemblyNames()\n        {\n            var serializedData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\",\n                \"Empty\",\n                null,\n                null);\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(typeof(InvocationDataFacts), job.Type);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesFullyQualifiedAssemblyNames_OfNonSignedAssembly_OfDifferentVersion()\n        {\n            try\n            {\n                GlobalConfiguration.Configuration.UseIgnoredAssemblyVersionTypeResolver();\n\n                var serializedData = new InvocationData(\n                    \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests, Version=9.9.9.9, Culture=neutral, PublicKeyToken=null\",\n                    \"Empty\",\n                    null,\n                    null);\n\n                var job = serializedData.Deserialize();\n\n                Assert.Equal(typeof(InvocationDataFacts), job.Type);\n            }\n            finally\n            {\n                GlobalConfiguration.Configuration.UseDefaultTypeResolver();\n            }\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesFullyQualifiedAssemblyNames_OfSignedAssembly_OfDifferentVersion()\n        {\n            try\n            {\n                GlobalConfiguration.Configuration.UseIgnoredAssemblyVersionTypeResolver();\n\n                var serializedData = new InvocationData(\n                    \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests, Version=9.9.9.9, Culture=neutral, PublicKeyToken=7cec85d7bea7798e\",\n                    \"Empty\",\n                    null,\n                    null);\n\n                var job = serializedData.Deserialize();\n\n                Assert.Equal(typeof(InvocationDataFacts), job.Type);\n            }\n            finally\n            {\n                GlobalConfiguration.Configuration.UseDefaultTypeResolver();\n            }\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesGenericTypes_WithFullyQualifiedAssemblyNames_OfSignedAssembly_OfDifferentVersion()\n        {\n            try\n            {\n                GlobalConfiguration.Configuration.UseIgnoredAssemblyVersionTypeResolver();\n\n                var serializedData = new InvocationData(\n                    \"Hangfire.Core.Tests.Storage.InvocationDataFacts+GenericType`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Hangfire.Core.Tests, Version=9.9.9.9, Culture=neutral, PublicKeyToken=7cec85d7bea7798e\",\n                    \"Method\",\n                    \"[\\\"System.Int32, System.Private.CoreLib, Version=9.9.9.9, Culture=neutral, PublicKeyToken=lalalalala\\\",\\\"System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e\\\"]\",\n                    \"[\\\"123\\\",\\\"456\\\"]\");\n\n                var job = serializedData.Deserialize();\n\n                Assert.Equal(typeof(GenericType<int>), job.Type);\n                Assert.Equal(\"Method\", job.Method.Name);\n                Assert.Equal(123, job.Args[0]);\n            }\n            finally\n            {\n                GlobalConfiguration.Configuration.UseDefaultTypeResolver();\n            }\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesSystemPrivateCoreLib_TypeForwarding()\n        {\n            var serializedData = new InvocationData(\n                \"System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e\",\n                \"IsNullOrEmpty\",\n                \"[\\\"System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e\\\"]\",\n                JobHelper.ToJson(new[] { JobHelper.ToJson(\"hello\") }));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(typeof(string), job.Type);\n            Assert.Equal(\"IsNullOrEmpty\", job.Method.Name);\n            Assert.Equal(\"hello\", job.Args[0]);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_WrapsAnException_WithTheJobLoadException()\n        {\n            var serializedData = new InvocationData(null, null, null, null);\n\n            Assert.Throws<JobLoadException>(\n                () => serializedData.Deserialize());\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_ThrowsAnException_WhenTypeCanNotBeFound()\n        {\n            var serializedData = new InvocationData(\n                \"NonExistingType\",\n                \"Perform\",\n                \"\",\n                \"\");\n\n            Assert.Throws<JobLoadException>(\n                () => serializedData.Deserialize());\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_ThrowsAnException_WhenMethodCanNotBeFound()\n        {\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                \"NonExistingMethod\",\n                JobHelper.ToJson(new [] { typeof(string) }),\n                \"\");\n\n            Assert.Throws<JobLoadException>(\n                () => serializedData.Deserialize());\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Serialize_CorrectlySerializesTheData()\n        {\n            var job = Job.FromExpression(() => Sample(\"Hello\"));\n\n            var invocationData = InvocationData.Serialize(job);\n\n            Assert.Equal(typeof(InvocationDataFacts).AssemblyQualifiedName, invocationData.Type);\n            Assert.Equal(\"Sample\", invocationData.Method);\n            Assert.Equal(JobHelper.ToJson(new[] { typeof(string) }), invocationData.ParameterTypes);\n            Assert.Equal(JobHelper.ToJson(new[] { \"\\\"Hello\\\"\" }), invocationData.Arguments);\n            Assert.Null(invocationData.Queue);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Serialize_CorrectlySerializes_TheJobQueueProperty()\n        {\n            var job = Job.FromExpression(() => Sample(\"Hello\"), \"critical\");\n\n            var invocationData = InvocationData.Serialize(job);\n\n            Assert.Equal(\"Sample\", invocationData.Method);\n            Assert.Equal(\"critical\", invocationData.Queue);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Serialize_CorrectlyHandles_ParameterTypes_InPossibleOldFormat()\n        {\n            var invocationData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"ComplicatedMethod\",\n                \"{\\\"$type\\\":\\\"System.Type[], mscorlib\\\",\\\"$values\\\":[\\\"System.Collections.Generic.IList`1[[System.String, mscorlib]], mscorlib\\\",\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts+SomeClass, Hangfire.Core.Tests\\\"]}\",\n                \"[null, null]\");\n\n            var serialized = invocationData.SerializePayload();\n            var job = InvocationData.DeserializePayload(serialized).Deserialize();\n\n            Assert.Equal(typeof(InvocationDataFacts), job.Type);\n            Assert.Equal(typeof(InvocationDataFacts).GetMethod(\"ComplicatedMethod\"), job.Method);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void Serialize_SerializesDateTimeUsingCustomFormatter_BeforeVersion170()\n        {\n            var dateTimeString = \"2019-03-05T13:20:04.5932150Z\";\n            var dateTime = DateTime.Parse(dateTimeString, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);\n            var dateTimeData = InvocationData.SerializeJob(Job.FromExpression(() => DateTimeMethod(dateTime)));\n            var nullableData = InvocationData.SerializeJob(Job.FromExpression(() => NullableDateTimeMethod(dateTime)));\n\n            Assert.Equal($\"[\\\"{dateTimeString}\\\"]\", dateTimeData.Arguments);\n            Assert.Equal($\"[\\\"{dateTimeString}\\\"]\", nullableData.Arguments);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void Serialize_SerializesDateTimeUsingRegularJsonFormatter_AfterVersion170()\n        {\n            var dateTimeString = \"\\\"2019-03-05T13:20:04.5932150Z\\\"\";\n            var dateTime = SerializationHelper.Deserialize<DateTime>(dateTimeString, SerializationOption.User);\n            var dateTimeData = InvocationData.SerializeJob(Job.FromExpression(() => DateTimeMethod(dateTime)));\n            var nullableData = InvocationData.SerializeJob(Job.FromExpression(() => NullableDateTimeMethod(dateTime)));\n\n            Assert.Equal($\"[\\\"\\\\\\\"2019-03-05T13:20:04.593215Z\\\\\\\"\\\"]\", dateTimeData.Arguments);\n            Assert.Equal($\"[\\\"\\\\\\\"2019-03-05T13:20:04.593215Z\\\\\\\"\\\"]\", nullableData.Arguments);\n        }\n\n        [DataCompatibilityRangeFact(), CleanSerializerSettings]\n        public void Serialize_WithTypeNameHandlingAuto_PreservesTypeInformation()\n        {\n            JobHelper.SetSerializerSettings(new JsonSerializerSettings\n            {\n                DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,\n                TypeNameHandling = TypeNameHandling.Auto\n            });\n\n            var job = Job.FromExpression(() => GenericMethod<object>(new SomeClass()));\n            var data = InvocationData.SerializeJob(job);\n\n            Assert.Equal(\"[\\\"{\\\\\\\"$type\\\\\\\":\\\\\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts+SomeClass, Hangfire.Core.Tests\\\\\\\"}\\\"]\", data.Arguments);\n        }\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void Deserialize_CanHandleArgumentWithExplicitTypeName_WhenUsingTypeNameHandlingAuto()\n        {\n            JobHelper.SetSerializerSettings(new JsonSerializerSettings\n            {\n                TypeNameHandling =  TypeNameHandling.Auto\n            });\n\n            var data = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"GenericMethod\",\n                \"[\\\"System.Object, mscorlib\\\"]\",\n                \"[\\\"{\\\\\\\"$type\\\\\\\":\\\\\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts+SomeClass, Hangfire.Core.Tests\\\\\\\"}\\\"]\");\n\n            var job = data.DeserializeJob();\n            Assert.Equal(\"GenericMethod\", job.Method.Name);\n            Assert.Equal(new object[] { typeof(object) }, job.Method.GetParameters().Select(x => x.ParameterType).ToArray());\n            Assert.IsType<SomeClass>(job.Args[0]);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void SerializePayload_CorrectlySerializesInvocationDataToString_WithOldFormat_InVersion_Pre_170()\n        {\n            var invocationData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"Sample\",\n                \"[\\\"System.String\\\"]\",\n                \"[\\\"\\\\\\\"Hello\\\\\\\"\\\"]\");\n\n            var payload = invocationData.SerializePayload();\n\n            Assert.Equal(\n                \"{\\\"Type\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"Method\\\":\\\"Sample\\\",\\\"ParameterTypes\\\":\\\"[\\\\\\\"System.String\\\\\\\"]\\\",\\\"Arguments\\\":\\\"[\\\\\\\"\\\\\\\\\\\\\\\"Hello\\\\\\\\\\\\\\\"\\\\\\\"]\\\"}\",\n                payload);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void SerializePayload_CorrectlySerializesInvocationDataWithQueueToString_WithOldFormat_InVersion_Pre_170()\n        {\n            var invocationData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"Sample\",\n                \"[\\\"System.String\\\"]\",\n                \"[\\\"\\\\\\\"Hello\\\\\\\"\\\"]\",\n                \"critical\");\n\n            var payload = invocationData.SerializePayload();\n\n            Assert.Equal(\n                \"{\\\"Type\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"Method\\\":\\\"Sample\\\",\\\"ParameterTypes\\\":\\\"[\\\\\\\"System.String\\\\\\\"]\\\",\\\"Arguments\\\":\\\"[\\\\\\\"\\\\\\\\\\\\\\\"Hello\\\\\\\\\\\\\\\"\\\\\\\"]\\\",\\\"Queue\\\":\\\"critical\\\"}\",\n                payload);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void SerializePayload_DoesNotIncludeArgumentsWhenStatedSo_WithOldFormat_InVersion_Pre_170()\n        {\n            var invocationData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"Sample\",\n                \"[\\\"System.String\\\"]\",\n                \"[\\\"\\\\\\\"Hello\\\\\\\"\\\"]\");\n\n            var payload = invocationData.SerializePayload(excludeArguments: true);\n\n            Assert.Equal(\n                \"{\\\"Type\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"Method\\\":\\\"Sample\\\",\\\"ParameterTypes\\\":\\\"[\\\\\\\"System.String\\\\\\\"]\\\",\\\"Arguments\\\":null}\",\n                payload);\n        }\n\n        [DataCompatibilityRangeFact(MaxExcludingLevel = CompatibilityLevel.Version_170)]\n        public void SerializePayload_SerializesInvocationDataToString_WithoutNullifyingEmptyEntries_InVersion_Pre_170()\n        {\n            var invocationData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"Empty\",\n                \"[]\",\n                \"[]\");\n\n            var payload = invocationData.SerializePayload();\n\n            Assert.Equal(\n                \"{\\\"Type\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"Method\\\":\\\"Empty\\\",\\\"ParameterTypes\\\":\\\"[]\\\",\\\"Arguments\\\":\\\"[]\\\"}\",\n                payload);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializePayload_CorrectlySerializesInvocationDataToString_WithNewFormat_InVersion_170()\n        {\n            var invocationData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"Sample\",\n                \"[\\\"System.String\\\"]\",\n                \"[\\\"\\\\\\\"Hello\\\\\\\"\\\"]\");\n\n            var payload = invocationData.SerializePayload();\n\n            Assert.Equal(\n                \"{\\\"t\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"m\\\":\\\"Sample\\\",\\\"p\\\":[\\\"System.String\\\"],\\\"a\\\":[\\\"\\\\\\\"Hello\\\\\\\"\\\"]}\",\n                payload);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializePayload_CorrectlySerializesInvocationDataWithQueueToString_WithNewFormat_InVersion_170()\n        {\n            var invocationData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"Sample\",\n                \"[\\\"System.String\\\"]\",\n                \"[\\\"\\\\\\\"Hello\\\\\\\"\\\"]\",\n                \"critical\");\n\n            var payload = invocationData.SerializePayload();\n\n            Assert.Equal(\n                \"{\\\"t\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"m\\\":\\\"Sample\\\",\\\"p\\\":[\\\"System.String\\\"],\\\"a\\\":[\\\"\\\\\\\"Hello\\\\\\\"\\\"],\\\"q\\\":\\\"critical\\\"}\",\n                payload);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializePayload_DoesNotIncludeArgumentsWhenStatedSo_WithNewFormat_InVersion_170()\n        {\n            var invocationData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"Sample\",\n                \"[\\\"System.String\\\"]\",\n                \"[\\\"\\\\\\\"Hello\\\\\\\"\\\"]\");\n\n            var payload = invocationData.SerializePayload(excludeArguments: true);\n\n            Assert.Equal(\n                \"{\\\"t\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"m\\\":\\\"Sample\\\",\\\"p\\\":[\\\"System.String\\\"]}\",\n                payload);\n        }\n\n        [DataCompatibilityRangeFact(MinLevel = CompatibilityLevel.Version_170)]\n        public void SerializePayload_SerializesInvocationDataToString_WithNullifyingEmptyEntries_InVersion_170()\n        {\n            var invocationData = new InvocationData(\n                \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\",\n                \"Empty\",\n                \"[]\",\n                \"[]\");\n\n            var payload = invocationData.SerializePayload();\n\n            Assert.Equal(\n                \"{\\\"t\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"m\\\":\\\"Empty\\\"}\",\n                payload);\n        }\n\n        [DataCompatibilityRangeTheory]\n\n        // Previous serialization format.\n        [InlineData(\"{\\\"$type\\\":\\\"Hangfire.Storage.InvocationData, Hangfire.Core\\\",\\\"Type\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\\\",\\\"Method\\\":\\\"Sample\\\",\\\"ParameterTypes\\\":\\\"{\\\\\\\"$type\\\\\\\":\\\\\\\"System.Type[], mscorlib\\\\\\\",\\\\\\\"$values\\\\\\\":[\\\\\\\"System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\\\\\\\"]}\\\",\\\"Arguments\\\":\\\"{\\\\\\\"$type\\\\\\\":\\\\\\\"System.String[], mscorlib\\\\\\\",\\\\\\\"$values\\\\\\\":[\\\\\\\"\\\\\\\\\\\\\\\"Hello\\\\\\\\\\\\\\\"\\\\\\\"]}\\\"}\")]\n        [InlineData(\"{\\\"Type\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\\\",\\\"Method\\\":\\\"Sample\\\",\\\"ParameterTypes\\\":\\\"{\\\\\\\"$type\\\\\\\":\\\\\\\"System.Type[], mscorlib\\\\\\\",\\\\\\\"$values\\\\\\\":[\\\\\\\"System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\\\\\\\"]}\\\",\\\"Arguments\\\":\\\"{\\\\\\\"$type\\\\\\\":\\\\\\\"System.String[], mscorlib\\\\\\\",\\\\\\\"$values\\\\\\\":[\\\\\\\"\\\\\\\\\\\\\\\"Hello\\\\\\\\\\\\\\\"\\\\\\\"]}\\\"}\")]\n        [InlineData(\"{\\\"$type\\\":\\\"Hangfire.Storage.InvocationData, Hangfire.Core\\\",\\\"Type\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\\\",\\\"Method\\\":\\\"Sample\\\",\\\"ParameterTypes\\\":\\\"[\\\\\\\"System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\\\\\\\"]\\\",\\\"Arguments\\\":\\\"[\\\\\\\"\\\\\\\\\\\\\\\"Hello\\\\\\\\\\\\\\\"\\\\\\\"]\\\"}\")]\n        [InlineData(\"{\\\"Type\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\\\",\\\"Method\\\":\\\"Sample\\\",\\\"ParameterTypes\\\":\\\"[\\\\\\\"System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\\\\\\\"]\\\",\\\"Arguments\\\":\\\"[\\\\\\\"\\\\\\\\\\\\\\\"Hello\\\\\\\\\\\\\\\"\\\\\\\"]\\\"}\")]\n\n        // New serialization format.\n        [InlineData(\"{\\\"t\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"m\\\":\\\"Sample\\\",\\\"p\\\":[\\\"System.String\\\"],\\\"a\\\":[\\\"\\\\\\\"Hello\\\\\\\"\\\"]}\")]\n        public void Deserialize_DeserializesCorrectlyStringToInvocationData(string invocationData)\n        {\n            try\n            {\n                JobHelper.SetSerializerSettings(new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });\n                var serializedData = InvocationData.DeserializePayload(invocationData);\n\n                var job = serializedData.Deserialize();\n\n                Assert.False(job.Type.GetTypeInfo().ContainsGenericParameters);\n                Assert.Equal(\"Sample\", job.Method.Name);\n                Assert.Equal(typeof(string), job.Method.GetParameters()[0].ParameterType);\n                Assert.Equal(1, job.Args.Count);\n                Assert.Equal(\"Hello\", job.Args[0]);\n            }\n            finally\n            {\n                JobHelper.SetSerializerSettings(null);\n            }\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_DeserializesCorrectlyShortFormatStringToInvocationData()\n        {\n            var invocationData = \"{\\\"t\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"m\\\":\\\"Empty\\\"}\";\n\n            var serializedData = InvocationData.DeserializePayload(invocationData);\n\n            var job = serializedData.Deserialize();\n\n            Assert.False(job.Type.GetTypeInfo().ContainsGenericParameters);\n            Assert.Equal(\"Empty\", job.Method.Name);\n            Assert.Empty(job.Method.GetParameters());\n            Assert.Equal(0, job.Args.Count);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesGenericTypes()\n        {\n            var serializedData = InvocationData.Serialize(\n                Job.FromExpression<GenericType<string>>(x => x.Method()));\n\n            var job = serializedData.Deserialize();\n\n            Assert.False(job.Type.GetTypeInfo().ContainsGenericParameters);\n            Assert.Equal(typeof(string), job.Type.GetGenericArguments()[0]);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesGenericMethods_WithOpenTypeParameters()\n        {\n            var serializedData = InvocationData.Serialize(\n                Job.FromExpression<GenericType<string>>(x => x.Method(\"asd\", 123)));\n\n            var job = serializedData.Deserialize();\n\n            Assert.False(job.Method.ContainsGenericParameters);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesMethodsDefinedInInterfaces()\n        {\n            var serializedData = new InvocationData(\n                typeof(IParent).AssemblyQualifiedName,\n                \"Method\",\n                JobHelper.ToJson(new Type[0]),\n                JobHelper.ToJson(new string[0]));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(typeof(IParent), job.Type);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_HandlesMethodsDefinedInParentInterfaces()\n        {\n            var serializedData = new InvocationData(\n                typeof(IChild).AssemblyQualifiedName,\n                \"Method\",\n                JobHelper.ToJson(new Type[0]),\n                JobHelper.ToJson(new string[0]));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(typeof(IChild), job.Type);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_RethrowsJsonException_InsteadOfNullValue_WhenReferenceConverterChosen()\n        {\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                \"ListMethod\",\n                JobHelper.ToJson(new [] { typeof(IList<string>) }),\n                JobHelper.ToJson(new [] { \"asdfasdf\" }));\n\n            var exception = Assert.Throws<JobLoadException>(() => serializedData.Deserialize());\n            Assert.IsType<JsonReaderException>(exception.InnerException);\n        }\n\n        [DataCompatibilityRangeTheory]\n        [MemberData(nameof(MemberData))]\n        public void Serialize_CorrectlySerializesJobToInvocationData(Job job, string typeName, string method, string parameterTypes, string serializedArgs)\n        {\n            try\n            {\n                InvocationData.SetTypeSerializer(TypeHelper.SimpleAssemblyTypeSerializer);\n\n                var invocationData = InvocationData.Serialize(job);\n\n                Assert.Equal(typeName, invocationData.Type);\n                Assert.Equal(method, invocationData.Method);\n                Assert.Equal(parameterTypes, invocationData.ParameterTypes);\n                Assert.Equal(serializedArgs, invocationData.Arguments);\n            }\n            finally\n            {\n                InvocationData.SetTypeSerializer(null);\n            }\n        }\n\n        [DataCompatibilityRangeTheory]\n        [MemberData(nameof(MemberData))]\n        public void Deserialize_CorrectlyDeserializesJobFromInvocationData(Job job, string typeName, string method, string parameterTypes, string serializedArgs)\n        {\n            var deserializedJob = new InvocationData(typeName, method, parameterTypes, serializedArgs).Deserialize();\n\n#if NETCOREAPP1_0 || NETCOREAPP2_1\n            Assert.Equal(job.Type.FullName, deserializedJob.Type.FullName);\n            Assert.Equal(job.Method.Name, deserializedJob.Method.Name);\n#else\n            Assert.Equal(job.Type, deserializedJob.Type);\n            Assert.Equal(job.Method, deserializedJob.Method);\n#endif\n\n            var parameters = job.Method.GetParameters();\n            var deserializedParameters = deserializedJob.Method.GetParameters();\n            for (var i = 0; i < parameters.Length; i++)\n            {\n                Assert.Equal(parameters[i].ParameterType, deserializedParameters[i].ParameterType);\n            }\n\n            for (var i = 0; i < job.Args.Count; i++)\n            {\n                Assert.Equal(job.Args[i], deserializedJob.Args[i]);\n            }\n        }\n\n        public static IEnumerable<object[]> MemberData\n        {\n            get\n            {\n                return new []\n                {\n                    new object[] { Job.FromExpression(() => Thread.Sleep(TimeSpan.FromSeconds(5))), \"System.Threading.Thread, mscorlib\", \"Sleep\", \"[\\\"System.TimeSpan, mscorlib\\\"]\", \"[\\\"\\\\\\\"00:00:05\\\\\\\"\\\"]\" },\n                    new object[] { Job.FromExpression(() => Console.WriteLine(\"4567\")), \"System.Console, mscorlib\", \"WriteLine\", \"[\\\"System.String\\\"]\", \"[\\\"\\\\\\\"4567\\\\\\\"\\\"]\" },\n                    new object[] { Job.FromExpression(() => Sample(\"str1\")), \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\", \"Sample\", \"[\\\"System.String\\\"]\", \"[\\\"\\\\\\\"str1\\\\\\\"\\\"]\" },\n                    new object[] { Job.FromExpression(() => ListMethod(new string[0])), \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\", \"ListMethod\", \"[\\\"System.Collections.Generic.IList`1[[System.String]], mscorlib\\\"]\", \"[\\\"[]\\\"]\" },\n\n                    new object[] { Job.FromExpression(() => GenericMethod(1)), \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\", \"GenericMethod\", \"[\\\"System.Int32\\\"]\", \"[\\\"1\\\"]\" },\n#if !NETCOREAPP1_0\n                    new object[] { Job.FromExpression(() => GenericMethod((StringDictionary)null)), \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\", \"GenericMethod\", \"[\\\"System.Collections.Specialized.StringDictionary, System\\\"]\", \"[null]\" },\n#endif\n                    new object[] { Job.FromExpression(() => GenericMethod((InvocationDataFacts)null)), \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\", \"GenericMethod\", \"[\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\"]\", \"[null]\" },\n                    new object[] { Job.FromExpression(() => GenericMethod((GlobalType)null)), \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\", \"GenericMethod\", \"[\\\"GlobalType, Hangfire.Core.Tests\\\"]\", \"[null]\" },\n                    new object[] { Job.FromExpression(() => OtherGenericMethod(1, new List<int>())), \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\", \"OtherGenericMethod\", \"[\\\"System.Int32\\\",\\\"System.Collections.Generic.List`1[[System.Int32]], mscorlib\\\"]\", \"[\\\"1\\\",\\\"[]\\\"]\" },\n\n                    new object[] { Job.FromExpression<NestedType>(x => x.Method()), \"Hangfire.Core.Tests.Storage.InvocationDataFacts+NestedType, Hangfire.Core.Tests\", \"Method\", \"[]\", \"[]\" },\n                    new object[] { Job.FromExpression<NestedType>(x => x.NestedGenericMethod(1)), \"Hangfire.Core.Tests.Storage.InvocationDataFacts+NestedType, Hangfire.Core.Tests\", \"NestedGenericMethod\", \"[\\\"System.Int32\\\"]\", \"[\\\"1\\\"]\" },\n\n                    new object[] { Job.FromExpression(() => ArrayOfNested(new NestedType[0])), \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\", \"ArrayOfNested\", \"[\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts+NestedType[], Hangfire.Core.Tests\\\"]\", \"[\\\"[]\\\"]\" },\n                    new object[] { Job.FromExpression(() => ArrayOfNestedGeneric(new NestedGenericType<int>[0])), \"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\", \"ArrayOfNestedGeneric\", \"[\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts+NestedGenericType`1[[System.Int32]][], Hangfire.Core.Tests\\\"]\", \"[\\\"[]\\\"]\" },\n\n                    new object[] { Job.FromExpression<GenericType<int>>(x => x.Method()), \"Hangfire.Core.Tests.Storage.InvocationDataFacts+GenericType`1[[System.Int32]], Hangfire.Core.Tests\", \"Method\", \"[]\", \"[]\" },\n                    new object[] { Job.FromExpression<GenericType<GlobalType>>(x => x.Method()), \"Hangfire.Core.Tests.Storage.InvocationDataFacts+GenericType`1[[GlobalType, Hangfire.Core.Tests]], Hangfire.Core.Tests\", \"Method\", \"[]\", \"[]\" },\n                    new object[] { Job.FromExpression<GenericType<InvocationDataFacts>>(x => x.Method()), \"Hangfire.Core.Tests.Storage.InvocationDataFacts+GenericType`1[[Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests]], Hangfire.Core.Tests\", \"Method\", \"[]\", \"[]\" },\n                    new object[] { Job.FromExpression<GenericType<int>>(x => x.Method(1, 1)), \"Hangfire.Core.Tests.Storage.InvocationDataFacts+GenericType`1[[System.Int32]], Hangfire.Core.Tests\", \"Method\", \"[\\\"System.Int32\\\",\\\"System.Int32\\\"]\", \"[\\\"1\\\",\\\"1\\\"]\" },\n                    new object[] { Job.FromExpression<GenericType<int>.NestedGenericType<string>>(x => x.Method(1, \"1\")), \"Hangfire.Core.Tests.Storage.InvocationDataFacts+GenericType`1+NestedGenericType`1[[System.Int32],[System.String]], Hangfire.Core.Tests\", \"Method\", \"[\\\"System.Int32\\\",\\\"System.String\\\"]\", \"[\\\"1\\\",\\\"\\\\\\\"1\\\\\\\"\\\"]\" },\n\n                    new object[] { Job.FromExpression<GlobalType>(x => x.Method()), \"GlobalType, Hangfire.Core.Tests\", \"Method\", \"[]\", \"[]\" },\n                    new object[] { Job.FromExpression<GlobalType>(x => x.GenericMethod(1)), \"GlobalType, Hangfire.Core.Tests\", \"GenericMethod\", \"[\\\"System.Int32\\\"]\", \"[\\\"1\\\"]\" },\n                    new object[] { Job.FromExpression<GlobalType.NestedType>(x => x.NestedMethod()), \"GlobalType+NestedType, Hangfire.Core.Tests\", \"NestedMethod\", \"[]\", \"[]\" },\n                    new object[] { Job.FromExpression<GlobalType.NestedGenericType<long>>(x => x.NestedGenericMethod(1, 1)), \"GlobalType+NestedGenericType`1[[System.Int64]], Hangfire.Core.Tests\", \"NestedGenericMethod\", \"[\\\"System.Int64\\\",\\\"System.Int32\\\"]\", \"[\\\"1\\\",\\\"1\\\"]\" },\n\n                    new object[] { Job.FromExpression<GlobalGenericType<int>>(x => x.Method()), \"GlobalGenericType`1[[System.Int32]], Hangfire.Core.Tests\", \"Method\", \"[]\", \"[]\" },\n                    new object[] { Job.FromExpression<GlobalGenericType<object>>(x => x.GenericMethod(1)), \"GlobalGenericType`1[[System.Object]], Hangfire.Core.Tests\", \"GenericMethod\", \"[\\\"System.Int32\\\"]\", \"[\\\"1\\\"]\" },\n                    new object[] { Job.FromExpression<GlobalGenericType<int>.NestedType>(x => x.Method()), \"GlobalGenericType`1+NestedType[[System.Int32]], Hangfire.Core.Tests\", \"Method\", \"[]\", \"[]\" },\n                    new object[] { Job.FromExpression<GlobalGenericType<long>.NestedGenericType<int>>(x => x.Method(1, 1)), \"GlobalGenericType`1+NestedGenericType`1[[System.Int64],[System.Int32]], Hangfire.Core.Tests\", \"Method\", \"[\\\"System.Int64\\\",\\\"System.Int32\\\"]\", \"[\\\"1\\\",\\\"1\\\"]\" },\n                };\n            }\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_LocalDateTimeArguments_ConvertedToRoundtripFormat()\n        {\n            var value = DateTime.Now;\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                nameof(DateTimeMethod),\n                JobHelper.ToJson(new[] { typeof(DateTime) }),\n                JobHelper.ToJson(new[] { value.ToString(\"o\", CultureInfo.InvariantCulture) }));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(value, (DateTime)job.Args[0]);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_UnknownDateTimeArguments_ConvertedToRoundtripFormat()\n        {\n            var value = new DateTime(2017, 1, 1, 1, 1, 1, 1, DateTimeKind.Unspecified);\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                nameof(DateTimeMethod),\n                JobHelper.ToJson(new[] { typeof(DateTime) }),\n                JobHelper.ToJson(new[] { value.ToString(\"o\", CultureInfo.InvariantCulture) }));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(value, (DateTime)job.Args[0]);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_UtcDateTimeArguments_ConvertedToRoundtripFormat()\n        {\n\n            var value = DateTime.UtcNow;\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                nameof(DateTimeMethod),\n                JobHelper.ToJson(new[] { typeof(DateTime) }),\n                JobHelper.ToJson(new[] { value.ToString(\"o\", CultureInfo.InvariantCulture) }));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(value, (DateTime)job.Args[0]);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_LocalDateTimeArguments_ConvertedToOldFormat_WithLoweredPrecision()\n        {\n            var value = DateTime.Now;\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                nameof(DateTimeMethod),\n                JobHelper.ToJson(new[] { typeof(DateTime) }),\n                JobHelper.ToJson(new[] { value.ToString(\"MM/dd/yyyy HH:mm:ss.ffff\", CultureInfo.InvariantCulture) }));\n\n            var job = serializedData.Deserialize();\n\n            var actualValue = (DateTime)job.Args[0];\n\n            Assert.Equal(value.Year, actualValue.Year);\n            Assert.Equal(value.Month, actualValue.Month);\n            Assert.Equal(value.Day, actualValue.Day);\n            Assert.Equal(value.Hour, actualValue.Hour);\n            Assert.Equal(value.Minute, actualValue.Minute);\n            Assert.Equal(value.Second, actualValue.Second);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_UnknownDateTimeArguments_ConvertedToOldFormat_WithLoweredPrecision()\n        {\n            var value = new DateTime(2017, 1, 1, 1, 1, 1, 1, DateTimeKind.Unspecified);\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                nameof(DateTimeMethod),\n                JobHelper.ToJson(new[] { typeof(DateTime) }),\n                JobHelper.ToJson(new[] { value.ToString(\"MM/dd/yyyy HH:mm:ss.ffff\", CultureInfo.InvariantCulture) }));\n\n            var job = serializedData.Deserialize();\n\n            var actualValue = (DateTime)job.Args[0];\n\n            Assert.Equal(value.Year, actualValue.Year);\n            Assert.Equal(value.Month, actualValue.Month);\n            Assert.Equal(value.Day, actualValue.Day);\n            Assert.Equal(value.Hour, actualValue.Hour);\n            Assert.Equal(value.Minute, actualValue.Minute);\n            Assert.Equal(value.Second, actualValue.Second);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_UtcDateTimeArguments_ConvertedToOldFormat_WithLoweredPrecision()\n        {\n            var value = DateTime.UtcNow;\n\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                nameof(DateTimeMethod),\n                JobHelper.ToJson(new[] { typeof(DateTime) }),\n                JobHelper.ToJson(new[] { value.ToString(\"MM/dd/yyyy HH:mm:ss.ffff\", CultureInfo.InvariantCulture) }));\n\n            var job = serializedData.Deserialize();\n\n            var actualValue = (DateTime)job.Args[0];\n\n            Assert.Equal(value.Year, actualValue.Year);\n            Assert.Equal(value.Month, actualValue.Month);\n            Assert.Equal(value.Day, actualValue.Day);\n            Assert.Equal(value.Hour, actualValue.Hour);\n            Assert.Equal(value.Minute, actualValue.Minute);\n            Assert.Equal(value.Second, actualValue.Second);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_NullableUtcDateTimeArguments()\n        {\n            DateTime? value = DateTime.UtcNow;\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                nameof(NullableDateTimeMethod),\n                JobHelper.ToJson(new[] { typeof(DateTime?) }),\n                JobHelper.ToJson(new[] { value.Value.ToString(\"o\", CultureInfo.InvariantCulture) }));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(value, job.Args[0]);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlySeserializes_NullableUtcDateTimeArguments_With_Null()\n        {\n            DateTime? value = null;\n\n            var serializedData = InvocationData.Serialize(Job.FromExpression(() => NullableDateTimeMethod(value)));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(value, job.Args[0]);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_NullableLocalDateTimeArguments()\n        {\n            DateTime? value = DateTime.Now;\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                nameof(NullableDateTimeMethod),\n                JobHelper.ToJson(new[] { typeof(DateTime?) }),\n                JobHelper.ToJson(new[] { value.Value.ToString(\"o\", CultureInfo.InvariantCulture) }));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(value, job.Args[0]);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_NullableDateTimeArguments_With_Null_Value()\n        {\n            DateTime? value = null;\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                nameof(NullableDateTimeMethod),\n                JobHelper.ToJson(new[] { typeof(DateTime?) }),\n                JobHelper.ToJson(new[] { value }));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(value, job.Args[0]);\n        }\n\n        [DataCompatibilityRangeFact]\n        public void Deserialize_CorrectlyDeserializes_DateTimesInRegularJsonFormat()\n        {\n            var dateTimeString = \"\\\"2019-03-05T13:20:04.5932150Z\\\"\";\n            var dateTime = SerializationHelper.Deserialize<DateTime>(dateTimeString, SerializationOption.User);\n\n            var dateTimeJob = new InvocationData(\n                GetType().AssemblyQualifiedName,\n                \"DateTimeMethod\",\n                JobHelper.ToJson(new [] { typeof(DateTime) }),\n                \"[\\\"\\\\\\\"2019-03-05T13:20:04.593215Z\\\\\\\"\\\"]\").DeserializeJob();\n\n            var nullableJob = new InvocationData(\n                GetType().AssemblyQualifiedName,\n                \"NullableDateTimeMethod\",\n                JobHelper.ToJson(new[] { typeof(DateTime?) }),\n                \"[\\\"\\\\\\\"2019-03-05T13:20:04.593215Z\\\\\\\"\\\"]\").DeserializeJob();\n\n            Assert.Equal(dateTime, dateTimeJob.Args[0]);\n            Assert.Equal(dateTime, nullableJob.Args[0]);\n        }\n\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void Deserialize_HandlesChangingProcessOfInternalDataSerialization()\n        {\n            SerializationHelper.SetUserSerializerSettings(SerializerSettingsHelper.DangerousSettings);\n\n            var serializedData = new InvocationData(\n                typeof(InvocationDataFacts).AssemblyQualifiedName,\n                \"ComplicatedMethod\",\n                SerializationHelper.Serialize(new[]\n                {\n                    typeof(IList<string>),\n                    typeof(SomeClass)\n                }, SerializationOption.User),\n                SerializationHelper.Serialize(new[]\n                {\n                    SerializationHelper.Serialize(new List<string> { \"one\", \"two\" }, SerializationOption.User),\n                    SerializationHelper.Serialize(new SomeClass { StringValue = \"value\" }, SerializationOption.User)\n                }, SerializationOption.User));\n\n            var job = serializedData.Deserialize();\n\n            Assert.Equal(typeof(InvocationDataFacts), job.Type);\n            Assert.Equal(2, job.Args.Count);\n\n            Assert.Equal(typeof(List<string>), job.Args[0]?.GetType());\n            Assert.Equal(\"one\", (job.Args[0] as List<string>)?[0]);\n            Assert.Equal(\"two\", (job.Args[0] as List<string>)?[1]);\n\n            Assert.Equal(typeof(SomeClass), job.Args[1]?.GetType());\n            Assert.Equal(\"value\", (job.Args[1] as SomeClass)?.StringValue);\n            Assert.Equal(0, (job.Args[1] as SomeClass)?.DefaultValue);\n            Assert.Null((job.Args[1] as SomeClass)?.NullObject);\n        }\n\n#if !NET452 && !NET461\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void DeserializeJob_CanPreviousFormat_WhenTypeNameHandlingOptionIsSetToAll()\n        {\n            var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };\n\n            JsonConvert.DefaultSettings = () => settings;\n#pragma warning disable 618\n            JobHelper.SetSerializerSettings(settings);\n#pragma warning restore 618\n\n            var job = InvocationData\n                .DeserializePayload(\"{\\\"$type\\\":\\\"Hangfire.Storage.InvocationData, Hangfire.Core\\\",\\\"Type\\\":\\\"System.Console, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\\\",\\\"Method\\\":\\\"WriteLine\\\",\\\"ParameterTypes\\\":\\\"{\\\\\\\"$type\\\\\\\":\\\\\\\"System.Type[], mscorlib\\\\\\\",\\\\\\\"$values\\\\\\\":[\\\\\\\"System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\\\\\\\"]}\\\",\\\"Arguments\\\":\\\"{\\\\\\\"$type\\\\\\\":\\\\\\\"System.String[], mscorlib\\\\\\\",\\\\\\\"$values\\\\\\\":[\\\\\\\"\\\\\\\\\\\\\\\"Hello \\\\\\\\\\\\\\\"\\\\\\\"]}\\\"}\")\n                .DeserializeJob();\n\n            Assert.Equal(\"System.Console\", job.Type.FullName);\n            Assert.Equal(\"WriteLine\", job.Method.Name);\n            Assert.Equal(\"Hello \", job.Args[0]);\n        }\n#endif\n\n        [Fact]\n        public void DeserializePayload_ThrowsAnException_WhenPayloadIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => InvocationData.DeserializePayload(null));\n\n            Assert.Equal(\"payload\", exception.ParamName);\n        }\n\n        // https://github.com/HangfireIO/Hangfire/issues/1470\n        [DataCompatibilityRangeFact, CleanSerializerSettings]\n        public void DeserializePayload_CanHandleFieldBasedSerialization_OfInvocationDataClass()\n        {\n#pragma warning disable 618\n            JobHelper.SetSerializerSettings(new JsonSerializerSettings { ContractResolver = new FieldsOnlyContractResolver() });\n#pragma warning restore 618\n            var payload = \"{\\\"<Type>k__BackingField\\\":\\\"System.Console, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\\\",\\\"<Method>k__BackingField\\\":\\\"WriteLine\\\",\\\"<ParameterTypes>k__BackingField\\\":\\\"[]\\\",\\\"<Arguments>k__BackingField\\\":\\\"[]\\\"}\";\n\n            var data = InvocationData.DeserializePayload(payload);\n\n            Assert.StartsWith(\"System.Console\", data.Type);\n            Assert.Equal(\"WriteLine\", data.Method);\n            Assert.Equal(\"[]\", data.ParameterTypes);\n            Assert.Equal(\"[]\", data.Arguments);\n        }\n\n        [DataCompatibilityRangeTheory]\n        [InlineData(\"{\\\"Type\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\\\",\\\"Method\\\":\\\"Sample\\\",\\\"ParameterTypes\\\":\\\"[\\\\\\\"System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\\\\\\\"]\\\",\\\"Arguments\\\":\\\"[\\\\\\\"\\\\\\\\\\\\\\\"Hello\\\\\\\\\\\\\\\"\\\\\\\"]\\\",\\\"Queue\\\":\\\"critical\\\"}\")]\n        [InlineData(\"{\\\"t\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"m\\\":\\\"Sample\\\",\\\"p\\\":[\\\"System.String\\\"],\\\"a\\\":[\\\"\\\\\\\"Hello\\\\\\\"\\\"],\\\"q\\\":\\\"critical\\\"}\")]\n        public void DeserializePayload_WithQueueNameSet_ReturnsCorrectInvocationData(string payload)\n        {\n            var invocationData = InvocationData.DeserializePayload(payload);\n\n            Assert.Contains(\"InvocationDataFacts\", invocationData.Type);\n            Assert.Equal(\"Sample\", invocationData.Method);\n            Assert.Equal(\"critical\", invocationData.Queue);\n        }\n\n        [Fact]\n        public void Deserialize_CorrectlyHandles_SystemXmlLinqEntities_SerializedWithNETFramework()\n        {\n            var job = InvocationData.DeserializePayload(\n                \"{\\\"t\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"m\\\":\\\"XmlLinqMethod\\\",\\\"p\\\":[\\\"System.Xml.Linq.XElement, System.Xml.Linq\\\"],\\\"a\\\":[\\\"{\\\\\\\"element\\\\\\\":\\\\\\\"This is a test\\\\\\\"}\\\"]}\")\n                .DeserializeJob();\n\n            Assert.Equal(typeof(InvocationDataFacts), job.Type);\n        }\n\n        [Fact]\n        public void Deserialize_CorrectlyHandles_SystemXmlLinqEntities_SerializedWithNETCore()\n        {\n            var job = InvocationData.DeserializePayload(\n                \"{\\\"t\\\":\\\"Hangfire.Core.Tests.Storage.InvocationDataFacts, Hangfire.Core.Tests\\\",\\\"m\\\":\\\"XmlLinqMethod\\\",\\\"p\\\":[\\\"System.Xml.Linq.XElement, System.Private.Xml.Linq\\\"],\\\"a\\\":[\\\"{\\\\\\\"element\\\\\\\":\\\\\\\"This is a test\\\\\\\"}\\\"]}\")\n                .DeserializeJob();\n\n            Assert.Equal(typeof(InvocationDataFacts), job.Type);\n        }\n\n        private class FieldsOnlyContractResolver: DefaultContractResolver \n        {\n            protected override List<MemberInfo> GetSerializableMembers(Type objectType)\n                => objectType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)\n                    .Cast<MemberInfo>()\n                    .ToList();\n\n            protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) \n                => base.CreateProperties(type, MemberSerialization.Fields);\n        }\n\n        [UsedImplicitly]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        public static void Empty()\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Sample(string arg)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void ListMethod(IList<string> arg)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void GenericMethod<T>(T arg)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void OtherGenericMethod<T1,T2>(T1 arg1, T2 arg2)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        public static void DateTimeMethod(DateTime arg)\n        {\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        public static void NullableDateTimeMethod(DateTime? arg)\n        {\n        }\n\n        [UsedImplicitly]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void XmlLinqMethod(System.Xml.Linq.XElement value)\n        {\n        }\n\n        [UsedImplicitly]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        public static void ArrayOfNested(NestedType[] value)\n        {\n        }\n\n        [UsedImplicitly]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        public static void ArrayOfNestedGeneric(NestedGenericType<int>[] value)\n        {\n        }\n\n        [UsedImplicitly]\n        public class GenericType<T1>\n        {\n            public void Method()\n            {\n            }\n\n            public void Method<T2>(T1 arg1, T2 arg2)\n            {\n            }\n\n            public class NestedGenericType<T2>\n            {\n                public void Method(T1 arg1, T2 arg2)\n                {\n                }\n            }\n        }\n\n        [UsedImplicitly]\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        public static void ComplicatedMethod(IList<string> arg, SomeClass objArg)\n        {\n        }\n\n        public class SomeClass\n        {\n            public string StringValue { get; set; }\n            public object NullObject { get; set; }\n            public int DefaultValue { get; set; }\n        }\n\n        [UsedImplicitly]\n        public class NestedType\n        {\n            [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n            [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n            public void Method() { }\n\n            [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n            [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n            public void NestedGenericMethod<T>(T arg1) { }\n        }\n\n        [UsedImplicitly]\n        public class NestedGenericType<T>\n        {\n            [UsedImplicitly]\n            public T Value { get; set; }\n        }\n\n        public interface IParent\n        {\n            void Method();\n        }\n\n        public interface IChild : IParent\n        {\n        }\n    }\n\n}\n\n[UsedImplicitly]\n[SuppressMessage(\"Design\", \"CA1050:Declare types in namespaces\")]\npublic class GlobalType\n{\n    [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n    [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n    public void Method() {}\n\n    [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n    [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n    public void GenericMethod<T>(T arg) {}\n\n    [UsedImplicitly]\n    public class NestedType\n    {\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        [SuppressMessage(\"Performance\", \"CA1822:Mark members as static\")]\n        public void NestedMethod() { }\n    }\n\n    [UsedImplicitly]\n    public class NestedGenericType<T>\n    {\n        public void NestedGenericMethod<T1>(T arg1, T1 arg2) { }\n    }\n}\n\n[UsedImplicitly]\n[SuppressMessage(\"Design\", \"CA1050:Declare types in namespaces\")]\npublic class GlobalGenericType<T>\n{\n    public void Method() { }\n    public void GenericMethod<T1>(T1 arg) { }\n\n    [UsedImplicitly]\n    public class NestedType\n    {\n        [SuppressMessage(\"ReSharper\", \"MemberCanBeMadeStatic.Global\")]\n        public void Method() { }\n    }\n\n    [UsedImplicitly]\n    public class NestedGenericType<T1>\n    {\n        public void Method(T arg1, T1 arg2) { }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Storage/MonitoringTypeFacts.cs",
    "content": "﻿using System.Collections.Generic;\nusing Hangfire.Storage.Monitoring;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Storage\n{\n    public class MonitoringTypeFacts\n    {\n        [Fact]\n        public void EnqueuedJobDto_Ctor_SetsInEnqueuedState()\n        {\n            Assert.True(new EnqueuedJobDto().InEnqueuedState);\n        }\n\n        [Fact]\n        public void FailedJobDto_Ctor_SetsInFailedState()\n        {\n            Assert.True(new FailedJobDto().InFailedState);\n        }\n\n        [Fact]\n        public void ProcessingJobDto_Ctor_SetsInProcessingState()\n        {\n            Assert.True(new ProcessingJobDto().InProcessingState);\n        }\n\n        [Fact]\n        public void ScheduledJobDto_Ctor_SetsInScheduledState()\n        {\n            Assert.True(new ScheduledJobDto().InScheduledState);\n        }\n\n        [Fact]\n        public void SucceededJobDto_Ctor_SetsInSucceededState()\n        {\n            Assert.True(new SucceededJobDto().InSucceededState);\n        }\n\n        [Fact]\n        public void DeletedJobDto_Ctor_SetsInDeletedState()\n        {\n            Assert.True(new DeletedJobDto().InDeletedState);\n        }\n\n        [Fact]\n        public void JobList_Ctor_ShouldInitializeCollection()\n        {\n            var list = new JobList<int>(new Dictionary<string, int> { { \"1\", 2 } });\n            Assert.Single(list);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Storage/StorageConnectionExtensionsFacts.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.Core.Tests.Storage\n{\n    public class StorageConnectionExtensionsFacts\n    {\n        private readonly Mock<IStorageConnection> _connection;\n\n        public StorageConnectionExtensionsFacts()\n        {\n            _connection = new Mock<IStorageConnection>();\n        }\n\n        [Fact]\n        public void GivenRecurringJobIsCancelledWhenGetRecurringJobsThenNotGetStateData()\n        {\n            _connection.Setup(o => o.GetAllItemsFromSet(It.IsAny<string>())).Returns(new HashSet<string> { \"1\" });\n            _connection.Setup(o => o.GetAllEntriesFromHash(\"recurring-job:1\")).Returns(new Dictionary<string, string>\n                {\n                    { \"Cron\", \"A\"},\n                    { \"Job\", @\"{\"\"Type\"\":\"\"ConsoleApplication1.CommandHandler, ConsoleApplication1\"\",\"\"Method\"\":\"\"Handle\"\",\"\"ParameterTypes\"\":\"\"[\\\"\"string\\\"\"]\"\",\"\"Arguments\"\":\"\"[\\\"\"Text\\\"\"]\"\"}\"},\n                    { \"LastJobId\", string.Empty}\n                }).Verifiable();\n\n            var result = _connection.Object.GetRecurringJobs().Single();\n\n            Assert.Null(result.LastJobState);\n            _connection.VerifyAll();\n        }\n\n        [Fact]\n        public void AcquireDistributedJobLock_AcquiresALock_WithTheCorrectResource()\n        {\n            var timeout = TimeSpan.FromSeconds(5);\n\n            _connection.Object.AcquireDistributedJobLock(\"some-id\", timeout);\n\n            _connection.Verify(x => x.AcquireDistributedLock(\n                \"job:some-id:state-lock\",\n                timeout));\n        }\n\n        [Fact]\n        public void GetRecurringJobs_WithGivenIdentifiers_QueriesForCorrespondingJobs()\n        {\n            // Act\n            var result = _connection.Object.GetRecurringJobs(new[] { \"a\", \"b\" });\n\n            // Assert\n            Assert.Equal(2, result.Count);\n            Assert.True(result[0].Removed);\n            Assert.True(result[1].Removed);\n\n            _connection.Verify(x => x.GetAllEntriesFromHash(\"recurring-job:a\"), Times.Once);\n            _connection.Verify(x => x.GetAllEntriesFromHash(\"recurring-job:b\"), Times.Once);\n        }\n\n\t    [Fact]\n\t    public void GetRecurringJobsWithNullDateTimeHashValues() \n\t\t{\n\t\t\t_connection.Setup(o => o.GetAllItemsFromSet(It.IsAny<string>())).Returns(new HashSet<string> { \"1\" });\n\t\t    _connection.Setup(o => o.GetAllEntriesFromHash(\"recurring-job:1\")).Returns(new Dictionary<string, string>\n\t\t    {\n\t\t\t    { \"Cron\", \"A\"},\n\t\t\t    { \"Job\", @\"{\"\"Type\"\":\"\"ConsoleApplication1.CommandHandler, ConsoleApplication1\"\",\"\"Method\"\":\"\"Handle\"\",\"\"ParameterTypes\"\":\"\"[\\\"\"string\\\"\"]\"\",\"\"Arguments\"\":\"\"[\\\"\"Text\\\"\"]\"\"}\"},\n\t\t\t\t{ \"NextExecution\", null },\n\t\t\t    { \"LastExecution\", null },\n\t\t\t    { \"CreatedAt\", null }\n\t\t    }).Verifiable();\n\n\t\t\tvar result = _connection.Object.GetRecurringJobs();\n\t\t\tAssert.Null(result[0].LastExecution);\n\t\t\tAssert.Null(result[0].NextExecution);\n\t\t\tAssert.Null(result[0].CreatedAt);\n\t\t\t_connection.VerifyAll();\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Stubs/DashboardContextStub.cs",
    "content": "﻿using Hangfire.Dashboard;\n\nnamespace Hangfire.Core.Tests.Stubs\n{\n    class DashboardContextStub : DashboardContext\n    {\n        public DashboardContextStub(DashboardOptions options) : base(new JobStorageStub(), options)\n        {\n            Response = new DashboardResponseStub();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Stubs/DashboardResponseStub.cs",
    "content": "using System;\nusing System.IO;\nusing System.Threading.Tasks;\nusing Hangfire.Dashboard;\n\nnamespace Hangfire.Core.Tests.Stubs\n{\n    class DashboardResponseStub : DashboardResponse\n    {\n        public override string ContentType { get; set; }\n        public override int StatusCode { get; set; }\n        public override Stream Body { get; }\n        public override void SetExpire(DateTimeOffset? value)\n        {\n            throw new NotImplementedException();\n        }\n\n        public override Task WriteAsync(string text)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Stubs/JobStorageStub.cs",
    "content": "﻿using System;\nusing Hangfire.Storage;\n\nnamespace Hangfire.Core.Tests.Stubs\n{\n    class JobStorageStub : JobStorage\n    {\n        public override IMonitoringApi GetMonitoringApi()\n        {\n            throw new NotImplementedException();\n        }\n\n        public override IStorageConnection GetConnection()\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/CleanSerializerSettingsAttribute.cs",
    "content": "﻿using System.Reflection;\nusing Hangfire.Common;\nusing Newtonsoft.Json;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    internal sealed class CleanSerializerSettingsAttribute : BeforeAfterTestAttribute\n    {\n        public override void Before(MethodInfo methodUnderTest)\n        {\n            ClearSettings();\n        }\n\n        public override void After(MethodInfo methodUnderTest)\n        {\n            ClearSettings();\n        }\n\n        private static void ClearSettings()\n        {\n#pragma warning disable 618\n            JobHelper.SetSerializerSettings(null);\n#pragma warning restore 618\n            SerializationHelper.SetUserSerializerSettings(null);\n#if !NET452 && !NET461\n            JsonConvert.DefaultSettings = null;\n#endif\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/CultureHelper.cs",
    "content": "﻿using System.Globalization;\nusing System.Threading;\n\nnamespace Hangfire.Core.Tests\n{\n    internal static class CultureHelper\n    {\n        public static void SetCurrentCulture(CultureInfo cultureInfo)\n        {\n#if !NETCOREAPP1_0\n            Thread.CurrentThread.CurrentCulture = cultureInfo;\n#else\n            CultureInfo.CurrentCulture = cultureInfo;\n#endif\n        }\n\n        public static void SetCurrentUICulture(CultureInfo cultureInfo)\n        {\n#if !NETCOREAPP1_0\n            Thread.CurrentThread.CurrentUICulture = cultureInfo;\n#else\n            CultureInfo.CurrentUICulture = cultureInfo;\n#endif\n        }\n\n        public static void SetCurrentCulture(string id)\n        {\n#if !NETCOREAPP1_0\n            Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(id);\n#else\n            CultureInfo.CurrentCulture = new CultureInfo(id);\n#endif\n        }\n\n        public static void SetCurrentUICulture(string id)\n        {\n#if !NETCOREAPP1_0\n            Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(id);\n#else\n            CultureInfo.CurrentUICulture = new CultureInfo(id);\n#endif\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/DataCompatibilityRangeFactAttribute.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Xunit;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    [XunitTestCaseDiscoverer(\"Hangfire.Core.Tests.DataCompatibilityRangeFactDiscoverer\", \"Hangfire.Core.Tests\")]\n    [AttributeUsage(AttributeTargets.Method)]\n    internal sealed class DataCompatibilityRangeFactAttribute : FactAttribute\n    {\n        internal static readonly CompatibilityLevel PossibleMinLevel;\n        internal static readonly CompatibilityLevel PossibleMaxExcludingLevel;\n\n        static DataCompatibilityRangeFactAttribute()\n        {\n            var compatibilityLevels = Enum.GetValues(typeof(CompatibilityLevel))\n                .Cast<CompatibilityLevel>()\n                .ToArray();\n\n            PossibleMinLevel = compatibilityLevels.Min();\n            PossibleMaxExcludingLevel = compatibilityLevels.Max() + 1;\n        }\n\n        public DataCompatibilityRangeFactAttribute()\n        {\n            MinLevel = PossibleMinLevel;\n            MaxExcludingLevel = PossibleMaxExcludingLevel;\n        }\n\n        public CompatibilityLevel MinLevel { get; set; }\n        public CompatibilityLevel MaxExcludingLevel { get; set; }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/DataCompatibilityRangeFactDiscoverer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Hangfire.Annotations;\nusing Xunit.Abstractions;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    [UsedImplicitly]\n    internal sealed class DataCompatibilityRangeFactDiscoverer : IXunitTestCaseDiscoverer\n    {\n        public IMessageSink DiagnosticMessageSink { get; }\n\n        public DataCompatibilityRangeFactDiscoverer(IMessageSink diagnosticMessageSink)\n        {\n            DiagnosticMessageSink = diagnosticMessageSink;\n        }\n\n        public IEnumerable<IXunitTestCase> Discover(\n            ITestFrameworkDiscoveryOptions discoveryOptions,\n            ITestMethod testMethod,\n            IAttributeInfo factAttribute)\n        {\n            if (testMethod.Method.GetParameters().Any())\n            {\n                yield return new ExecutionErrorTestCase(\n                    DiagnosticMessageSink,\n                    discoveryOptions.MethodDisplayOrDefault(),\n                    testMethod,\n                    \"[CompatibilityLevelFact] methods are not allowed to have parameters. Did you mean to use [CompatibilityLevelTheory]?\");\n            }\n            else if (testMethod.Method.IsGenericMethodDefinition)\n            {\n                yield return new ExecutionErrorTestCase(\n                    DiagnosticMessageSink,\n                    discoveryOptions.MethodDisplayOrDefault(),\n                    testMethod, \n                    \"[CompatibilityLevelFact] methods are not allowed to be generic.\");\n            }\n            else\n            {\n                var compatibilityLevels = GetAllowedCompatibilityLevels(factAttribute);\n\n                foreach (var compatibilityLevel in compatibilityLevels)\n                {\n                    yield return new DataCompatibilityRangeTestCase(\n                        DiagnosticMessageSink,\n                        discoveryOptions.MethodDisplayOrDefault(),\n                        testMethod,\n                        new object[] { compatibilityLevel });\n                }\n            }\n        }\n\n        internal static CompatibilityLevel[] GetAllowedCompatibilityLevels(IAttributeInfo attributeInfo)\n        {\n            var compatibilityLevels = Enum.GetValues(typeof(CompatibilityLevel))\n                .Cast<CompatibilityLevel>()\n                .ToArray();\n\n            var minLevel = attributeInfo.GetNamedArgument<CompatibilityLevel>(\"MinLevel\");\n            var maxExcludingLevel = attributeInfo.GetNamedArgument<CompatibilityLevel>(\"MaxExcludingLevel\");\n\n            var result = new List<CompatibilityLevel>();\n\n            foreach (var compatibilityLevel in compatibilityLevels)\n            {\n                if (compatibilityLevel >= minLevel && compatibilityLevel < maxExcludingLevel)\n                {\n                    result.Add(compatibilityLevel);\n                }\n            }\n\n            return result.ToArray();\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/DataCompatibilityRangeTestCase.cs",
    "content": "﻿using System.Threading;\nusing System.Threading.Tasks;\nusing Xunit.Abstractions;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    internal sealed class DataCompatibilityRangeTestCase : XunitTestCase\n    {\n#pragma warning disable 618\n        public DataCompatibilityRangeTestCase()\n#pragma warning restore 618\n        {\n        }\n\n        public DataCompatibilityRangeTestCase(\n            IMessageSink diagnosticMessageSink,\n            TestMethodDisplay defaultMethodDisplay,\n            ITestMethod testMethod,\n            object[] testMethodArguments)\n            : base(diagnosticMessageSink, defaultMethodDisplay, testMethod, testMethodArguments)\n        {\n        }\n\n        public override Task<RunSummary> RunAsync(\n            IMessageSink diagnosticMessageSink, \n            IMessageBus messageBus, \n            object[] constructorArguments,\n            ExceptionAggregator aggregator, \n            CancellationTokenSource cancellationTokenSource)\n        {\n            return new DataCompatibilityRangeTestCaseRunner(\n                    this,\n                    DisplayName,\n                    SkipReason,\n                    constructorArguments,\n                    TestMethodArguments,\n                    messageBus,\n                    aggregator,\n                    cancellationTokenSource)\n                .RunAsync();\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/DataCompatibilityRangeTestCaseRunner.cs",
    "content": "﻿using System.Threading;\nusing System.Threading.Tasks;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    internal sealed class DataCompatibilityRangeTestCaseRunner : XunitTestCaseRunner\n    {\n        public DataCompatibilityRangeTestCaseRunner(\n            IXunitTestCase testCase,\n            string displayName,\n            string skipReason,\n            object[] constructorArguments,\n            object[] testMethodArguments,\n            IMessageBus messageBus,\n            ExceptionAggregator aggregator,\n            CancellationTokenSource cancellationTokenSource)\n            : base(testCase, displayName, skipReason, constructorArguments, testMethodArguments, messageBus, aggregator,\n                cancellationTokenSource)\n        {\n        }\n\n        protected override Task<RunSummary> RunTestAsync()\n        {\n            return new DataCompatibilityRangeTestRunner(new XunitTest(TestCase, DisplayName), MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments, SkipReason, BeforeAfterAttributes, new ExceptionAggregator(Aggregator), CancellationTokenSource)\n                .RunAsync();\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/DataCompatibilityRangeTestRunner.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Xunit.Abstractions;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    internal sealed class DataCompatibilityRangeTestRunner : XunitTestRunner\n    {\n        private static readonly SemaphoreSlim SyncRoot = new SemaphoreSlim(1, 1);\n\n        public DataCompatibilityRangeTestRunner(\n            ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, object[] testMethodArguments, string skipReason, IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) : base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource)\n        {\n        }\n\n        protected override async Task<Tuple<decimal, string>> InvokeTestAsync(ExceptionAggregator aggregator)\n        {\n            CompatibilityLevel? oldCompatibilityLevel = null;\n\n            try\n            {\n                await SyncRoot.WaitAsync(CancellationTokenSource.Token);\n\n                var compatibilityLevel = (CompatibilityLevel)TestMethodArguments[TestMethodArguments.Length - 1];\n                TestMethodArguments = TestMethodArguments.Take(TestMethodArguments.Length - 1).ToArray();\n\n                oldCompatibilityLevel = GlobalConfiguration.CompatibilityLevel;\n                GlobalConfiguration.CompatibilityLevel = compatibilityLevel;\n\n                return await base.InvokeTestAsync(aggregator);\n            }\n            finally\n            {\n                if (oldCompatibilityLevel.HasValue) GlobalConfiguration.CompatibilityLevel = oldCompatibilityLevel.Value;\n                SyncRoot.Release();\n            }\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/DataCompatibilityRangeTheoryAttribute.cs",
    "content": "﻿using System;\nusing Xunit;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    [XunitTestCaseDiscoverer(\"Hangfire.Core.Tests.DataCompatibilityRangeTheoryDiscoverer\", \"Hangfire.Core.Tests\")]\n    [AttributeUsage(AttributeTargets.Method)]\n    internal sealed class DataCompatibilityRangeTheoryAttribute : TheoryAttribute\n    {\n        public DataCompatibilityRangeTheoryAttribute()\n        {\n            MinLevel = DataCompatibilityRangeFactAttribute.PossibleMinLevel;\n            MaxExcludingLevel = DataCompatibilityRangeFactAttribute.PossibleMaxExcludingLevel;\n        }\n\n        public CompatibilityLevel MinLevel { get; set; }\n        public CompatibilityLevel MaxExcludingLevel { get; set; }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/DataCompatibilityRangeTheoryDiscoverer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n#if NETCOREAPP1_0\nusing System.Reflection;\n#endif\nusing Hangfire.Annotations;\nusing Xunit.Abstractions;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    [UsedImplicitly]\n    internal class DataCompatibilityRangeTheoryDiscoverer : IXunitTestCaseDiscoverer\n    {\n        public DataCompatibilityRangeTheoryDiscoverer(IMessageSink diagnosticMessageSink)\n        {\n            DiagnosticMessageSink = diagnosticMessageSink;\n        }\n\n        protected IMessageSink DiagnosticMessageSink { get; }\n\n        protected virtual IEnumerable<IXunitTestCase> CreateTestCasesForTheory(\n            ITestFrameworkDiscoveryOptions discoveryOptions,\n            ITestMethod testMethod,\n            IAttributeInfo theoryAttribute)\n        {\n            var compatibilityLevels = DataCompatibilityRangeFactDiscoverer.GetAllowedCompatibilityLevels(theoryAttribute);\n\n            foreach (var compatibilityLevel in compatibilityLevels)\n            {\n                yield return new DataCompatibilityRangeTheoryTestCase(\n                    compatibilityLevel,\n                    DiagnosticMessageSink,\n                    discoveryOptions.MethodDisplayOrDefault(),\n                    testMethod);\n            }\n        }\n\n        protected virtual IEnumerable<IXunitTestCase> CreateTestCasesForDataRow(\n            ITestFrameworkDiscoveryOptions discoveryOptions,\n            ITestMethod testMethod,\n            IAttributeInfo theoryAttribute,\n            object[] dataRow)\n        {\n            var compatibilityLevels = DataCompatibilityRangeFactDiscoverer.GetAllowedCompatibilityLevels(theoryAttribute);\n\n            foreach (var compatibilityLevel in compatibilityLevels)\n            {\n                yield return new DataCompatibilityRangeTestCase(\n                    DiagnosticMessageSink,\n                    discoveryOptions.MethodDisplayOrDefault(),\n                    testMethod,\n                    dataRow.Concat(new object[] { compatibilityLevel }).ToArray());\n            }\n        }\n\n        protected virtual IEnumerable<IXunitTestCase> CreateTestCasesForSkip(\n            ITestFrameworkDiscoveryOptions discoveryOptions,\n            ITestMethod testMethod,\n            IAttributeInfo theoryAttribute,\n            string skipReason)\n        {\n            return new[]\n            {\n                new XunitTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod)\n            };\n        }\n\n        protected virtual IEnumerable<IXunitTestCase> CreateTestCasesForSkippedDataRow(\n            ITestFrameworkDiscoveryOptions discoveryOptions,\n            ITestMethod testMethod,\n            IAttributeInfo theoryAttribute,\n            object[] dataRow,\n            string skipReason)\n        {\n            return new[]\n            {\n                new XunitSkippedDataRowTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod, skipReason, dataRow)\n            };\n        }\n\n        public virtual IEnumerable<IXunitTestCase> Discover(\n            ITestFrameworkDiscoveryOptions discoveryOptions,\n            ITestMethod testMethod,\n            IAttributeInfo theoryAttribute)\n        {\n            var skipArgument = theoryAttribute.GetNamedArgument<string>(\"Skip\");\n            if (skipArgument != null)\n            {\n                return CreateTestCasesForSkip(discoveryOptions, testMethod, theoryAttribute, skipArgument);\n            }\n\n            if (discoveryOptions.PreEnumerateTheoriesOrDefault())\n            {\n                try\n                {\n                    var customAttributes = testMethod.Method.GetCustomAttributes(typeof(DataAttribute));\n                    var testCases = new List<IXunitTestCase>();\n\n                    foreach (var attributeInfo in customAttributes)\n                    {\n                        var dataDiscovererAttribute = attributeInfo.GetCustomAttributes(typeof(DataDiscovererAttribute)).First();\n                        IDataDiscoverer dataDiscoverer;\n                        try\n                        {\n                            dataDiscoverer = ExtensibilityPointFactory.GetDataDiscoverer(DiagnosticMessageSink, dataDiscovererAttribute);\n                        }\n                        catch (InvalidCastException)\n                        {\n                            if (attributeInfo is IReflectionAttributeInfo reflectionAttributeInfo)\n                            {\n                                testCases.Add(new ExecutionErrorTestCase(\n                                    DiagnosticMessageSink,\n                                    discoveryOptions.MethodDisplayOrDefault(),\n                                    testMethod,\n                                    $\"Data discoverer specified for {reflectionAttributeInfo.Attribute.GetType()} on {testMethod.TestClass.Class.Name}.{testMethod.Method.Name} does not implement IDataDiscoverer.\"));\n\n                                continue;\n                            }\n\n                            testCases.Add(new ExecutionErrorTestCase(\n                                DiagnosticMessageSink,\n                                discoveryOptions.MethodDisplayOrDefault(),\n                                testMethod,\n                                $\"A data discoverer specified on {testMethod.TestClass.Class.Name}.{testMethod.Method.Name} does not implement IDataDiscoverer.\"));\n\n                            continue;\n                        }\n                        if (dataDiscoverer == null)\n                        {\n                            if (attributeInfo is IReflectionAttributeInfo reflectionAttributeInfo)\n                            {\n                                testCases.Add(new ExecutionErrorTestCase(\n                                    DiagnosticMessageSink,\n                                    discoveryOptions.MethodDisplayOrDefault(),\n                                    testMethod,\n                                    $\"Data discoverer specified for {reflectionAttributeInfo.Attribute.GetType()} on {testMethod.TestClass.Class.Name}.{testMethod.Method.Name} does not exist.\"));\n                            }\n                            else\n                            {\n                                testCases.Add(new ExecutionErrorTestCase(\n                                    DiagnosticMessageSink,\n                                    discoveryOptions.MethodDisplayOrDefault(),\n                                    testMethod,\n                                    $\"A data discoverer specified on {testMethod.TestClass.Class.Name}.{testMethod.Method.Name} does not exist.\"));\n                            }\n                        }\n                        else\n                        {\n                            if (!dataDiscoverer.SupportsDiscoveryEnumeration(attributeInfo, testMethod.Method))\n                            {\n                                testCases.Add(new ExecutionErrorTestCase(\n                                    DiagnosticMessageSink,\n                                    discoveryOptions.MethodDisplayOrDefault(),\n                                    testMethod,\n                                    $\"DataDiscoverer doesn't support discovery enumeration for {testMethod.TestClass.Class.Name}.{testMethod.Method.Name}.\"));\n                            }\n\n                            var data = dataDiscoverer.GetData(attributeInfo, testMethod.Method);\n                            if (data == null)\n                            {\n                                testCases.Add(new ExecutionErrorTestCase(\n                                    DiagnosticMessageSink,\n                                    discoveryOptions.MethodDisplayOrDefault(),\n                                    testMethod,\n                                    $\"Test data returned null for {testMethod.TestClass.Class.Name}.{testMethod.Method.Name}. Make sure it is statically initialized before this test method is called.\"));\n                            }\n                            else\n                            {\n                                var serializationHelperType = Type.GetType(\"Xunit.Sdk.SerializationHelper, xunit.execution.desktop\", throwOnError: false);\n                                if (serializationHelperType == null)\n                                {\n                                    serializationHelperType = Type.GetType(\"Xunit.Sdk.SerializationHelper, xunit.execution.dotnet\", throwOnError: false);\n\n                                    if (serializationHelperType == null)\n                                    {\n                                        DiagnosticMessageSink.OnMessage(new DiagnosticMessage(\n                                            $\"Xunit.Sdk.SerializationHelper type not found for {testMethod.TestClass.Class.Name}.{testMethod.Method.Name}; falling back to single test case.\"));\n\n                                        return CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute);\n                                    }\n                                }\n\n                                var isSerializableMethod = serializationHelperType.GetMethod(\"IsSerializable\", new [] { typeof(object) });\n                                if (isSerializableMethod == null)\n                                {\n                                    DiagnosticMessageSink.OnMessage(new DiagnosticMessage(\n                                        $\"Xunit.Sdk.SerializationHelper.IsSerializable method not found for {testMethod.TestClass.Class.Name}.{testMethod.Method.Name}; falling back to single test case.\"));\n\n                                    return CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute);\n                                }\n\n                                var skipArgument2 = attributeInfo.GetNamedArgument<string>(\"Skip\");\n\n                                foreach (var dataRow in data)\n                                {\n                                    if (!(bool)isSerializableMethod.Invoke(null, new object[] { dataRow }))\n                                    {\n                                        DiagnosticMessageSink.OnMessage(new DiagnosticMessage(\n                                            $\"Non-serializable data ('{dataRow.GetType().FullName}') found for '{testMethod.TestClass.Class.Name}.{testMethod.Method.Name}'; falling back to single test case.\"));\n\n                                        return CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute);\n                                    }\n\n                                    var collection = skipArgument2 != null\n                                        ? CreateTestCasesForSkippedDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow, skipArgument2)\n                                        : CreateTestCasesForDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow);\n\n                                    testCases.AddRange(collection);\n                                }\n                            }\n                        }\n                    }\n\n                    if (testCases.Count == 0)\n                    {\n                        testCases.Add(new ExecutionErrorTestCase(\n                            DiagnosticMessageSink,\n                            discoveryOptions.MethodDisplayOrDefault(),\n                            testMethod,\n                            $\"No data found for {testMethod.TestClass.Class.Name}.{testMethod.Method.Name}\"));\n                    }\n\n                    return testCases;\n                }\n                catch (Exception ex)\n                {\n                    DiagnosticMessageSink.OnMessage(new DiagnosticMessage(\n                        $\"Exception thrown during theory discovery on '{testMethod.TestClass.Class.Name}.{testMethod.Method.Name}'; falling back to single test case.{Environment.NewLine}{ex}\"));\n                }\n            }\n\n            return CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute);\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/DataCompatibilityRangeTheoryTestCase.cs",
    "content": "﻿using System;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Hangfire.Annotations;\nusing Xunit.Abstractions;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    internal sealed class DataCompatibilityRangeTheoryTestCase : XunitTestCase\n    {\n        [Obsolete(\"Called by the de-serializer; should only be called by deriving classes for de-serialization purposes\")]\n        [UsedImplicitly]\n        public DataCompatibilityRangeTheoryTestCase()\n        {\n        }\n\n        public DataCompatibilityRangeTheoryTestCase(\n            CompatibilityLevel compatibilityLevel,\n            IMessageSink diagnosticMessageSink,\n            TestMethodDisplay defaultMethodDisplay,\n            ITestMethod testMethod)\n            : base(diagnosticMessageSink, defaultMethodDisplay, testMethod, new object[] { compatibilityLevel })\n        {\n        }\n\n        public override Task<RunSummary> RunAsync(\n            IMessageSink diagnosticMessageSink,\n            IMessageBus messageBus,\n            object[] constructorArguments,\n            ExceptionAggregator aggregator,\n            CancellationTokenSource cancellationTokenSource)\n        {\n            return new DataCompatibilityRangeTheoryTestCaseRunner(this, DisplayName, SkipReason, constructorArguments, TestMethodArguments, diagnosticMessageSink, messageBus, aggregator, cancellationTokenSource).RunAsync();\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/DataCompatibilityRangeTheoryTestCaseRunner.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Xunit.Abstractions;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    public class DataCompatibilityRangeTheoryTestCaseRunner : XunitTestCaseRunner\n    {\n        private readonly ExceptionAggregator _cleanupAggregator = new ExceptionAggregator();\n        private Exception _dataDiscoveryException;\n        private readonly IMessageSink _diagnosticMessageSink;\n        private readonly List<XunitTestRunner> _testRunners = new List<XunitTestRunner>();\n        private readonly List<IDisposable> _toDispose = new List<IDisposable>();\n\n        public DataCompatibilityRangeTheoryTestCaseRunner(\n             IXunitTestCase testCase,\n             string displayName,\n             string skipReason,\n             object[] constructorArguments,\n             object[] testMethodArguments,\n             IMessageSink diagnosticMessageSink,\n             IMessageBus messageBus,\n             ExceptionAggregator aggregator,\n             CancellationTokenSource cancellationTokenSource)\n            : base(testCase, displayName, skipReason, constructorArguments, testMethodArguments, messageBus, aggregator, cancellationTokenSource)\n        {\n            _diagnosticMessageSink = diagnosticMessageSink;\n        }\n\n        protected override async Task AfterTestCaseStartingAsync()\n        {\n            await base.AfterTestCaseStartingAsync();\n\n            try\n            {\n                var dataAttributes = TestCase.TestMethod.Method.GetCustomAttributes(typeof(DataAttribute));\n\n                foreach (var dataAttribute in dataAttributes)\n                {\n                    var discovererAttribute = dataAttribute.GetCustomAttributes(typeof(DataDiscovererAttribute)).First();\n                    var args = discovererAttribute.GetConstructorArguments().Cast<string>().ToList();\n                    var discovererType = Type.GetType($\"{args[0]}, {args[1]}\");\n                    if (discovererType == null)\n                    {\n                        if (dataAttribute is IReflectionAttributeInfo reflectionAttribute)\n                            Aggregator.Add(new InvalidOperationException($\"Data discoverer specified for {reflectionAttribute.Attribute.GetType()} on {TestCase.TestMethod.TestClass.Class.Name}.{TestCase.TestMethod.Method.Name} does not exist.\"));\n                        else\n                            Aggregator.Add(new InvalidOperationException($\"A data discoverer specified on {TestCase.TestMethod.TestClass.Class.Name}.{TestCase.TestMethod.Method.Name} does not exist.\"));\n\n                        continue;\n                    }\n\n                    IDataDiscoverer discoverer;\n                    try\n                    {\n                        discoverer = ExtensibilityPointFactory.GetDataDiscoverer(_diagnosticMessageSink, discovererType);\n                    }\n                    catch (InvalidCastException)\n                    {\n                        Aggregator.Add(dataAttribute is IReflectionAttributeInfo reflectionAttribute\n                            ? new InvalidOperationException(\n                                $\"Data discoverer specified for {reflectionAttribute.Attribute.GetType()} on {TestCase.TestMethod.TestClass.Class.Name}.{TestCase.TestMethod.Method.Name} does not implement IDataDiscoverer.\")\n                            : new InvalidOperationException(\n                                $\"A data discoverer specified on {TestCase.TestMethod.TestClass.Class.Name}.{TestCase.TestMethod.Method.Name} does not implement IDataDiscoverer.\"));\n\n                        continue;\n                    }\n\n                    var data = discoverer.GetData(dataAttribute, TestCase.TestMethod.Method);\n                    if (data == null)\n                    {\n                        Aggregator.Add(new InvalidOperationException($\"Test data returned null for {TestCase.TestMethod.TestClass.Class.Name}.{TestCase.TestMethod.Method.Name}. Make sure it is statically initialized before this test method is called.\"));\n                        continue;\n                    }\n\n                    foreach (var dataRow in data)\n                    {\n                        _toDispose.AddRange(dataRow.OfType<IDisposable>());\n\n                        ITypeInfo[] resolvedTypes = null;\n                        var methodToRun = TestMethod;\n                        var convertedDataRow = methodToRun.ResolveMethodArguments(dataRow);\n\n                        if (methodToRun.IsGenericMethodDefinition)\n                        {\n                            resolvedTypes = TestCase.TestMethod.Method.ResolveGenericTypes(convertedDataRow);\n                            methodToRun = methodToRun.MakeGenericMethod(resolvedTypes.Select(t => ((IReflectionTypeInfo)t).Type).ToArray());\n                        }\n\n                        var parameterTypes = methodToRun.GetParameters().Select(p => p.ParameterType).ToArray();\n                        convertedDataRow = Reflector.ConvertArguments(convertedDataRow, parameterTypes);\n\n                        object compatibilityLevel;\n\n                        if (TestMethodArguments[0] is CompatibilityLevel level)\n                        {\n                            compatibilityLevel = level;\n                        }\n                        else\n                        {\n                            compatibilityLevel = Enum.Parse(typeof(CompatibilityLevel), (string)TestMethodArguments[0]);\n                        }\n\n                        var finalDataRow = convertedDataRow.Concat(new [] { compatibilityLevel }).ToArray();\n\n                        var theoryDisplayName = TestCase.TestMethod.Method.GetDisplayNameWithArguments(DisplayName, finalDataRow, resolvedTypes);\n                        var test = new XunitTest(TestCase, theoryDisplayName);\n                        var skipReason = SkipReason ?? dataAttribute.GetNamedArgument<string>(\"Skip\");\n\n                        var testRunner = new DataCompatibilityRangeTestRunner(test, MessageBus, TestClass, ConstructorArguments,\n                            methodToRun, finalDataRow, skipReason, BeforeAfterAttributes, Aggregator,\n                            CancellationTokenSource);\n\n                        _testRunners.Add(testRunner);\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                _dataDiscoveryException = ex;\n            }\n        }\n\n        protected override Task BeforeTestCaseFinishedAsync()\n        {\n            Aggregator.Aggregate(_cleanupAggregator);\n\n            return base.BeforeTestCaseFinishedAsync();\n        }\n\n        protected override async Task<RunSummary> RunTestAsync()\n        {\n            if (_dataDiscoveryException != null)\n                return RunTest_DataDiscoveryException();\n\n            var runSummary = new RunSummary();\n            foreach (var testRunner in _testRunners)\n                runSummary.Aggregate(await testRunner.RunAsync());\n\n            var timer = new ExecutionTimer();\n            foreach (var disposable in _toDispose)\n                timer.Aggregate(() => _cleanupAggregator.Run(disposable.Dispose));\n\n            runSummary.Time += timer.Total;\n            return runSummary;\n        }\n\n        private RunSummary RunTest_DataDiscoveryException()\n        {\n            var test = new XunitTest(TestCase, DisplayName);\n\n            if (!MessageBus.QueueMessage(new TestStarting(test)))\n                CancellationTokenSource.Cancel();\n            else if (!MessageBus.QueueMessage(new TestFailed(test, 0, null, Unwrap(_dataDiscoveryException))))\n                CancellationTokenSource.Cancel();\n            if (!MessageBus.QueueMessage(new TestFinished(test, 0, null)))\n                CancellationTokenSource.Cancel();\n\n            return new RunSummary { Total = 1, Failed = 1 };\n        }\n\n        private static Exception Unwrap(Exception ex)\n        {\n            while (true)\n            {\n                if (ex is TargetInvocationException invocationException)\n                    ex = invocationException.InnerException;\n                else\n                    break;\n            }\n            return ex;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/GlobalLockAttribute.cs",
    "content": "using System.Reflection;\nusing System.Threading;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    internal sealed class GlobalLockAttribute : BeforeAfterTestAttribute\n    {\n        private readonly object _globalLock = new object();\n\n        public string Reason { get; set; }\n\n        public override void Before(MethodInfo methodUnderTest)\n        {\n            Monitor.Enter(_globalLock);\n        }\n\n        public override void After(MethodInfo methodUnderTest)\n        {\n            Monitor.Exit(_globalLock);\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/PlatformHelper.cs",
    "content": "﻿using System;\n\n#if NETCOREAPP1_0\nusing System.Runtime.InteropServices;\n#endif\n\nnamespace Hangfire.Core.Tests\n{\n    internal static class PlatformHelper\n    {\n        public static bool IsRunningOnWindows()\n        {\n#if !NETCOREAPP1_0\n            return Environment.OSVersion.Platform == PlatformID.Win32NT;\n#else\n            return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);\n#endif\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/SequenceAttribute.cs",
    "content": "﻿using System;\nusing System.Reflection;\nusing Moq.Sequences;\nusing Xunit;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    public class SequenceAttribute : BeforeAfterTestAttribute\n    {\n        private IDisposable _sequence;\n\n        public override void Before(MethodInfo methodUnderTest)\n        {\n            _sequence = Sequence.Create();\n        }\n\n        public override void After(MethodInfo methodUnderTest)\n        {\n            _sequence.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/SerializerSettingsHelper.cs",
    "content": "﻿using System.Runtime.Serialization.Formatters;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.Core.Tests\n{\n    public static class SerializerSettingsHelper\n    {\n        public static JsonSerializerSettings DangerousSettings = new JsonSerializerSettings\n        {\n            TypeNameHandling = TypeNameHandling.All,\n\n#if NET452 || NET461 || NETCOREAPP3_1\n            TypeNameAssemblyFormat = FormatterAssemblyStyle.Full,\n#else\n            TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,\n#endif\n\n            DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,\n\n            Formatting = Formatting.Indented,\n\n            NullValueHandling = NullValueHandling.Ignore,\n            DefaultValueHandling = DefaultValueHandling.Ignore,\n        };\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/Utils/StaticLockAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Reflection;\nusing System.Threading;\nusing Xunit.Sdk;\n\nnamespace Hangfire.Core.Tests\n{\n    internal sealed class StaticLockAttribute : BeforeAfterTestAttribute\n    {\n        private readonly ConcurrentDictionary<Type, object> _locks\n            = new ConcurrentDictionary<Type, object>(); \n        \n        public override void Before(MethodInfo methodUnderTest)\n        {\n            var type = GetType(methodUnderTest);\n            _locks.TryAdd(type, new object());\n\n            Monitor.Enter(_locks[type]);\n        }\n\n        public override void After(MethodInfo methodUnderTest)\n        {\n            Monitor.Exit(_locks[GetType(methodUnderTest)]);\n        }\n\n        private static Type GetType(MethodInfo methodInfo)\n        {\n            return methodInfo.DeclaringType;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.Core.Tests/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \".NETFramework,Version=v4.5.2\": {\n      \"Microsoft.NET.Test.Sdk\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[17.8.0, )\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net452\": \"1.0.3\"\n        }\n      },\n      \"Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.10.0, )\",\n        \"resolved\": \"4.10.0\",\n        \"contentHash\": \"YZ1yTTDkFdgjglo9v2Gy4jnWUUIPTQETCul9CDguydLYrVgQXr6L1n3CEJqy/S9kgX+Er0cRQixky2grMwtvxA==\",\n        \"dependencies\": {\n          \"Castle.Core\": \"4.3.1\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\",\n          \"System.ValueTuple\": \"4.4.0\"\n        }\n      },\n      \"Moq.Sequences\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.1.0, )\",\n        \"resolved\": \"2.1.0\",\n        \"contentHash\": \"0mD4H/K+WtwcICaI8ytRnozVOkSGyEdQaCVcBKo3WUsAAJXfhKhpraiZZB2rMl/9jB9S6uo3x4GMbqaKTkwvgA==\",\n        \"dependencies\": {\n          \"Moq\": \"4.7.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[5.0.1, )\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"xunit\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"NL00nGsDsyWc1CWxz5FXXjLpW9oFG18WJoTPCyhNv4KGP/e5iLJqAqgM1uaJZyQ6WaTtmWIy4yjYP3RdcaT7Vw==\",\n        \"dependencies\": {\n          \"xunit.analyzers\": \"0.10.0\",\n          \"xunit.assert\": \"[2.4.0]\",\n          \"xunit.core\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.runner.visualstudio\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"3eq5cGXbEJkqW9nwLuXwtxy9B5gMA8i7HW4rN63AhAvy5UvEcQbZnve23wx/oPrkyg/4CbfNhxkBezS0b1oUdQ==\"\n      },\n      \"Castle.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"8Y/eTr6GTElAGV7eAmJuhfLhGdFpNvaNrQ9UQYDScziLmX+/BLGM+9eQr0IcdNDcPN0ADmbtwT6MgecGKy4obw==\"\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net452\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"kuFOgilYbs29xENHlqQ6aJYa+t56u+OqHx85P7GYLVlo7HL3nsug9IQY2DoPgkOpZ2xb9btYV2EFK7Enll8S3A==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\"\n      },\n      \"System.ValueTuple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==\"\n      },\n      \"xunit.abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.2\",\n        \"contentHash\": \"vItLB0WkaKg0426RgWq+ZdXH6D+YV/uH28C0weWMOBnVx7I+luHuEYss9hoOngpkiN5kUpLvh9VZRx1H2sk59A==\"\n      },\n      \"xunit.analyzers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.10.0\",\n        \"contentHash\": \"4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==\"\n      },\n      \"xunit.assert\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"Swvkm6iTjZr8TiUj5vMnmfG+2dD4s/BIBgsVOzTxxmoq2ndGsmM2WIL4wuqJ8RhxydWIDOPpIaaytjT2pMTEdg==\"\n      },\n      \"xunit.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"BJ/O/tPEcHUCwQYuwqXoYccTMyw6B5dA6yh7WxWWBhKbjqTsG9RWL0nCQXM5yQYJwUuFzBkiXDPN1BO6UdBB4Q==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\",\n          \"xunit.extensibility.execution\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.extensibility.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"qr/KrR6uukHXD9e/lLQjyCPfMEDuvvhNFDzsYzCF2kKlYKiqcADfUvA9Q68rBtKFtwHFeghjWEuv15KoGD2SfA==\",\n        \"dependencies\": {\n          \"xunit.abstractions\": \"2.0.2\"\n        }\n      },\n      \"xunit.extensibility.execution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"252Dzn7i5bMPKtAL15aOP3qJhxKd+57I8ldwIQRJa745JxQuiBu5Da0vtIISVTtc3buRSkBwVnD9iUzsEmCzZA==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      }\n    },\n    \".NETFramework,Version=v4.6.1\": {\n      \"Microsoft.NET.Test.Sdk\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[17.8.0, )\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net461\": \"1.0.3\"\n        }\n      },\n      \"Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.10.0, )\",\n        \"resolved\": \"4.10.0\",\n        \"contentHash\": \"YZ1yTTDkFdgjglo9v2Gy4jnWUUIPTQETCul9CDguydLYrVgQXr6L1n3CEJqy/S9kgX+Er0cRQixky2grMwtvxA==\",\n        \"dependencies\": {\n          \"Castle.Core\": \"4.3.1\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\",\n          \"System.ValueTuple\": \"4.4.0\"\n        }\n      },\n      \"Moq.Sequences\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.1.0, )\",\n        \"resolved\": \"2.1.0\",\n        \"contentHash\": \"0mD4H/K+WtwcICaI8ytRnozVOkSGyEdQaCVcBKo3WUsAAJXfhKhpraiZZB2rMl/9jB9S6uo3x4GMbqaKTkwvgA==\",\n        \"dependencies\": {\n          \"Moq\": \"4.7.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[5.0.1, )\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"xunit\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"NL00nGsDsyWc1CWxz5FXXjLpW9oFG18WJoTPCyhNv4KGP/e5iLJqAqgM1uaJZyQ6WaTtmWIy4yjYP3RdcaT7Vw==\",\n        \"dependencies\": {\n          \"xunit.analyzers\": \"0.10.0\",\n          \"xunit.assert\": \"[2.4.0]\",\n          \"xunit.core\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.runner.visualstudio\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"3eq5cGXbEJkqW9nwLuXwtxy9B5gMA8i7HW4rN63AhAvy5UvEcQbZnve23wx/oPrkyg/4CbfNhxkBezS0b1oUdQ==\"\n      },\n      \"Castle.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"8Y/eTr6GTElAGV7eAmJuhfLhGdFpNvaNrQ9UQYDScziLmX+/BLGM+9eQr0IcdNDcPN0ADmbtwT6MgecGKy4obw==\"\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net461\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\"\n      },\n      \"System.ValueTuple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==\"\n      },\n      \"xunit.abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.2\",\n        \"contentHash\": \"vItLB0WkaKg0426RgWq+ZdXH6D+YV/uH28C0weWMOBnVx7I+luHuEYss9hoOngpkiN5kUpLvh9VZRx1H2sk59A==\"\n      },\n      \"xunit.analyzers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.10.0\",\n        \"contentHash\": \"4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==\"\n      },\n      \"xunit.assert\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"Swvkm6iTjZr8TiUj5vMnmfG+2dD4s/BIBgsVOzTxxmoq2ndGsmM2WIL4wuqJ8RhxydWIDOPpIaaytjT2pMTEdg==\"\n      },\n      \"xunit.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"BJ/O/tPEcHUCwQYuwqXoYccTMyw6B5dA6yh7WxWWBhKbjqTsG9RWL0nCQXM5yQYJwUuFzBkiXDPN1BO6UdBB4Q==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\",\n          \"xunit.extensibility.execution\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.extensibility.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"qr/KrR6uukHXD9e/lLQjyCPfMEDuvvhNFDzsYzCF2kKlYKiqcADfUvA9Q68rBtKFtwHFeghjWEuv15KoGD2SfA==\",\n        \"dependencies\": {\n          \"xunit.abstractions\": \"2.0.2\"\n        }\n      },\n      \"xunit.extensibility.execution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"252Dzn7i5bMPKtAL15aOP3qJhxKd+57I8ldwIQRJa745JxQuiBu5Da0vtIISVTtc3buRSkBwVnD9iUzsEmCzZA==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      }\n    },\n    \"net6.0\": {\n      \"Microsoft.NET.Test.Sdk\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[17.8.0, )\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==\",\n        \"dependencies\": {\n          \"Microsoft.CodeCoverage\": \"17.8.0\",\n          \"Microsoft.TestPlatform.TestHost\": \"17.8.0\"\n        }\n      },\n      \"Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.10.0, )\",\n        \"resolved\": \"4.10.0\",\n        \"contentHash\": \"YZ1yTTDkFdgjglo9v2Gy4jnWUUIPTQETCul9CDguydLYrVgQXr6L1n3CEJqy/S9kgX+Er0cRQixky2grMwtvxA==\",\n        \"dependencies\": {\n          \"Castle.Core\": \"4.3.1\",\n          \"NETStandard.Library\": \"1.6.1\",\n          \"System.Linq.Queryable\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\",\n          \"System.ValueTuple\": \"4.4.0\"\n        }\n      },\n      \"Moq.Sequences\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.1.0, )\",\n        \"resolved\": \"2.1.0\",\n        \"contentHash\": \"0mD4H/K+WtwcICaI8ytRnozVOkSGyEdQaCVcBKo3WUsAAJXfhKhpraiZZB2rMl/9jB9S6uo3x4GMbqaKTkwvgA==\",\n        \"dependencies\": {\n          \"Moq\": \"4.7.0\",\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[13.0.3, )\",\n        \"resolved\": \"13.0.3\",\n        \"contentHash\": \"HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==\"\n      },\n      \"xunit\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"NL00nGsDsyWc1CWxz5FXXjLpW9oFG18WJoTPCyhNv4KGP/e5iLJqAqgM1uaJZyQ6WaTtmWIy4yjYP3RdcaT7Vw==\",\n        \"dependencies\": {\n          \"xunit.analyzers\": \"0.10.0\",\n          \"xunit.assert\": \"[2.4.0]\",\n          \"xunit.core\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.runner.visualstudio\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"3eq5cGXbEJkqW9nwLuXwtxy9B5gMA8i7HW4rN63AhAvy5UvEcQbZnve23wx/oPrkyg/4CbfNhxkBezS0b1oUdQ==\",\n        \"dependencies\": {\n          \"Microsoft.NET.Test.Sdk\": \"15.0.0\"\n        }\n      },\n      \"Castle.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"8Y/eTr6GTElAGV7eAmJuhfLhGdFpNvaNrQ9UQYDScziLmX+/BLGM+9eQr0IcdNDcPN0ADmbtwT6MgecGKy4obw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\",\n          \"System.Collections.Specialized\": \"4.3.0\",\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.ComponentModel.TypeConverter\": \"4.3.0\",\n          \"System.Diagnostics.TraceSource\": \"4.3.0\",\n          \"System.Dynamic.Runtime\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Xml.XmlDocument\": \"4.3.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.CodeCoverage\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"vvVR/B08YVghQ4jHEloxqw2ZWzEGE1AOA5E0DioUM3ujbXz6FD3AfB/0Jl2ohJPd0nXYGwmPe1En6HTsSriq1A==\"\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==\"\n      },\n      \"Microsoft.TestPlatform.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==\",\n        \"dependencies\": {\n          \"NuGet.Frameworks\": \"6.5.0\",\n          \"System.Reflection.Metadata\": \"1.6.0\"\n        }\n      },\n      \"Microsoft.TestPlatform.TestHost\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==\",\n        \"dependencies\": {\n          \"Microsoft.TestPlatform.ObjectModel\": \"17.8.0\",\n          \"Newtonsoft.Json\": \"13.0.1\"\n        }\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"NuGet.Frameworks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.5.0\",\n        \"contentHash\": \"QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.NonGeneric\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Specialized\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==\",\n        \"dependencies\": {\n          \"System.Collections.NonGeneric\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==\",\n        \"dependencies\": {\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel.TypeConverter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.NonGeneric\": \"4.3.0\",\n          \"System.Collections.Specialized\": \"4.3.0\",\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.ComponentModel.Primitives\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.TraceSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VnYp1NxGx8Ww731y2LJ1vpfb/DKVNKEZ8Jsh5SgQTZREL/YpWRArgh9pI8CDLmgHspZmLL697CaLvH85qQpRiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Dynamic.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Queryable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"In1Bmmvl/j52yPu3xgakQSI0YIckPUr870w4K5+Lak3JCCa8hl+my65lABOuKfYs4ugmZy25ScFerC4nz8+b6g==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Metadata\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.0\",\n        \"contentHash\": \"COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==\"\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ValueTuple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==\"\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XmlDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"xunit.abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.2\",\n        \"contentHash\": \"vItLB0WkaKg0426RgWq+ZdXH6D+YV/uH28C0weWMOBnVx7I+luHuEYss9hoOngpkiN5kUpLvh9VZRx1H2sk59A==\"\n      },\n      \"xunit.analyzers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.10.0\",\n        \"contentHash\": \"4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==\"\n      },\n      \"xunit.assert\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"Swvkm6iTjZr8TiUj5vMnmfG+2dD4s/BIBgsVOzTxxmoq2ndGsmM2WIL4wuqJ8RhxydWIDOPpIaaytjT2pMTEdg==\"\n      },\n      \"xunit.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"BJ/O/tPEcHUCwQYuwqXoYccTMyw6B5dA6yh7WxWWBhKbjqTsG9RWL0nCQXM5yQYJwUuFzBkiXDPN1BO6UdBB4Q==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\",\n          \"xunit.extensibility.execution\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.extensibility.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"qr/KrR6uukHXD9e/lLQjyCPfMEDuvvhNFDzsYzCF2kKlYKiqcADfUvA9Q68rBtKFtwHFeghjWEuv15KoGD2SfA==\",\n        \"dependencies\": {\n          \"xunit.abstractions\": \"2.0.2\"\n        }\n      },\n      \"xunit.extensibility.execution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"252Dzn7i5bMPKtAL15aOP3qJhxKd+57I8ldwIQRJa745JxQuiBu5Da0vtIISVTtc3buRSkBwVnD9iUzsEmCzZA==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.CSharp\": \"[4.4.0, )\",\n          \"Newtonsoft.Json\": \"[11.0.1, )\"\n        }\n      }\n    },\n    \"net8.0\": {\n      \"Microsoft.NET.Test.Sdk\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[17.8.0, )\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==\",\n        \"dependencies\": {\n          \"Microsoft.CodeCoverage\": \"17.8.0\",\n          \"Microsoft.TestPlatform.TestHost\": \"17.8.0\"\n        }\n      },\n      \"Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.10.0, )\",\n        \"resolved\": \"4.10.0\",\n        \"contentHash\": \"YZ1yTTDkFdgjglo9v2Gy4jnWUUIPTQETCul9CDguydLYrVgQXr6L1n3CEJqy/S9kgX+Er0cRQixky2grMwtvxA==\",\n        \"dependencies\": {\n          \"Castle.Core\": \"4.3.1\",\n          \"NETStandard.Library\": \"1.6.1\",\n          \"System.Linq.Queryable\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\",\n          \"System.ValueTuple\": \"4.4.0\"\n        }\n      },\n      \"Moq.Sequences\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.1.0, )\",\n        \"resolved\": \"2.1.0\",\n        \"contentHash\": \"0mD4H/K+WtwcICaI8ytRnozVOkSGyEdQaCVcBKo3WUsAAJXfhKhpraiZZB2rMl/9jB9S6uo3x4GMbqaKTkwvgA==\",\n        \"dependencies\": {\n          \"Moq\": \"4.7.0\",\n          \"NETStandard.Library\": \"1.6.1\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[13.0.3, )\",\n        \"resolved\": \"13.0.3\",\n        \"contentHash\": \"HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==\"\n      },\n      \"xunit\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"NL00nGsDsyWc1CWxz5FXXjLpW9oFG18WJoTPCyhNv4KGP/e5iLJqAqgM1uaJZyQ6WaTtmWIy4yjYP3RdcaT7Vw==\",\n        \"dependencies\": {\n          \"xunit.analyzers\": \"0.10.0\",\n          \"xunit.assert\": \"[2.4.0]\",\n          \"xunit.core\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.runner.visualstudio\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"3eq5cGXbEJkqW9nwLuXwtxy9B5gMA8i7HW4rN63AhAvy5UvEcQbZnve23wx/oPrkyg/4CbfNhxkBezS0b1oUdQ==\",\n        \"dependencies\": {\n          \"Microsoft.NET.Test.Sdk\": \"15.0.0\"\n        }\n      },\n      \"Castle.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"8Y/eTr6GTElAGV7eAmJuhfLhGdFpNvaNrQ9UQYDScziLmX+/BLGM+9eQr0IcdNDcPN0ADmbtwT6MgecGKy4obw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\",\n          \"System.Collections.Specialized\": \"4.3.0\",\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.ComponentModel.TypeConverter\": \"4.3.0\",\n          \"System.Diagnostics.TraceSource\": \"4.3.0\",\n          \"System.Dynamic.Runtime\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Xml.XmlDocument\": \"4.3.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.CodeCoverage\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"vvVR/B08YVghQ4jHEloxqw2ZWzEGE1AOA5E0DioUM3ujbXz6FD3AfB/0Jl2ohJPd0nXYGwmPe1En6HTsSriq1A==\"\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==\"\n      },\n      \"Microsoft.TestPlatform.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==\",\n        \"dependencies\": {\n          \"NuGet.Frameworks\": \"6.5.0\",\n          \"System.Reflection.Metadata\": \"1.6.0\"\n        }\n      },\n      \"Microsoft.TestPlatform.TestHost\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==\",\n        \"dependencies\": {\n          \"Microsoft.TestPlatform.ObjectModel\": \"17.8.0\",\n          \"Newtonsoft.Json\": \"13.0.1\"\n        }\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"NuGet.Frameworks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.5.0\",\n        \"contentHash\": \"QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.NonGeneric\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Specialized\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==\",\n        \"dependencies\": {\n          \"System.Collections.NonGeneric\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==\",\n        \"dependencies\": {\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel.TypeConverter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.NonGeneric\": \"4.3.0\",\n          \"System.Collections.Specialized\": \"4.3.0\",\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.ComponentModel.Primitives\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.TraceSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VnYp1NxGx8Ww731y2LJ1vpfb/DKVNKEZ8Jsh5SgQTZREL/YpWRArgh9pI8CDLmgHspZmLL697CaLvH85qQpRiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Dynamic.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Queryable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"In1Bmmvl/j52yPu3xgakQSI0YIckPUr870w4K5+Lak3JCCa8hl+my65lABOuKfYs4ugmZy25ScFerC4nz8+b6g==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Metadata\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.0\",\n        \"contentHash\": \"COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==\"\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ValueTuple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==\"\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XmlDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"xunit.abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.2\",\n        \"contentHash\": \"vItLB0WkaKg0426RgWq+ZdXH6D+YV/uH28C0weWMOBnVx7I+luHuEYss9hoOngpkiN5kUpLvh9VZRx1H2sk59A==\"\n      },\n      \"xunit.analyzers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.10.0\",\n        \"contentHash\": \"4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==\"\n      },\n      \"xunit.assert\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"Swvkm6iTjZr8TiUj5vMnmfG+2dD4s/BIBgsVOzTxxmoq2ndGsmM2WIL4wuqJ8RhxydWIDOPpIaaytjT2pMTEdg==\"\n      },\n      \"xunit.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"BJ/O/tPEcHUCwQYuwqXoYccTMyw6B5dA6yh7WxWWBhKbjqTsG9RWL0nCQXM5yQYJwUuFzBkiXDPN1BO6UdBB4Q==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\",\n          \"xunit.extensibility.execution\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.extensibility.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"qr/KrR6uukHXD9e/lLQjyCPfMEDuvvhNFDzsYzCF2kKlYKiqcADfUvA9Q68rBtKFtwHFeghjWEuv15KoGD2SfA==\",\n        \"dependencies\": {\n          \"xunit.abstractions\": \"2.0.2\"\n        }\n      },\n      \"xunit.extensibility.execution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"252Dzn7i5bMPKtAL15aOP3qJhxKd+57I8ldwIQRJa745JxQuiBu5Da0vtIISVTtc3buRSkBwVnD9iUzsEmCzZA==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.CSharp\": \"[4.4.0, )\",\n          \"Newtonsoft.Json\": \"[11.0.1, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "tests/Hangfire.SqlServer.Msmq.Tests/Hangfire.SqlServer.Msmq.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n  <PropertyGroup>\r\n    <TargetFramework>net452</TargetFramework>\r\n    <NoWarn>0618</NoWarn>\r\n  </PropertyGroup>\r\n  \r\n  <ItemGroup>\r\n    <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies\" Version=\"1.0.3\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"9.0.1\" NoWarn=\"NU1903\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.Core\\Hangfire.Core.csproj\" />\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.SqlServer\\Hangfire.SqlServer.csproj\" />\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.SqlServer.Msmq\\Hangfire.SqlServer.Msmq.csproj\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <Reference Include=\"System.Messaging\" />\r\n    <Reference Include=\"System.Transactions\" />\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "tests/Hangfire.SqlServer.Msmq.Tests/MessageQueueExtensionsFacts.cs",
    "content": "﻿using MQTools;\nusing Xunit;\n\nnamespace Hangfire.SqlServer.Msmq.Tests\n{\n    public class MessageQueueExtensionsFacts\n    {\n        [Theory]\n        [InlineData(@\"ComputerName\\Private$\\QueueName\", \"ComputerName\", \"Private$\", \"QueueName\")]\n        [InlineData(@\"ComputerName\\QueueName\", \"ComputerName\", \"\", \"QueueName\")]\n        [InlineData(@\".\\Private$\\QueueName\", \".\", \"Private$\", \"QueueName\")]\n        [InlineData(@\".\\QueueName\", \".\", \"\", \"QueueName\")]\n        [InlineData(@\"FormatName:Direct=OS:ComputerName\\QueueName\", \"ComputerName\", \"\", \"QueueName\")]\n        [InlineData(@\"FormatName:Direct=OS:ComputerName\\Private$\\QueueName\", \"ComputerName\", \"Private$\", \"QueueName\")]\n        public void QueueRegex_CorrectlyParsesPublicAndPrivateQueuePaths(\n            string queuePath, \n            string expectedComputerName,\n            string expectedQueueType,\n            string expectedQueueName)\n        {\n            var match = MessageQueueExtensions.GetQueuePathMatch(queuePath);\n\n            Assert.NotNull(match);\n            Assert.Equal(expectedComputerName, match.Groups[\"computerName\"].Value);\n            Assert.Equal(expectedQueueType, match.Groups[\"queueType\"].Value);\n            Assert.Equal(expectedQueueName, match.Groups[\"queue\"].Value);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Msmq.Tests/MsmqJobQueueFacts.cs",
    "content": "﻿using System;\nusing System.Data;\nusing System.Messaging;\nusing System.Threading;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable PossibleNullReferenceException\n\nnamespace Hangfire.SqlServer.Msmq.Tests\n{\n    public class MsmqJobQueueFacts\n    {\n        private readonly CancellationToken _token;\n        private readonly Mock<IDbConnection> _connection;\n\n        public MsmqJobQueueFacts()\n        {\n            _token = new CancellationToken();\n            _connection = new Mock<IDbConnection>();\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenPathPatternIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new MsmqJobQueue(null, MsmqTransactionType.Internal));\n\n            Assert.Equal(\"pathPattern\", exception.ParamName);\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\")]\n        public void Enqueue_SendsTheJobId()\n        {\n            // Arrange\n            var queue = CreateQueue(MsmqTransactionType.Internal);\n\n            // Act\n            queue.Enqueue(_connection.Object, \"my-queue\", \"job-id\");\n\n            // Assert\n            using (var messageQueue = CleanMsmqQueueAttribute.GetMessageQueue(\"my-queue\"))\n            using (var transaction = new MessageQueueTransaction())\n            {\n                transaction.Begin();\n\n                var message = messageQueue.Receive(TimeSpan.FromSeconds(5), transaction);\n\n                Assert.Equal(\"job-id\", message.Label);\n\n                transaction.Commit();\n            }\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\")]\n        public void Enqueue_AddsAJob_WhenIdIsLongValue()\n        {\n            // Arrange\n            var queue = CreateQueue(MsmqTransactionType.Internal);\n\n            // Act\n            queue.Enqueue(_connection.Object, \"my-queue\", (int.MaxValue + 1L).ToString());\n\n            // Assert\n            using (var messageQueue = CleanMsmqQueueAttribute.GetMessageQueue(\"my-queue\"))\n            using (var transaction = new MessageQueueTransaction())\n            {\n                transaction.Begin();\n\n                var message = messageQueue.Receive(TimeSpan.FromSeconds(5), transaction);\n\n                Assert.Equal((int.MaxValue + 1L).ToString(), message.Label);\n\n                transaction.Commit();\n            }\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\")]\n        public void Dequeue_ReturnsFetchedJob_WithJobId()\n        {\n            MsmqUtils.EnqueueJobId(\"my-queue\", \"job-id\");\n            var queue = CreateQueue(MsmqTransactionType.Internal);\n\n            var fetchedJob = queue.Dequeue(new[] { \"my-queue\" }, _token);\n\n            Assert.Equal(\"job-id\", fetchedJob.JobId);\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\")]\n        public void Dequeue_ThrowsCanceledException_WhenTokenHasBeenCancelled()\n        {\n            var queue = CreateQueue(MsmqTransactionType.Internal);\n            var token = new CancellationToken(true);\n\n            Assert.Throws<OperationCanceledException>(\n                () => queue.Dequeue(new[] { \"my-queue\" }, token));\n        }\n\n        [Fact, CleanMsmqQueue(\"queue-1\", \"queue-2\")]\n        public void Dequeue_ReturnsFetchedJob_FromOtherQueues_IfFirstAreEmpty()\n        {\n            MsmqUtils.EnqueueJobId(\"queue-2\", \"job-id\");\n            var queue = CreateQueue(MsmqTransactionType.Internal);\n\n            var fetchedJob = queue.Dequeue(new[] { \"queue-1\", \"queue-2\" }, _token);\n\n            Assert.Equal(\"job-id\", fetchedJob.JobId);\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\")]\n        public void Dequeue_MakesJobInvisibleForOtherFetchers()\n        {\n            // Arrange\n            MsmqUtils.EnqueueJobId(\"my-queue\", \"job-id\");\n            var queue = CreateQueue(MsmqTransactionType.Internal);\n\n            // Act\n            var fetchedJob = queue.Dequeue(new[] { \"my-queue\" }, _token);\n\n            // Assert\n            Assert.NotNull(fetchedJob);\n\n            var exception = Assert.Throws<MessageQueueException>(\n                () => MsmqUtils.DequeueJobId(\"my-queue\", TimeSpan.FromSeconds(1)));\n\n            Assert.Equal(MessageQueueErrorCode.IOTimeout, exception.MessageQueueErrorCode);\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\")]\n        public void RemoveFromQueue_OnFetchedJob_RemovesTheJobCompletely()\n        {\n            // Arrange\n            MsmqUtils.EnqueueJobId(\"my-queue\", \"job-id\");\n            var queue = CreateQueue(MsmqTransactionType.Internal);\n\n            // Act\n            using (var fetchedJob = queue.Dequeue(new[] { \"my-queue\" }, _token))\n            {\n                fetchedJob.RemoveFromQueue();\n            }\n\n            // Assert\n            var exception = Assert.Throws<MessageQueueException>(\n                () => MsmqUtils.DequeueJobId(\"my-queue\", TimeSpan.FromSeconds(5)));\n\n            Assert.Equal(MessageQueueErrorCode.IOTimeout, exception.MessageQueueErrorCode);\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\")]\n        public void DisposeWithoutRemoval_OnFetchedJob_ReturnsTheJobToTheQueue()\n        {\n            // Arrange\n            MsmqUtils.EnqueueJobId(\"my-queue\", \"job-id\");\n            var queue = CreateQueue(MsmqTransactionType.Internal);\n\n            // Act\n            var fetchedJob = queue.Dequeue(new[] { \"my-queue\" }, _token);\n            fetchedJob.Dispose();\n\n            // Assert\n            var jobId = MsmqUtils.DequeueJobId(\"my-queue\", TimeSpan.FromSeconds(5));\n            Assert.Equal(\"job-id\", jobId);\n        }\n\n        private static MsmqJobQueue CreateQueue(MsmqTransactionType transactionType)\n        {\n            return new MsmqJobQueue(CleanMsmqQueueAttribute.PathPattern, transactionType);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Msmq.Tests/MsmqJobQueueMonitoringApiFacts.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Xunit;\n\nnamespace Hangfire.SqlServer.Msmq.Tests\n{\n    public class MsmqJobQueueMonitoringApiFacts\n    {\n        private static readonly string PathPattern = CleanMsmqQueueAttribute.PathPattern;\n        private static readonly string[] Queues = { \"default\", \"critical\" };\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenPathPatternIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new MsmqJobQueueMonitoringApi(null, Queues));\n\n            Assert.Equal(\"pathPattern\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenQueuesCollectionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new MsmqJobQueueMonitoringApi(PathPattern, null));\n\n            Assert.Equal(\"queues\", exception.ParamName);\n        }\n\n        [Fact]\n        public void GetQueues_ReturnsAllGivenQueues()\n        {\n            var api = CreateMonitoringApi();\n\n            var queues = api.GetQueues();\n\n            Assert.Equal(Queues, queues);\n        }\n\n        [Fact]\n        public void GetFetchedJobIds_ReturnsEmptyCollection()\n        {\n            var api = CreateMonitoringApi();\n\n            var fetchedJobIds = api.GetFetchedJobIds(\"\", 1, 14);\n\n            Assert.Empty(fetchedJobIds);\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\", \"another-queue\")]\n        public void GetEnqueuedAndFetchedCount_ReturnsCorrectCounters()\n        {\n            MsmqUtils.EnqueueJobId(\"my-queue\", \"1\");\n            MsmqUtils.EnqueueJobId(\"my-queue\", \"2\");\n            MsmqUtils.EnqueueJobId(\"another-queue\", \"3\");\n\n            var api = CreateMonitoringApi();\n\n            var result = api.GetEnqueuedAndFetchedCount(\"my-queue\");\n\n            Assert.Equal(2, result.EnqueuedCount);\n            Assert.Null(result.FetchedCount);\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\")]\n        public void GetEnqueuedJobIds_ReturnsEmptyCollection_IfQueueIsEmpty()\n        {\n            var api = CreateMonitoringApi();\n\n            var result = api.GetEnqueuedJobIds(\"my-queue\", 5, 15);\n\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\")]\n        public void GetEnqueuedJobIds_ReturnsCorrectResult()\n        {\n            for (var i = 1; i <= 10; i++) { MsmqUtils.EnqueueJobId(\"my-queue\", i.ToString()); }\n            var api = CreateMonitoringApi();\n\n            var result = api.GetEnqueuedJobIds(\"my-queue\", 3, 2).ToArray();\n\n            Assert.Equal(2, result.Length);\n            Assert.Equal(4, result[0]);\n            Assert.Equal(5, result[1]);\n        }\n\n        [Fact, CleanMsmqQueue(\"my-queue\")]\n        public void GetEnqueuedJobIds_ReturnsCorrectResult_WhenJobIdIsLongValue()\n        {\n            MsmqUtils.EnqueueJobId(\"my-queue\", (int.MaxValue + 1L).ToString());\n\n            var api = CreateMonitoringApi();\n\n            var result = api.GetEnqueuedJobIds(\"my-queue\", 0, 1).ToArray();\n\n            Assert.Single(result);\n            Assert.Equal(int.MaxValue + 1L, result[0]);\n        }\n\n        private static MsmqJobQueueMonitoringApi CreateMonitoringApi()\n        {\n            return new MsmqJobQueueMonitoringApi(PathPattern, Queues);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Msmq.Tests/MsmqJobQueueProviderFacts.cs",
    "content": "﻿using Xunit;\n\nnamespace Hangfire.SqlServer.Msmq.Tests\n{\n    public class MsmqJobQueueProviderFacts\n    {\n        private static readonly string[] Queues = { \"default\" };\n            \n        [Fact]\n        public void GetJobQueue_ReturnsNonNullInstance()\n        {\n            var provider = CreateProvider();\n\n            var jobQueue = provider.GetJobQueue();\n\n            Assert.NotNull(jobQueue);\n        }\n\n        [Fact]\n        public void GetMonitoringApi_ReturnsNonNullInstance()\n        {\n            var provider = CreateProvider();\n\n            var monitoring = provider.GetJobQueueMonitoringApi();\n\n            Assert.NotNull(monitoring);\n        }\n\n        private static MsmqJobQueueProvider CreateProvider()\n        {\n            return new MsmqJobQueueProvider(\n                CleanMsmqQueueAttribute.PathPattern,\n                Queues,\n                MsmqTransactionType.Internal);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Msmq.Tests/MsmqSqlServerStorageExtensionsFacts.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.SqlServer.Msmq.Tests\n{\n    public class MsmqSqlServerStorageExtensionsFacts\n    {\n        private readonly SqlServerStorage _storage;\n\n        public MsmqSqlServerStorageExtensionsFacts()\n        {\n            _storage = new SqlServerStorage(\n                @\"Server=.\\sqlexpress;Database=TheDatabase;Trusted_Connection=True;\",\n                new SqlServerStorageOptions { PrepareSchemaIfNecessary = false });\n        }\n\n        [Fact]\n        public void UseMsmqQueues_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => MsmqSqlServerStorageExtensions.UseMsmqQueues(null, CleanMsmqQueueAttribute.PathPattern));\n            \n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact]\n        public void UseMsmqQueues_AddsMsmqJobQueueProvider()\n        {\n            _storage.UseMsmqQueues(CleanMsmqQueueAttribute.PathPattern);\n\n            var providerTypes = _storage.QueueProviders.Select(x => x.GetType());\n            Assert.Contains(typeof(MsmqJobQueueProvider), providerTypes);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Msmq.Tests/Utils/CleanMsmqQueueAttribute.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Messaging;\nusing System.Reflection;\nusing System.Threading;\nusing Xunit.Sdk;\n\nnamespace Hangfire.SqlServer.Msmq.Tests\n{\n    public class CleanMsmqQueueAttribute : BeforeAfterTestAttribute\n    {\n        private static readonly object GlobalLock = new object();\n\n        public static readonly string PathPattern = @\".\\Private$\\hangfire-{0}\";\n\n        private readonly string[] _queues;\n\n        public CleanMsmqQueueAttribute(params string[] queues)\n        {\n            _queues = queues;\n        }\n\n        public override void Before(MethodInfo methodUnderTest)\n        {\n            Monitor.Enter(GlobalLock);\n\n            foreach (var queuePath in _queues.Select(GetPath))\n            {\n                if (MessageQueue.Exists(queuePath))\n                {\n                    MessageQueue.Delete(queuePath);\n                }\n\n                using (MessageQueue.Create(queuePath, transactional: true))\n                {\n                    // We just need to create it.\n                }\n            }\n        }\n\n        public override void After(MethodInfo methodUnderTest)\n        {\n            Monitor.Exit(GlobalLock);\n        }\n\n        public static MessageQueue GetMessageQueue(string queue)\n        {\n            return new MessageQueue(GetPath(queue));\n        }\n\n        private static string GetPath(string queue)\n        {\n            return String.Format(PathPattern, queue);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Msmq.Tests/Utils/MsmqUtils.cs",
    "content": "﻿using System;\nusing System.Messaging;\n\n// ReSharper disable PossibleNullReferenceException\n\nnamespace Hangfire.SqlServer.Msmq.Tests\n{\n    internal sealed class MsmqUtils\n    {\n        public static void EnqueueJobId(string queue, string jobId)\n        {\n            using (var messageQueue = CleanMsmqQueueAttribute.GetMessageQueue(queue))\n            using (var message = new Message { Body = jobId, Label = jobId, Formatter = new BinaryMessageFormatter() })\n            using (var transaction = new MessageQueueTransaction())\n            {\n                transaction.Begin();\n                messageQueue.Send(message, transaction);\n                transaction.Commit();\n            }\n        }\n\n        public static string DequeueJobId(string queue, TimeSpan timeout)\n        {\n            using (var messageQueue = CleanMsmqQueueAttribute.GetMessageQueue(queue))\n            using (var transaction = new MessageQueueTransaction())\n            {\n                transaction.Begin();\n\n                using (var message = messageQueue.Receive(timeout, transaction))\n                {\n                    message.Formatter = new BinaryMessageFormatter();\n                    transaction.Commit();\n\n                    return (string)message.Body;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Msmq.Tests/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \".NETFramework,Version=v4.5.2\": {\n      \"Microsoft.NET.Test.Sdk\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[17.8.0, )\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net452\": \"1.0.3\"\n        }\n      },\n      \"Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.10.0, )\",\n        \"resolved\": \"4.10.0\",\n        \"contentHash\": \"YZ1yTTDkFdgjglo9v2Gy4jnWUUIPTQETCul9CDguydLYrVgQXr6L1n3CEJqy/S9kgX+Er0cRQixky2grMwtvxA==\",\n        \"dependencies\": {\n          \"Castle.Core\": \"4.3.1\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\",\n          \"System.ValueTuple\": \"4.4.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[9.0.1, )\",\n        \"resolved\": \"9.0.1\",\n        \"contentHash\": \"U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==\"\n      },\n      \"xunit\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"NL00nGsDsyWc1CWxz5FXXjLpW9oFG18WJoTPCyhNv4KGP/e5iLJqAqgM1uaJZyQ6WaTtmWIy4yjYP3RdcaT7Vw==\",\n        \"dependencies\": {\n          \"xunit.analyzers\": \"0.10.0\",\n          \"xunit.assert\": \"[2.4.0]\",\n          \"xunit.core\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.runner.visualstudio\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"3eq5cGXbEJkqW9nwLuXwtxy9B5gMA8i7HW4rN63AhAvy5UvEcQbZnve23wx/oPrkyg/4CbfNhxkBezS0b1oUdQ==\"\n      },\n      \"Castle.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"8Y/eTr6GTElAGV7eAmJuhfLhGdFpNvaNrQ9UQYDScziLmX+/BLGM+9eQr0IcdNDcPN0ADmbtwT6MgecGKy4obw==\"\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Dapper\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.60.6\",\n        \"contentHash\": \"mmnJNhKMeF2KhvVXDoVQlFxre8aJAo71YBJrKqFlvuqzYC2QiXUq94/GCDBJzU7paq4GqpkV2glw3308TcGibw==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net452\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"kuFOgilYbs29xENHlqQ6aJYa+t56u+OqHx85P7GYLVlo7HL3nsug9IQY2DoPgkOpZ2xb9btYV2EFK7Enll8S3A==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\"\n      },\n      \"System.ValueTuple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==\"\n      },\n      \"xunit.abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.2\",\n        \"contentHash\": \"vItLB0WkaKg0426RgWq+ZdXH6D+YV/uH28C0weWMOBnVx7I+luHuEYss9hoOngpkiN5kUpLvh9VZRx1H2sk59A==\"\n      },\n      \"xunit.analyzers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.10.0\",\n        \"contentHash\": \"4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==\"\n      },\n      \"xunit.assert\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"Swvkm6iTjZr8TiUj5vMnmfG+2dD4s/BIBgsVOzTxxmoq2ndGsmM2WIL4wuqJ8RhxydWIDOPpIaaytjT2pMTEdg==\"\n      },\n      \"xunit.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"BJ/O/tPEcHUCwQYuwqXoYccTMyw6B5dA6yh7WxWWBhKbjqTsG9RWL0nCQXM5yQYJwUuFzBkiXDPN1BO6UdBB4Q==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\",\n          \"xunit.extensibility.execution\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.extensibility.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"qr/KrR6uukHXD9e/lLQjyCPfMEDuvvhNFDzsYzCF2kKlYKiqcADfUvA9Q68rBtKFtwHFeghjWEuv15KoGD2SfA==\",\n        \"dependencies\": {\n          \"xunit.abstractions\": \"2.0.2\"\n        }\n      },\n      \"xunit.extensibility.execution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"252Dzn7i5bMPKtAL15aOP3qJhxKd+57I8ldwIQRJa745JxQuiBu5Da0vtIISVTtc3buRSkBwVnD9iUzsEmCzZA==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      },\n      \"hangfire.sqlserver\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Dapper\": \"[1.60.6, )\",\n          \"Hangfire.Core\": \"[1.0.0, )\"\n        }\n      },\n      \"hangfire.sqlserver.msmq\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Hangfire.Core\": \"[1.0.0, )\",\n          \"Hangfire.SqlServer\": \"[1.0.0, )\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/CountersAggregatorFacts.cs",
    "content": "﻿extern alias ReferencedDapper;\n\nusing System;\nusing System.Data.Common;\nusing System.Linq;\nusing System.Threading;\nusing ReferencedDapper::Dapper;\nusing Xunit;\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class CountersAggregatorFacts\n    {\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void CountersAggregatorExecutesProperly(bool useMicrosoftDataSqlClient)\n        {\n            var createSql = $@\"\ninsert into [{Constants.DefaultSchema}].Counter ([Key], [Value], ExpireAt) \nvalues ('key', 1, @expireAt)\";\n\n            using (var connection = CreateConnection(useMicrosoftDataSqlClient))\n            {\n                // Arrange\n                connection.Execute(createSql, new { expireAt = DateTime.UtcNow.AddHours(1) });\n\n                var aggregator = CreateAggregator(useMicrosoftDataSqlClient);\n                var cts = new CancellationTokenSource();\n                cts.Cancel();\n\n                // Act\n                aggregator.Execute(cts.Token);\n\n                // Assert\n                Assert.Equal(1, connection.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].AggregatedCounter\").Single());\n            }\n        }\n\n        private static DbConnection CreateConnection(bool useMicrosoftDataSqlClient)\n        {\n            return ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient);\n        }\n\n        private static CountersAggregator CreateAggregator(bool useMicrosoftDataSqlClient)\n        {\n            var storage = new SqlServerStorage(() => ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient));\n            return new CountersAggregator(storage, TimeSpan.Zero);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/ExpirationManagerFacts.cs",
    "content": "﻿extern alias ReferencedDapper;\n\nusing System;\nusing System.Data.Common;\nusing System.Linq;\nusing System.Threading;\nusing ReferencedDapper::Dapper;\nusing Xunit;\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class ExpirationManagerFacts\n    {\n        private readonly CancellationTokenSource _cts = new CancellationTokenSource();\n        \n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            Assert.Throws<ArgumentNullException>(() => new ExpirationManager(null, TimeSpan.Zero, TimeSpan.FromTicks(1)));\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Execute_RemovesOutdatedRecords(bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = CreateConnection(useMicrosoftDataSqlClient))\n            {\n                CreateExpirationEntry(connection, DateTime.UtcNow.AddMonths(-1));\n                var manager = CreateManager(useMicrosoftDataSqlClient);\n\n                manager.Execute(_cts.Token);\n\n                Assert.True(IsEntryExpired(connection));\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Execute_DoesNotRemoveEntries_WithNoExpirationTimeSet(bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = CreateConnection(useMicrosoftDataSqlClient))\n            {\n                CreateExpirationEntry(connection, null);\n                var manager = CreateManager(useMicrosoftDataSqlClient);\n\n                manager.Execute(_cts.Token);\n\n                Assert.False(IsEntryExpired(connection));\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Execute_DoesNotRemoveEntries_WithFreshExpirationTime(bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = CreateConnection(useMicrosoftDataSqlClient))\n            {\n                CreateExpirationEntry(connection, DateTime.UtcNow.AddMonths(1));\n                var manager = CreateManager(useMicrosoftDataSqlClient);\n\n                manager.Execute(_cts.Token);\n\n                Assert.False(IsEntryExpired(connection));\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Execute_Processes_AggregatedCounterTable(bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = CreateConnection(useMicrosoftDataSqlClient))\n            {\n                // Arrange\n                var createSql = $@\"\ninsert into [{Constants.DefaultSchema}].AggregatedCounter ([Key], [Value], ExpireAt) \nvalues ('key', 1, @expireAt)\";\n                connection.Execute(createSql, new { expireAt = DateTime.UtcNow.AddMonths(-1) });\n\n                var manager = CreateManager(useMicrosoftDataSqlClient);\n\n                // Act\n                manager.Execute(_cts.Token);\n\n                // Assert\n                Assert.Equal(0, connection.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].Counter\").Single());\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Execute_Processes_JobTable(bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = CreateConnection(useMicrosoftDataSqlClient))\n            {\n                // Arrange\n                var createSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt, ExpireAt) \nvalues ('', '', getutcdate(), @expireAt)\";\n                connection.Execute(createSql, new { expireAt = DateTime.UtcNow.AddMonths(-1) });\n\n                var manager = CreateManager(useMicrosoftDataSqlClient);\n\n                // Act\n                manager.Execute(_cts.Token);\n\n                // Assert\n                Assert.Equal(0, connection.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].Job\").Single());\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Execute_Processes_StateTable_WhenOptionIsConfigured(bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = CreateConnection(useMicrosoftDataSqlClient))\n            {\n                // Arrange\n                var now = DateTime.UtcNow;\n                var createSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, StateName, CreatedAt)\nvalues ('', '', '', getutcdate());\ndeclare @JobId bigint;\nset @JobId = scope_identity();\ninsert into [{Constants.DefaultSchema}].State (JobId, Name, CreatedAt)\nvalues (@JobId, 'old-state-1', @createdAt1);\ninsert into [{Constants.DefaultSchema}].State (JobId, Name, CreatedAt)\nvalues (@JobId, 'old-state-2', @createdAt2);\ninsert into [{Constants.DefaultSchema}].State (JobId, Name, CreatedAt)\nvalues (@JobId, 'current-state', @createdAt3);\ndeclare @StateId bigint;\nset @StateId = scope_identity();\nupdate [{Constants.DefaultSchema}].Job set StateId = @StateId;\nselect @JobId as Id;\";\n\n                var jobId = connection\n                    .Query(createSql, new { createdAt1 = now.AddDays(-1), createdAt2 = now.AddMonths(-1), createdAt3 = now.AddMonths(-1) })\n                    .Single().Id;\n\n                var manager = CreateManager(useMicrosoftDataSqlClient, TimeSpan.FromDays(7));\n\n                // Act\n                manager.Execute(_cts.Token);\n\n                // Assert\n                var states = connection\n                    .Query<string>($\"select [Name] from [{Constants.DefaultSchema}].State where JobId = @jobId order by Id\", new { jobId })\n                    .ToList();\n                \n                Assert.Equal(\"old-state-1\", states[0]);\n                Assert.Equal(\"current-state\", states[1]);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Execute_Processes_ListTable(bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = CreateConnection(useMicrosoftDataSqlClient))\n            {\n                // Arrange\n                var createSql = $@\"\ninsert into [{Constants.DefaultSchema}].List ([Key], ExpireAt) \nvalues ('key', @expireAt)\";\n                connection.Execute(createSql, new { expireAt = DateTime.UtcNow.AddMonths(-1) });\n\n                var manager = CreateManager(useMicrosoftDataSqlClient);\n\n                // Act\n                manager.Execute(_cts.Token);\n\n                // Assert\n                Assert.Equal(0, connection.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].List\").Single());\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Execute_Processes_SetTable(bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = CreateConnection(useMicrosoftDataSqlClient))\n            {\n                // Arrange\n                var createSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Score], [Value], ExpireAt) \nvalues ('key', 0, '', @expireAt)\";\n                connection.Execute(createSql, new { expireAt = DateTime.UtcNow.AddMonths(-1) });\n\n                var manager = CreateManager(useMicrosoftDataSqlClient);\n\n                // Act\n                manager.Execute(_cts.Token);\n\n                // Assert\n                Assert.Equal(0, connection.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].[Set]\").Single());\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Execute_Processes_HashTable(bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = CreateConnection(useMicrosoftDataSqlClient))\n            {\n                // Arrange\n                var createSql = $@\"\ninsert into [{Constants.DefaultSchema}].Hash ([Key], [Field], [Value], ExpireAt) \nvalues ('key', 'field', '', @expireAt)\";\n                connection.Execute(createSql, new { expireAt = DateTime.UtcNow.AddMonths(-1) });\n\n                var manager = CreateManager(useMicrosoftDataSqlClient);\n\n                // Act\n                manager.Execute(_cts.Token);\n\n                // Assert\n                Assert.Equal(0, connection.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].Hash\").Single());\n            }\n        }\n\n        private static void CreateExpirationEntry(DbConnection connection, DateTime? expireAt)\n        {\n            var insertSql = $@\"\ninsert into [{Constants.DefaultSchema}].AggregatedCounter ([Key], [Value], [ExpireAt])\nvalues (N'key', 1, @expireAt)\";\n\n            connection.Execute(insertSql, new { expireAt });\n        }\n\n        private static bool IsEntryExpired(DbConnection connection)\n        {\n            var count = connection.Query<int>(\n                    $\"select count(*) from [{Constants.DefaultSchema}].AggregatedCounter where [Key] = N'key'\").Single();\n            return count == 0;\n        }\n\n        private DbConnection CreateConnection(bool useMicrosoftDataSqlClient)\n        {\n            return ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient);\n        }\n\n        private ExpirationManager CreateManager(bool useMicrosoftDataSqlClient, TimeSpan? stateExpirationTimeout = null)\n        {\n            var storage = new SqlServerStorage(() => ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient));\n            return new ExpirationManager(storage, stateExpirationTimeout ?? TimeSpan.Zero, TimeSpan.FromTicks(1));\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/GlobalTestsConfiguration.cs",
    "content": "﻿using Xunit;\n\n[assembly: CollectionBehavior(MaxParallelThreads = 1)]\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/Hangfire.SqlServer.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n  <PropertyGroup>\r\n    <TargetFrameworks>net452;net461;net6.0;net8.0</TargetFrameworks>\r\n    <NoWarn>0618</NoWarn>\r\n  </PropertyGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net452' or '$(TargetFramework)'=='net461'\">\r\n    <PackageReference Include=\"Microsoft.NETFramework.ReferenceAssemblies\" Version=\"1.0.3\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net452'\">\r\n    <PackageReference Include=\"Dapper\" Version=\"1.60.6\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"5.0.1\" NoWarn=\"NU1903\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net461'\">\r\n    <PackageReference Include=\"System.Data.SqlClient\" Version=\"4.5.0\" NoWarn=\"NU1902;NU1903\" />\r\n    <PackageReference Include=\"Dapper\" Version=\"1.60.6\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"5.0.1\" NoWarn=\"NU1903\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='netcoreapp3.1'\">\r\n    <PackageReference Include=\"System.Data.SqlClient\" Version=\"4.8.6\" NoWarn=\"NU1902\" />\r\n    <PackageReference Include=\"Microsoft.Data.SqlClient\" Version=\"3.1.5\" NoWarn=\"NU1902\" />\r\n    <PackageReference Include=\"Dapper\" Version=\"2.1.28\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net6.0'\">\r\n    <PackageReference Include=\"System.Data.SqlClient\" Version=\"4.8.6\" NoWarn=\"NU1902\" />\r\n    <PackageReference Include=\"Microsoft.Data.SqlClient\" Version=\"5.2.2\" NoWarn=\"NU1902\" />\r\n    <PackageReference Include=\"Dapper\" Version=\"2.1.28\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup Condition=\"'$(TargetFramework)'=='net8.0'\">\r\n    <PackageReference Include=\"System.Data.SqlClient\" Version=\"4.9.0\" NoWarn=\"NU1902\" />\r\n    <PackageReference Include=\"Microsoft.Data.SqlClient\" Version=\"6.1.1\" NoWarn=\"NU1902\" />\r\n    <PackageReference Include=\"Dapper\" Version=\"2.1.28\" />\r\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.Core\\Hangfire.Core.csproj\" />\r\n    <ProjectReference Include=\"..\\..\\src\\Hangfire.SqlServer\\Hangfire.SqlServer.csproj\" />\r\n  </ItemGroup>\r\n\r\n  <Target Name=\"ChangeAliasesOfStrongNameAssemblies\" BeforeTargets=\"FindReferenceAssembliesForReferences;ResolveReferences\">\r\n    <ItemGroup>\r\n      <ReferencePath Condition=\"'%(FileName)' == 'Dapper'\">\r\n        <Aliases>ReferencedDapper</Aliases>\r\n      </ReferencePath>\r\n    </ItemGroup>\r\n  </Target>\r\n</Project>"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/PersistentJobQueueProviderCollectionFacts.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Moq;\nusing Xunit;\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class PersistentJobQueueProviderCollectionFacts\n    {\n        private static readonly string[] Queues = { \"default\", \"critical\" };\n        private readonly Mock<IPersistentJobQueueProvider> _defaultProvider;\n        private readonly Mock<IPersistentJobQueueProvider> _provider;\n\n        public PersistentJobQueueProviderCollectionFacts()\n        {\n            _defaultProvider = new Mock<IPersistentJobQueueProvider>();\n            _provider = new Mock<IPersistentJobQueueProvider>();\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenDefaultProviderIsNull()\n        {\n            Assert.Throws<ArgumentNullException>(\n                () => new PersistentJobQueueProviderCollection(null));\n        }\n\n        [Fact]\n        public void Enumeration_IncludesTheDefaultProvider()\n        {\n            var collection = CreateCollection();\n\n            var result = collection.ToArray();\n\n            Assert.Single(result);\n            Assert.Same(_defaultProvider.Object, result[0]);\n        }\n\n        [Fact]\n        public void GetProvider_ReturnsTheDefaultProvider_WhenProviderCanNotBeResolvedByQueue()\n        {\n            var collection = CreateCollection();\n\n            var provider = collection.GetProvider(\"queue\");\n\n            Assert.Same(_defaultProvider.Object, provider);\n        }\n\n        [Fact]\n        public void Add_ThrowsAnException_WhenProviderIsNull()\n        {\n            var collection = CreateCollection();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => collection.Add(null, Queues));\n\n            Assert.Equal(\"provider\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Add_ThrowsAnException_WhenQueuesCollectionIsNull()\n        {\n            var collection = CreateCollection();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => collection.Add(_provider.Object, null));\n\n            Assert.Equal(\"queues\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Enumeration_ContainsAddedProvider()\n        {\n            var collection = CreateCollection();\n\n            collection.Add(_provider.Object, Queues);\n\n            Assert.Contains(_provider.Object, collection);\n        }\n\n        [Fact]\n        public void GetProvider_CanBeResolved_ByAnyQueue()\n        {\n            var collection = CreateCollection();\n            collection.Add(_provider.Object, Queues);\n\n            var provider1 = collection.GetProvider(\"default\");\n            var provider2 = collection.GetProvider(\"critical\");\n\n            Assert.NotSame(_defaultProvider.Object, provider1);\n            Assert.Same(provider1, provider2);\n        }\n\n        private PersistentJobQueueProviderCollection CreateCollection()\n        {\n            return new PersistentJobQueueProviderCollection(_defaultProvider.Object);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/SqlServerConnectionFacts.cs",
    "content": "﻿extern alias ReferencedDapper;\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data.Common;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Linq;\nusing System.Threading;\nusing ReferencedDapper::Dapper;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n// ReSharper disable PossibleNullReferenceException\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class SqlServerConnectionFacts\n    {\n        private readonly Mock<IPersistentJobQueue> _queue;\n        private readonly PersistentJobQueueProviderCollection _providers;\n\n        public SqlServerConnectionFacts()\n        {\n            _queue = new Mock<IPersistentJobQueue>();\n\n            var provider = new Mock<IPersistentJobQueueProvider>();\n            provider.Setup(x => x.GetJobQueue())\n                .Returns(_queue.Object);\n\n            _providers = new PersistentJobQueueProviderCollection(provider.Object);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerConnection(null));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void FetchNextJob_DelegatesItsExecution_ToTheQueue(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var token = new CancellationToken();\n                var queues = new[] { \"default\" };\n\n                connection.FetchNextJob(queues, token);\n\n                _queue.Verify(x => x.Dequeue(queues, token));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void FetchNextJob_Throws_IfMultipleProvidersResolved(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var token = new CancellationToken();\n                var anotherProvider = new Mock<IPersistentJobQueueProvider>();\n                _providers.Add(anotherProvider.Object, new [] { \"critical\" });\n\n                Assert.Throws<InvalidOperationException>(\n                    () => connection.FetchNextJob(new[] { \"critical\", \"default\" }, token));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void CreateWriteTransaction_ReturnsNonNullInstance(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var transaction = connection.CreateWriteTransaction();\n                Assert.NotNull(transaction);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void AcquireLock_ReturnsNonNullInstance(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                using (var @lock = connection.AcquireDistributedLock(\"1\", TimeSpan.FromSeconds(1)))\n                {\n                    Assert.NotNull(@lock);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void AcquireDistributedLock_ThrowsAnException_WhenResourceIsNullOrEmpty(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                () => connection.AcquireDistributedLock(\"\", TimeSpan.FromMinutes(5)));\n\n                Assert.Equal(\"resource\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void AcquireDistributedLock_AcquiresExclusiveApplicationLock_OnSession(bool useMicrosoftDataSqlClient)\n        {\n            using (var sql = ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient))\n            {\n                var storage = new SqlServerStorage(sql);\n\n                using (var connection = new SqlServerConnection(storage))\n                using (connection.AcquireDistributedLock(\"hello\", TimeSpan.FromMinutes(5)))\n                {\n                    var lockMode = sql.Query<string>(\n                        $\"select applock_mode('public', '{Constants.DefaultSchema}:hello', 'session')\").Single();\n\n                    Assert.Equal(\"Exclusive\", lockMode);\n                }\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void AcquireDistributedLock_ThrowsAnException_IfLockCanNotBeGranted(bool useMicrosoftDataSqlClient)\n        {\n            var releaseLock = new ManualResetEventSlim(false);\n            var lockAcquired = new ManualResetEventSlim(false);\n\n            var thread = new Thread(\n                () => UseConnection(connection1 =>\n                {\n                    using (connection1.AcquireDistributedLock(\"exclusive\", TimeSpan.Zero))\n                    {\n                        lockAcquired.Set();\n                        releaseLock.Wait();\n                    }\n                }, useMicrosoftDataSqlClient));\n            thread.Start();\n\n            lockAcquired.Wait();\n\n            UseConnection(connection2 =>\n            {\n                Assert.Throws<DistributedLockTimeoutException>(\n                    () =>\n                    {\n                        using (connection2.AcquireDistributedLock(\"exclusive\", TimeSpan.FromSeconds(1)))\n                        {\n                        }\n                    });\n            }, useMicrosoftDataSqlClient);\n\n            releaseLock.Set();\n            thread.Join();\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void AcquireDistributedLock_Dispose_ReleasesExclusiveApplicationLock(bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var distributedLock = connection.AcquireDistributedLock(\"hello\", TimeSpan.FromMinutes(5));\n                distributedLock.Dispose();\n\n                var lockMode = sql.Query<string>(\n                    $\"select applock_mode('public', '{Constants.DefaultSchema}:hello', 'session')\").Single();\n\n                Assert.Equal(\"NoLock\", lockMode);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void AcquireDistributedLock_IsReentrant_FromTheSameConnection_OnTheSameResource(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                using (connection.AcquireDistributedLock(\"hello\", TimeSpan.FromMinutes(5)))\n                using (connection.AcquireDistributedLock(\"hello\", TimeSpan.FromMinutes(5)))\n                {\n                    Assert.True(true);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void CreateExpiredJob_ThrowsAnException_WhenJobIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.CreateExpiredJob(\n                        null,\n                        new Dictionary<string, string>(),\n                        DateTime.UtcNow,\n                        TimeSpan.Zero));\n\n                Assert.Equal(\"job\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void CreateExpiredJob_ThrowsAnException_WhenParametersCollectionIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.CreateExpiredJob(\n                        Job.FromExpression(() => SampleMethod(\"hello\")),\n                        null,\n                        DateTime.UtcNow,\n                        TimeSpan.Zero));\n\n                Assert.Equal(\"parameters\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CreatesAJobInTheStorage_AndSetsItsParameters(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", \"Value1\" }, { \"Key2\", \"Value2\" }, { \"Key3\", \"Value3\" } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                Assert.NotNull(jobId);\n                Assert.NotEmpty(jobId);\n\n                var sqlJob = sql.Query($\"select * from [{Constants.DefaultSchema}].Job\").Single();\n                Assert.Equal(jobId, sqlJob.Id.ToString());\n                Assert.Equal(createdAt, sqlJob.CreatedAt);\n                Assert.Null((int?) sqlJob.StateId);\n                Assert.Null((string) sqlJob.StateName);\n\n                var invocationData = InvocationData.DeserializePayload((string)sqlJob.InvocationData);\n                invocationData.Arguments = sqlJob.Arguments;\n\n                var job = invocationData.Deserialize();\n                Assert.Equal(typeof(SqlServerConnectionFacts), job.Type);\n                Assert.Equal(\"SampleMethod\", job.Method.Name);\n                Assert.Equal(\"Hello\", job.Args[0]);\n\n                Assert.True(createdAt.AddDays(1).AddMinutes(-1) < sqlJob.ExpireAt);\n                Assert.True(sqlJob.ExpireAt < createdAt.AddDays(1).AddMinutes(1));\n\n                var parameters = sql.Query(\n                    $\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\",\n                    new { id = jobId })\n                    .ToDictionary(x => (string) x.Name, x => (string) x.Value);\n\n                Assert.Equal(\"Value1\", parameters[\"Key1\"]);\n                Assert.Equal(\"Value2\", parameters[\"Key2\"]);\n                Assert.Equal(\"Value3\", parameters[\"Key3\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateParametersWithNonNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", \"Value1\" } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Equal(\"Value1\", parameters[\"Key1\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateTwoParametersWithNonNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", \"Value1\" }, { \"Key2\", \"Value2\" } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Equal(\"Value1\", parameters[\"Key1\"]);\n                Assert.Equal(\"Value2\", parameters[\"Key2\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateThreeParametersWithNonNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", \"Value1\" }, { \"Key2\", \"Value2\" }, { \"Key3\", \"Value3\" } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Equal(\"Value1\", parameters[\"Key1\"]);\n                Assert.Equal(\"Value2\", parameters[\"Key2\"]);\n                Assert.Equal(\"Value3\", parameters[\"Key3\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateFourParametersWithNonNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", \"Value1\" }, { \"Key2\", \"Value2\" }, { \"Key3\", \"Value3\" }, { \"Key4\", \"Value4\" } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Equal(\"Value1\", parameters[\"Key1\"]);\n                Assert.Equal(\"Value2\", parameters[\"Key2\"]);\n                Assert.Equal(\"Value3\", parameters[\"Key3\"]);\n                Assert.Equal(\"Value4\", parameters[\"Key4\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateManyParametersWithNonNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", \"Value1\" }, { \"Key2\", \"Value2\" }, { \"Key3\", \"Value3\" }, { \"Key4\", \"Value4\" }, { \"Key5\", \"Value5\" } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Equal(\"Value1\", parameters[\"Key1\"]);\n                Assert.Equal(\"Value2\", parameters[\"Key2\"]);\n                Assert.Equal(\"Value3\", parameters[\"Key3\"]);\n                Assert.Equal(\"Value4\", parameters[\"Key4\"]);\n                Assert.Equal(\"Value5\", parameters[\"Key5\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateParametersWithNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", null } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Null(parameters[\"Key1\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateTwoParametersWithNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", null }, { \"Key2\", null } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Null(parameters[\"Key1\"]);\n                Assert.Null(parameters[\"Key2\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateThreeParametersWithNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", null }, { \"Key2\", null }, { \"Key3\", null } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Null(parameters[\"Key1\"]);\n                Assert.Null(parameters[\"Key2\"]);\n                Assert.Null(parameters[\"Key3\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateFourParametersWithNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", null }, { \"Key2\", null }, { \"Key3\", null }, { \"Key4\", null } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Null(parameters[\"Key1\"]);\n                Assert.Null(parameters[\"Key2\"]);\n                Assert.Null(parameters[\"Key3\"]);\n                Assert.Null(parameters[\"Key4\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateManyParametersWithNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string> { { \"Key1\", null }, { \"Key2\", null }, { \"Key3\", null }, { \"Key4\", null }, { \"Key5\", null } },\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Null(parameters[\"Key1\"]);\n                Assert.Null(parameters[\"Key2\"]);\n                Assert.Null(parameters[\"Key3\"]);\n                Assert.Null(parameters[\"Key4\"]);\n                Assert.Null(parameters[\"Key5\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void CreateExpiredJob_CanCreateJobWithoutParameters(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string>(), \n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                var parameters = sql\n                    .Query($\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id\", new { id = jobId })\n                    .ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                Assert.Empty(parameters);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetJobData_ThrowsAnException_WhenJobIdIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(\n                connection => Assert.Throws<ArgumentNullException>(() => connection.GetJobData(null)),\n                useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetJobData_ReturnsNull_WhenThereIsNoSuchJob(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetJobData(\"1\");\n                Assert.Null(result);\n            }, useMicrosoftDataSqlClient);\n        }\n        \n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetJobData_ReturnsNull_WhenIdentifierCanNotBeParsedAsLong(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetJobData(\"some-non-long-id\");\n                Assert.Null(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetJobData_ReturnsResult_WhenJobExists(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, StateName, CreatedAt)\nvalues (@invocationData, @arguments, @stateName, getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnections((sql, connection) =>\n            {\n                var job = Job.FromExpression(() => SampleMethod(\"wrong\"));\n\n                var jobId = sql.Query(\n                    arrangeSql,\n                    new\n                    {\n                        invocationData = JobHelper.ToJson(InvocationData.Serialize(job)),\n                        stateName = \"Succeeded\",\n                        arguments = \"['Arguments']\"\n                    }).Single();\n\n                var result = connection.GetJobData(((long)jobId.Id).ToString());\n\n                Assert.NotNull(result);\n                Assert.NotNull(result.Job);\n                Assert.Equal(\"Succeeded\", result.State);\n                Assert.Equal(\"Arguments\", result.Job.Args[0]);\n                Assert.Null(result.LoadException);\n                Assert.True(DateTime.UtcNow.AddMinutes(-1) < result.CreatedAt);\n                Assert.True(result.CreatedAt < DateTime.UtcNow.AddMinutes(1));\n                Assert.Empty(result.ParametersSnapshot);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetJobData_ReturnsResultWithParameters_WhenJobAndParametersExist(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, StateName, CreatedAt)\nvalues (@invocationData, @arguments, @stateName, getutcdate())\ndeclare @id bigint = scope_identity();\ninsert into [{Constants.DefaultSchema}].JobParameter ([JobId], [Name], [Value])\nvalues (@id, N'Param1', N'Value1'), (@id, N'Param2', 'Value2')\nselect @id as Id\";\n\n            UseConnections((sql, connection) =>\n            {\n                var job = Job.FromExpression(() => SampleMethod(\"wrong\"));\n\n                var jobId = sql.Query(\n                    arrangeSql,\n                    new\n                    {\n                        invocationData = JobHelper.ToJson(InvocationData.Serialize(job)),\n                        stateName = \"Succeeded\",\n                        arguments = \"['Arguments']\"\n                    }).Single();\n\n                var result = connection.GetJobData(((long)jobId.Id).ToString());\n\n                Assert.NotNull(result);\n                Assert.Equal(\"SampleMethod\", result.Job.Method.Name);\n                Assert.Equal(2, result.ParametersSnapshot.Count);\n                Assert.Equal(\"Value1\", result.ParametersSnapshot[\"Param1\"]);\n                Assert.Equal(\"Value2\", result.ParametersSnapshot[\"Param2\"]);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetStateData_ThrowsAnException_WhenJobIdIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(\n                connection => Assert.Throws<ArgumentNullException>(() => connection.GetStateData(null)),\n                useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetStateData_ReturnsNull_IfThereIsNoSuchState(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetStateData(\"1\");\n                Assert.Null(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetStateData_ReturnsNull_WhenIdentifierCanNotBeParsedAsLong(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetStateData(\"some-non-long-id\");\n                Assert.Null(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetStateData_ReturnsCorrectData(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, StateName, CreatedAt)\nvalues ('', '', '', getutcdate());\ndeclare @JobId bigint;\nset @JobId = scope_identity();\ninsert into [{Constants.DefaultSchema}].State (JobId, Name, CreatedAt)\nvalues (@JobId, 'old-state', getutcdate());\ninsert into [{Constants.DefaultSchema}].State (JobId, Name, Reason, Data, CreatedAt)\nvalues (@JobId, @name, @reason, @data, getutcdate());\ndeclare @StateId bigint;\nset @StateId = scope_identity();\nupdate [{Constants.DefaultSchema}].Job set StateId = @StateId;\nselect @JobId as Id;\";\n\n            UseConnections((sql, connection) =>\n            {\n                var data = new Dictionary<string, string>\n                {\n                    { \"Key\", \"Value\" }\n                };\n\n                var jobId = (long)sql.Query(\n                    arrangeSql,\n                    new { name = \"Name\", reason = \"Reason\", @data = JobHelper.ToJson(data) }).Single().Id;\n\n                var result = connection.GetStateData(jobId.ToString());\n\n                Assert.NotNull(result);\n\n                Assert.Equal(\"Name\", result.Name);\n                Assert.Equal(\"Reason\", result.Reason);\n                Assert.Equal(\"Value\", result.Data[\"Key\"]);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetStateData_ReturnsCorrectData_WhenPropertiesAreCamelcased(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, StateName, CreatedAt)\nvalues ('', '', '', getutcdate());\ndeclare @JobId bigint;\nset @JobId = scope_identity();\ninsert into [{Constants.DefaultSchema}].State (JobId, Name, CreatedAt)\nvalues (@JobId, 'old-state', getutcdate());\ninsert into [{Constants.DefaultSchema}].State (JobId, Name, Reason, Data, CreatedAt)\nvalues (@JobId, @name, @reason, @data, getutcdate());\ndeclare @StateId bigint;\nset @StateId = scope_identity();\nupdate [{Constants.DefaultSchema}].Job set StateId = @StateId;\nselect @JobId as Id;\";\n\n            UseConnections((sql, connection) =>\n            {\n                var data = new Dictionary<string, string>\n                {\n                    { \"key\", \"Value\" }\n                };\n\n                var jobId = (long)sql.Query(\n                    arrangeSql,\n                    new { name = \"Name\", reason = \"Reason\", @data = JobHelper.ToJson(data) }).Single().Id;\n\n                var result = connection.GetStateData(jobId.ToString());\n                Assert.NotNull(result);\n\n                Assert.Equal(\"Value\", result.Data[\"Key\"]);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetJobData_ReturnsJobLoadException_IfThereWasADeserializationException(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, StateName, CreatedAt)\nvalues (@invocationData, @arguments, @stateName, getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnections((sql, connection) =>\n            {\n                var jobId = sql.Query(\n                    arrangeSql,\n                    new\n                    {\n                        invocationData = JobHelper.ToJson(new InvocationData(null, null, null, null)),\n                        stateName = \"Succeeded\",\n                        arguments = \"['Arguments']\"\n                    }).Single();\n\n                var result = connection.GetJobData(((long)jobId.Id).ToString());\n\n                Assert.NotNull(result.LoadException);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void SetParameter_ThrowsAnException_WhenJobIdIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.SetJobParameter(null, \"name\", \"value\"));\n\n                Assert.Equal(\"id\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void SetParameter_ThrowsAnException_WhenNameIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.SetJobParameter(\"1\", null, \"value\"));\n\n                Assert.Equal(\"name\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void SetParameters_CreatesNewParameter_WhenParameterWithTheGivenNameDoesNotExists(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues ('', '', getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnections((sql, connection) =>\n            {\n                var job = sql.Query(arrangeSql).Single();\n                string jobId = job.Id.ToString();\n\n                connection.SetJobParameter(jobId, \"Name\", \"Value\");\n\n                var parameter = sql.Query(\n                    $\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id and Name = @name\",\n                    new { id = jobId, name = \"Name\" }).Single();\n\n                Assert.Equal(\"Value\", parameter.Value);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void SetParameter_UpdatesValue_WhenParameterWithTheGivenName_AlreadyExists(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues ('', '', getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnections((sql, connection) =>\n            {\n                var job = sql.Query(arrangeSql).Single();\n                string jobId = job.Id.ToString();\n\n                connection.SetJobParameter(jobId, \"Name\", \"Value\");\n                connection.SetJobParameter(jobId, \"Name\", \"AnotherValue\");\n\n                var parameter = sql.Query(\n                    $\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id and Name = @name\",\n                    new { id = jobId, name = \"Name\" }).Single();\n\n                Assert.Equal(\"AnotherValue\", parameter.Value);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void SetParameter_CanAcceptNulls_AsValues(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues ('', '', getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnections((sql, connection) =>\n            {\n                var job = sql.Query(arrangeSql).Single();\n                string jobId = job.Id.ToString();\n\n                connection.SetJobParameter(jobId, \"Name\", null);\n\n                var parameter = sql.Query(\n                    $\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id and Name = @name\",\n                    new { id = jobId, name = \"Name\" }).Single();\n\n                Assert.Equal((string) null, parameter.Value);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void SetJobParameter_WithIgnoreDupKeyOption_InsertsNonExistingValue(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            try\n            {\n                UseConnections((sql, connection) =>\n                {\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[JobParameter] REBUILD WITH (IGNORE_DUP_KEY = ON)\");\n\n                    string jobId = sql.Query($@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt) values ('', '', getutcdate())\nselect scope_identity() as Id\").Single().Id.ToString();\n\n                    connection.SetJobParameter(jobId, \"Name\", \"Value\");\n\n                    var parameter = sql.Query(\n                        $\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @jobId and Name = N'Name'\",\n                        new { jobId }).Single();\n\n                    Assert.Equal(\"Value\", parameter.Value);\n                }, useBatching, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnections((sql, _) => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[JobParameter] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useBatching, useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void SetJobParameter_WithIgnoreDupKeyOption_UpdatesExistingValue_WhenIgnoreDupKeyOptionIsSet(bool setIgnoreDupKey, bool useMicrosoftDataSqlClient)\n        {\n            try\n            {\n                UseConnections((sql, connection) =>\n                {\n                    sql.Execute(\"SET XACT_ABORT ON\");\n                    var onOrOff = setIgnoreDupKey ? \"ON\" : \"OFF\";\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[JobParameter] REBUILD WITH (IGNORE_DUP_KEY = {onOrOff})\");\n\n                    string jobId1 = sql.Query($@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt) values ('', '', getutcdate())\nselect scope_identity() as Id\").Single().Id.ToString();\n\n                    string jobId2 = sql.Query($@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt) values ('', '', getutcdate())\nselect scope_identity() as Id\").Single().Id.ToString();\n\n                    sql.Execute(\n                        $@\"insert into [{Constants.DefaultSchema}].[JobParameter] (JobId, Name, Value) values\n(@jobId1, N'Name1', N'Value1'),\n(@jobId1, N'Name2', N'Value1'),\n(@jobId2, N'Name1', N'Value1')\", new { jobId1, jobId2 });\n\n                    connection.SetJobParameter(jobId1, \"Name1\", \"Value2\");\n\n                    var parameters1 = sql.Query(\n                        $\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @jobId1\",\n                        new { jobId1 }).ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                    Assert.Equal(\"Value2\", parameters1[\"Name1\"]);\n                    Assert.Equal(\"Value1\", parameters1[\"Name2\"]);\n\n                    var parameters2 = sql.Query(\n                        $\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @jobId2\",\n                        new { jobId2 }).ToDictionary(x => (string)x.Name, x => (string)x.Value);\n\n                    Assert.Equal(\"Value1\", parameters2[\"Name1\"]);\n                }, useBatching: false, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnections((sql, _) => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[JobParameter] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useBatching: false, useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetParameter_ThrowsAnException_WhenJobIdIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.GetJobParameter(null, \"hello\"));\n\n                Assert.Equal(\"id\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetParameter_ThrowsAnException_WhenNameIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.GetJobParameter(\"1\", null));\n\n                Assert.Equal(\"name\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetParameter_ReturnsNull_WhenParameterDoesNotExists(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var value = connection.GetJobParameter(\"1\", \"hello\");\n                Assert.Null(value);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetParameter_ReturnsNull_WhenJobIdCanNotBeParsedAsLong(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetJobParameter(\"some-non-long-id\", \"name\");\n                Assert.Null(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetParameter_ReturnsParameterValue_WhenJobExists(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ndeclare @id bigint\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues ('', '', getutcdate())\nset @id = scope_identity()\ninsert into [{Constants.DefaultSchema}].JobParameter (JobId, Name, Value)\nvalues (@id, @name, @value)\nselect @id\";\n\n            UseConnections((sql, connection) =>\n            {\n                var id = sql.Query<long>(\n                    arrangeSql,\n                    new { name = \"name\", value = \"value\" }).Single();\n\n                var value = connection.GetJobParameter(id.ToString(), \"name\");\n\n                Assert.Equal(\"value\", value);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetFirstByLowestScoreFromSet_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.GetFirstByLowestScoreFromSet(null, 0, 1));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetFirstByLowestScoreFromSet_ThrowsAnException_ToScoreIsLowerThanFromScore(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentException>(\n                    () => connection.GetFirstByLowestScoreFromSet(\"key\", 0, -1));\n\n                Assert.Equal(\"toScore\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetFirstByLowestScoreFromSet_ReturnsNull_WhenTheKeyDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetFirstByLowestScoreFromSet(\n                    \"key\", 0, 1);\n\n                Assert.Null(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetFirstByLowestScoreFromSet_ThrowsArgException_WhenRequestingLessThanZero(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentException>(\n                    () => connection.GetFirstByLowestScoreFromSet(\"key\", 0, 1, -1));\n\n                Assert.Equal(\"count\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetFirstByLowestScoreFromSet_ReturnsEmpty_WhenNoneExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetFirstByLowestScoreFromSet(\"key\", 0, 1, 10);\n                Assert.Empty(result);\n            }, useMicrosoftDataSqlClient);\n        }\n        \n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetFirstByLowestScoreFromSet_ReturnsN_WhenMoreThanNExist(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], Score, Value)\nvalues \n('key', 1.0, '1234'),\n('key', -1.0, '567'),\n('key', -5.0, '890'),\n('another-key', -2.0, 'abcd')\";\n            \n            UseConnections((sql, connection) =>\n            {\n                sql.Execute(arrangeSql);\n                \n                var result = connection.GetFirstByLowestScoreFromSet(\"key\", -10.0, 10.0, 2);\n                \n                Assert.Equal(2, result.Count);\n                Assert.Equal(\"890\", result.ElementAt(0));\n                Assert.Equal(\"567\", result.ElementAt(1));\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n        \n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetFirstByLowestScoreFromSet_ReturnsN_WhenMoreThanNExist_And_RequestedCountIsGreaterThanN(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], Score, Value)\nvalues \n('key', 1.0, '1234'),\n('key', -1.0, '567'),\n('key', -5.0, '890'),\n('another-key', -2.0, 'abcd')\";\n            \n            UseConnections((sql, connection) =>\n            {\n                sql.Execute(arrangeSql);\n                \n                var result = connection.GetFirstByLowestScoreFromSet(\"another-key\", -10.0, 10.0, 5);\n                \n                Assert.Single(result);\n                Assert.Equal(\"abcd\", result.First());\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetFirstByLowestScoreFromSet_ReturnsTheValueWithTheLowestScore(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], Score, Value)\nvalues \n('key', 1.0, '1.0'),\n('key', -1.0, '-1.0'),\n('key', -5.0, '-5.0'),\n('another-key', -2.0, '-2.0')\";\n\n            UseConnections((sql, connection) =>\n            {\n                sql.Execute(arrangeSql);\n\n                var result = connection.GetFirstByLowestScoreFromSet(\"key\", -1.0, 3.0);\n                \n                Assert.Equal(\"-1.0\", result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void AnnounceServer_ThrowsAnException_WhenServerIdIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.AnnounceServer(null, new ServerContext()));\n\n                Assert.Equal(\"serverId\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void AnnounceServer_ThrowsAnException_WhenContextIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.AnnounceServer(\"server\", null));\n\n                Assert.Equal(\"context\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void AnnounceServer_CreatesOrUpdatesARecord(bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                var context1 = new ServerContext\n                {\n                    Queues = new[] { \"critical\", \"default\" },\n                    WorkerCount = 4\n                };\n                connection.AnnounceServer(\"server\", context1);\n\n                var server = sql.Query($\"select * from [{Constants.DefaultSchema}].Server\").Single();\n                Assert.Equal(\"server\", server.Id);\n                Assert.True(((string)server.Data).StartsWith(\n                    \"{\\\"WorkerCount\\\":4,\\\"Queues\\\":[\\\"critical\\\",\\\"default\\\"],\\\"StartedAt\\\":\"),\n                    server.Data);\n                Assert.NotNull(server.LastHeartbeat);\n                Assert.True(DateTime.UtcNow.AddHours(-1) < server.LastHeartbeat &&\n                            server.LastHeartbeat < DateTime.UtcNow.AddHours(1));\n\n                var context2 = new ServerContext\n                {\n                    Queues = new[] { \"default\" },\n                    WorkerCount = 1000 \n                };\n                connection.AnnounceServer(\"server\", context2);\n                var sameServer = sql.Query($\"select * from [{Constants.DefaultSchema}].Server\").Single();\n                Assert.Equal(\"server\", sameServer.Id);\n                Assert.Contains(\"1000\", sameServer.Data);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void RemoveServer_ThrowsAnException_WhenServerIdIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(\n                connection => Assert.Throws<ArgumentNullException>(() => connection.RemoveServer(null)),\n                useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void RemoveServer_RemovesAServerRecord(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Server (Id, Data, LastHeartbeat)\nvalues \n('Server1', '', getutcdate()),\n('Server2', '', getutcdate())\";\n\n            UseConnections((sql, connection) =>\n            {\n                sql.Execute(arrangeSql);\n\n                connection.RemoveServer(\"Server1\");\n\n                var server = sql.Query($\"select * from [{Constants.DefaultSchema}].Server\").Single();\n                Assert.NotEqual(\"Server1\", server.Id, StringComparer.OrdinalIgnoreCase);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Heartbeat_ThrowsAnException_WhenServerIdIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(\n                connection => Assert.Throws<ArgumentNullException>(() => connection.Heartbeat(null)),\n                useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Heartbeat_UpdatesLastHeartbeat_OfTheServerWithGivenId(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Server (Id, Data, LastHeartbeat)\nvalues\n('server1', '', '2012-12-12 12:12:12'),\n('server2', '', '2012-12-12 12:12:12')\";\n\n            UseConnections((sql, connection) =>\n            {\n                sql.Execute(arrangeSql);\n\n                connection.Heartbeat(\"server1\");\n\n                var servers = sql.Query($\"select * from [{Constants.DefaultSchema}].Server\")\n                    .ToDictionary(x => (string)x.Id, x => (DateTime)x.LastHeartbeat);\n\n                Assert.NotEqual(2012, servers[\"server1\"].Year);\n                Assert.Equal(2012, servers[\"server2\"].Year);\n\n                Assert.True(DateTime.UtcNow.AddHours(-1) < servers[\"server1\"] &&\n                            servers[\"server1\"] < DateTime.UtcNow.AddHours(1));\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void RemoveTimedOutServers_ThrowsAnException_WhenTimeOutIsNegative(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(\n                connection => Assert.Throws<ArgumentException>(() => connection.RemoveTimedOutServers(TimeSpan.FromMinutes(-5))),\n                useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void RemoveTimedOutServers_DoItsWorkPerfectly(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Server (Id, Data, LastHeartbeat)\nvalues (@id, '', @heartbeat)\";\n\n            UseConnections((sql, connection) =>\n            {\n                sql.Execute(\n                    arrangeSql,\n                    new[]\n                    {\n                        new { id = \"server1\", heartbeat = DateTime.UtcNow.AddDays(-1) },\n                        new { id = \"server2\", heartbeat = DateTime.UtcNow.AddHours(-12) }\n                    });\n\n                connection.RemoveTimedOutServers(TimeSpan.FromHours(15));\n\n                var liveServer = sql.Query($\"select * from [{Constants.DefaultSchema}].Server\").Single();\n                Assert.Equal(\"server2\", liveServer.Id);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetAllItemsFromSet_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(\n                connection => Assert.Throws<ArgumentNullException>(() => connection.GetAllItemsFromSet(null)),\n                useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetAllItemsFromSet_ReturnsEmptyCollection_WhenKeyDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetAllItemsFromSet(\"some-set\");\n\n                Assert.NotNull(result);\n                Assert.Empty(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetAllItemsFromSet_ReturnsAllItems(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], Score, Value)\nvalues (@key, 0.0, @value)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"some-set\", value = \"1\" },\n                    new { key = \"some-set\", value = \"2\" },\n                    new { key = \"another-set\", value = \"3\" }\n                });\n\n                // Act\n                var result = connection.GetAllItemsFromSet(\"some-set\");\n\n                // Assert\n                Assert.Equal(2, result.Count);\n                Assert.Contains(\"1\", result);\n                Assert.Contains(\"2\", result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void SetRangeInHash_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.SetRangeInHash(null, new Dictionary<string, string>()));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void SetRangeInHash_ThrowsAnException_WhenKeyValuePairsArgumentIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.SetRangeInHash(\"some-hash\", null));\n\n                Assert.Equal(\"keyValuePairs\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void SetRangeInHash_ThrowsSqlException_WhenKeyIsTooLong(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.ThrowsAny<DbException>(\n                    () => connection.SetRangeInHash(\n                    \"123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_12345\",\n                    new Dictionary<string, string> { { \"field\", \"value\" } }));\n\n                Assert.Contains(\"data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void SetRangeInHash_MergesAllRecords(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                connection.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                {\n                    { \"Key1\", \"Value1\" },\n                    { \"Key2\", \"Value2\" }\n                });\n\n                var result = sql.Query(\n                    $\"select * from [{Constants.DefaultSchema}].Hash where [Key] = @key\",\n                    new { key = \"some-hash\" })\n                    .ToDictionary(x => (string)x.Field, x => (string)x.Value);\n\n                Assert.Equal(\"Value1\", result[\"Key1\"]);\n                Assert.Equal(\"Value2\", result[\"Key2\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void SetRangeInHash_CanCreateFieldsWithNullValues(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                connection.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                {\n                    { \"Key1\", null }\n                });\n\n                var result = sql.Query(\n                        $\"select * from [{Constants.DefaultSchema}].Hash where [Key] = @key\",\n                        new { key = \"some-hash\" })\n                    .ToDictionary(x => (string)x.Field, x => (string)x.Value);\n\n                Assert.Null(result[\"Key1\"]);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void SetRangeInHash_ReleasesTheAcquiredLock(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                connection.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                {\n                    { \"Key\", \"Value\" }\n                });\n\n                var result = sql.QuerySingle<string>($\"select APPLOCK_MODE( 'public' , 'HangFire:Hash:Lock' , 'Session' )\");\n                Assert.Equal(\"NoLock\", result);\n            }, useBatching, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false), InlineData(false, true)]\n        [InlineData(true, false), InlineData(true, true)]\n        public void SetRangeInHash_WithIgnoreDupKeyOption_InsertsNonExistingValue(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            try\n            {\n                UseConnections((sql, connection) =>\n                {\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Hash] REBUILD WITH (IGNORE_DUP_KEY = ON)\");\n\n                    connection.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                    {\n                        { \"key\", \"value\" }\n                    });\n\n                    var result = sql\n                        .Query($\"select * from [{Constants.DefaultSchema}].Hash where [Key] = N'some-hash'\")\n                        .ToDictionary(x => (string)x.Field, x => (string)x.Value);\n\n                    Assert.Equal(\"value\", result[\"key\"]);\n                }, useBatching, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnections((sql, _) => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Hash] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useBatching, useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false, false), InlineData(false, false, true)]\n        [InlineData(false,  true, false), InlineData(false,  true, true)]\n        [InlineData( true, false, false), InlineData( true, false, true)]\n        [InlineData( true,  true, false), InlineData( true,  true, true)]\n        public void SetRangeInHash_WithIgnoreDupKeyOption_UpdatesExistingValue_WhenIgnoreDupKeyOptionIsSet(bool setIgnoreDupKey, bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            try\n            {\n                UseConnections((sql, connection) =>\n                {\n                    var onOrOff = setIgnoreDupKey ? \"ON\" : \"OFF\";\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Hash] REBUILD WITH (IGNORE_DUP_KEY = {onOrOff})\");\n                    sql.Execute($@\"insert into [{Constants.DefaultSchema}].Hash([Key], Field, Value) VALUES\n(N'some-hash', N'key1', N'value1'),\n(N'some-hash', N'key2', N'value1'),\n(N'othr-hash', N'key1', N'value1')\");\n\n                    connection.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                    {\n                        { \"key1\", \"value2\" }\n                    });\n\n                    var someResult = sql\n                        .Query($\"select * from [{Constants.DefaultSchema}].Hash where [Key] = N'some-hash'\")\n                        .ToDictionary(x => (string)x.Field, x => (string)x.Value);\n\n                    Assert.Equal(\"value2\", someResult[\"key1\"]);\n                    Assert.Equal(\"value1\", someResult[\"key2\"]);\n\n                    var othrResult = sql\n                        .Query($\"select * from [{Constants.DefaultSchema}].Hash where [Key] = N'othr-hash'\")\n                        .ToDictionary(x => (string)x.Field, x => (string)x.Value);\n\n                    Assert.Equal(\"value1\", othrResult[\"key1\"]);\n                }, useBatching, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnections((sql, _) => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Hash] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useBatching, useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetAllEntriesFromHash_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(\n                connection => Assert.Throws<ArgumentNullException>(() => connection.GetAllEntriesFromHash(null)),\n                useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetAllEntriesFromHash_ReturnsNull_IfHashDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetAllEntriesFromHash(\"some-hash\");\n                Assert.Null(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetAllEntriesFromHash_ReturnsAllKeysAndTheirValues(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Hash ([Key], [Field], [Value])\nvalues (@key, @field, @value)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"some-hash\", field = \"Key1\", value = \"Value1\" },\n                    new { key = \"some-hash\", field = \"Key2\", value = \"Value2\" },\n                    new { key = \"another-hash\", field = \"Key3\", value = \"Value3\" }\n                });\n\n                // Act\n                var result = connection.GetAllEntriesFromHash(\"some-hash\");\n\n                // Assert\n                Assert.NotNull(result);\n                Assert.Equal(2, result.Count);\n                Assert.Equal(\"Value1\", result[\"Key1\"]);\n                Assert.Equal(\"Value2\", result[\"Key2\"]);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetCount_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                Assert.Throws<ArgumentNullException>(\n                    () => connection.GetSetCount(null));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetCount_ReturnsZero_WhenSetDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetSetCount(\"my-set\");\n                Assert.Equal(0, result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetCount_ReturnsNumberOfElements_InASet(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [Score])\nvalues (@key, @value, 0.0)\";\n\n            UseConnections((sql, connection) =>\n            {\n                sql.Execute(arrangeSql, new List<dynamic>\n                {\n                    new { key = \"set-1\", value = \"value-1\" },\n                    new { key = \"set-2\", value = \"value-1\" },\n                    new { key = \"set-1\", value = \"value-2\" }\n                });\n\n                var result = connection.GetSetCount(\"set-1\");\n\n                Assert.Equal(2, result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetCount_Limited_ThrowsAnException_WhenKeysArgumentIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.GetSetCount((IEnumerable<string>) null, 10));\n\n                Assert.Equal(\"keys\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetCount_Limited_ThrowsAnException_WhenLimitIsNegative(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentOutOfRangeException>(\n                    () => connection.GetSetCount(Enumerable.Empty<string>(), -10));\n\n                Assert.Equal(\"limit\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetCount_Limited_ReturnsZero_WhenSetsArgumentIsEmpty(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetSetCount(Enumerable.Empty<string>(), 10);\n                Assert.Equal(0, result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetCount_Limited_ReturnsZero_WhenGivenSetsDoNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetSetCount(new [] { \"set-1\", \"set-2\" }.AsEnumerable(), 10);\n                Assert.Equal(0, result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetCount_Limited_ReturnsTheSum_OfGivenSetCardinalities(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [Score])\nvalues (@Key, @Value, 0.0)\";\n\n            UseConnections((sql, connection) =>\n            {\n                sql.Execute(arrangeSql, new List<dynamic>\n                {\n                    new { Key = \"set-1\", Value = \"1\" },\n                    new { Key = \"set-1\", Value = \"2\" },\n                    new { Key = \"set-2\", Value = \"2\" },\n                    new { Key = \"set-2\", Value = \"3\" },\n                    new { Key = \"set-3\", Value = \"1\" }\n                });\n\n                var result = connection.GetSetCount(new [] { \"set-1\", \"set-2\" }.AsEnumerable(), 10);\n                Assert.Equal(4, result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetCount_Limited_LimitValue_IsConsidered(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [Score])\nvalues (@Key, @Value, 0.0)\";\n\n            UseConnections((sql, connection) =>\n            {\n                sql.Execute(arrangeSql, new List<dynamic>\n                {\n                    new { Key = \"set-1\", Value = \"1\" },\n                    new { Key = \"set-1\", Value = \"2\" },\n                    new { Key = \"set-2\", Value = \"2\" },\n                    new { Key = \"set-2\", Value = \"3\" },\n                    new { Key = \"set-3\", Value = \"1\" }\n                });\n\n                var result = connection.GetSetCount(new [] { \"set-1\", \"set-2\", \"set-4\" }.AsEnumerable(), 2);\n                Assert.Equal(2, result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetRangeFromSet_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                Assert.Throws<ArgumentNullException>(() => connection.GetRangeFromSet(null, 0, 1));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetRangeFromSet_ReturnsPagedElements_SortedByScore(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [Score])\nvalues (@Key, @Value, @Score)\";\n\n            UseConnections((sql, connection) =>\n            {\n                sql.Execute(arrangeSql, new List<dynamic>\n                {\n                    new { Key = \"set-1\", Value = \"4\", Score = 4.0D },\n                    new { Key = \"set-2\", Value = \"4\", Score = 4.0D },\n                    new { Key = \"set-1\", Value = \"6\", Score = 6.0D },\n                    new { Key = \"set-1\", Value = \"2\", Score = 2.0D },\n                    new { Key = \"set-1\", Value = \"3\", Score = 3.0D },\n                    new { Key = \"set-1\", Value = \"5\", Score = 5.0D },\n                    new { Key = \"set-1\", Value = \"1\", Score = 1.0D },\n                });\n\n                var result = connection.GetRangeFromSet(\"set-1\", 2, 4);\n\n                Assert.Equal(new [] { \"3\", \"4\", \"5\" }, result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetCounter_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                Assert.Throws<ArgumentNullException>(\n                    () => connection.GetCounter(null));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetContains_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.GetSetContains(null, \"value\"));\n                \n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetContains_ThrowsAnException_WhenValueIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.GetSetContains(\"key\", null));\n\n                Assert.Equal(\"value\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetContains_ReturnsFalse_WhenGivenSetDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetSetContains(\"non-existing-set\", \"some-value\");\n                Assert.False(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetContains_ReturnsTrue_WhenGivenSetExists_AndContainsTheValue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [Score])\nvalues (@Key, @Value, 0.0)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new List<dynamic>\n                {\n                    new { Key = \"my-set\", Value = \"1\" },\n                    new { Key = \"my-set\", Value = \"2\" },\n                });\n\n                // Act\n                var result = connection.GetSetContains(\"my-set\", \"2\");\n\n                // Assert\n                Assert.True(result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetContains_ReturnsFalse_WhenGivenSetExists_ButContainsOtherValues(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [Score])\nvalues (@Key, @Value, 0.0)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new List<dynamic>\n                {\n                    new { Key = \"my-set\", Value = \"1\" },\n                    new { Key = \"my-set\", Value = \"2\" },\n                });\n\n                // Act\n                var result = connection.GetSetContains(\"my-set\", \"3\");\n\n                // Assert\n                Assert.False(result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n        \n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetContains_ReturnsFalse_WhenAnotherSetContainsTheValue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [Score])\nvalues (@Key, @Value, 0.0)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new List<dynamic>\n                {\n                    new { Key = \"my-set\", Value = \"1\" },\n                    new { Key = \"another-set\", Value = \"2\" },\n                });\n\n                // Act\n                var result = connection.GetSetContains(\"my-set\", \"2\");\n\n                // Assert\n                Assert.False(result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetCounter_ReturnsZero_WhenKeyDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetCounter(\"my-counter\");\n                Assert.Equal(0, result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetCounter_ReturnsSumOfValues_InCounterTable(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Counter ([Key], [Value])\nvalues (@key, @value)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"counter-1\", value = 1 },\n                    new { key = \"counter-2\", value = 1 },\n                    new { key = \"counter-1\", value = 1 }\n                });\n\n                // Act\n                var result = connection.GetCounter(\"counter-1\");\n\n                // Assert\n                Assert.Equal(2, result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetCounter_IncludesValues_FromCounterAggregateTable(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].AggregatedCounter ([Key], [Value])\nvalues (@key, @value)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"counter-1\", value = 12 },\n                    new { key = \"counter-2\", value = 15 }\n                });\n\n                // Act\n                var result = connection.GetCounter(\"counter-1\");\n\n                Assert.Equal(12, result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetHashCount_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                Assert.Throws<ArgumentNullException>(() => connection.GetHashCount(null));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetHashCount_ReturnsZero_WhenKeyDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetHashCount(\"my-hash\");\n                Assert.Equal(0, result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetHashCount_ReturnsNumber_OfHashFields(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Hash ([Key], [Field])\nvalues (@key, @field)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"hash-1\", field = \"field-1\" },\n                    new { key = \"hash-1\", field = \"field-2\" },\n                    new { key = \"hash-2\", field = \"field-1\" }\n                });\n\n                // Act\n                var result = connection.GetHashCount(\"hash-1\");\n\n                // Assert\n                Assert.Equal(2, result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetHashTtl_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                Assert.Throws<ArgumentNullException>(\n                    () => connection.GetHashTtl(null));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetHashTtl_ReturnsNegativeValue_WhenHashDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetHashTtl(\"my-hash\");\n                Assert.True(result < TimeSpan.Zero);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetHashTtl_ReturnsExpirationTimeForHash(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Hash ([Key], [Field], [ExpireAt])\nvalues (@key, @field, @expireAt)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"hash-1\", field = \"field\", expireAt = (DateTime?)DateTime.UtcNow.AddHours(1) },\n                    new { key = \"hash-2\", field = \"field\", expireAt = (DateTime?) null }\n                });\n\n                // Act\n                var result = connection.GetHashTtl(\"hash-1\");\n\n                // Assert\n                Assert.True(TimeSpan.FromMinutes(59) < result);\n                Assert.True(result < TimeSpan.FromMinutes(61));\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetListCount_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                Assert.Throws<ArgumentNullException>(\n                    () => connection.GetListCount(null));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetListCount_ReturnsZero_WhenListDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetListCount(\"my-list\");\n                Assert.Equal(0, result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetListCount_ReturnsTheNumberOfListElements(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].List ([Key])\nvalues (@key)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"list-1\" },\n                    new { key = \"list-1\" },\n                    new { key = \"list-2\" }\n                });\n\n                // Act\n                var result = connection.GetListCount(\"list-1\");\n\n                // Assert\n                Assert.Equal(2, result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetListTtl_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                Assert.Throws<ArgumentNullException>(\n                    () => connection.GetListTtl(null));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetListTtl_ReturnsNegativeValue_WhenListDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetListTtl(\"my-list\");\n                Assert.True(result < TimeSpan.Zero);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetListTtl_ReturnsExpirationTimeForList(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].List ([Key], [ExpireAt])\nvalues (@key, @expireAt)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"list-1\", expireAt = (DateTime?) DateTime.UtcNow.AddHours(1) },\n                    new { key = \"list-2\", expireAt = (DateTime?) null }\n                });\n\n                // Act\n                var result = connection.GetListTtl(\"list-1\");\n\n                // Assert\n                Assert.True(TimeSpan.FromMinutes(59) < result);\n                Assert.True(result < TimeSpan.FromMinutes(61));\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetValueFromHash_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.GetValueFromHash(null, \"name\"));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetValueFromHash_ThrowsAnException_WhenNameIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.GetValueFromHash(\"key\", null));\n\n                Assert.Equal(\"name\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetValueFromHash_ReturnsNull_WhenHashDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetValueFromHash(\"my-hash\", \"name\");\n                Assert.Null(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetValueFromHash_ReturnsValue_OfAGivenField(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Hash ([Key], [Field], [Value])\nvalues (@key, @field, @value)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"hash-1\", field = \"field-1\", value = \"1\" },\n                    new { key = \"hash-1\", field = \"field-2\", value = \"2\" },\n                    new { key = \"hash-2\", field = \"field-1\", value = \"3\" }\n                });\n\n                // Act\n                var result = connection.GetValueFromHash(\"hash-1\", \"field-1\");\n\n                // Assert\n                Assert.Equal(\"1\", result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetRangeFromList_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => connection.GetRangeFromList(null, 0, 1));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetRangeFromList_ReturnsAnEmptyList_WhenListDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetRangeFromList(\"my-list\", 0, 1);\n                Assert.Empty(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetRangeFromList_ReturnsAllEntries_WithinGivenBounds(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].List ([Key], [Value])\nvalues (@key, @value)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"list-1\", value = \"1\" },\n                    new { key = \"list-2\", value = \"2\" },\n                    new { key = \"list-1\", value = \"3\" },\n                    new { key = \"list-1\", value = \"4\" },\n                    new { key = \"list-1\", value = \"5\" }\n                });\n\n                // Act\n                var result = connection.GetRangeFromList(\"list-1\", 1, 2);\n                \n                // Assert\n                Assert.Equal(new [] { \"4\", \"3\" }, result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetAllItemsFromList_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                Assert.Throws<ArgumentNullException>(\n                    () => connection.GetAllItemsFromList(null));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetAllItemsFromList_ReturnsAnEmptyList_WhenListDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetAllItemsFromList(\"my-list\");\n                Assert.Empty(result);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetAllItemsFromList_ReturnsAllItems_FromAGivenList(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].List ([Key], Value)\nvalues (@key, @value)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"list-1\", value = \"1\" },\n                    new { key = \"list-2\", value = \"2\" },\n                    new { key = \"list-1\", value = \"3\" }\n                });\n\n                // Act\n                var result = connection.GetAllItemsFromList(\"list-1\");\n\n                // Assert\n                Assert.Equal(new [] { \"3\", \"1\" }, result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetTtl_ThrowsAnException_WhenKeyIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                Assert.Throws<ArgumentNullException>(() => connection.GetSetTtl(null));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetTtl_ReturnsNegativeValue_WhenSetDoesNotExist(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var result = connection.GetSetTtl(\"my-set\");\n                Assert.True(result < TimeSpan.Zero);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetSetTtl_ReturnsExpirationTime_OfAGivenSet(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [ExpireAt], [Score])\nvalues (@key, @value, @expireAt, 0.0)\";\n\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"set-1\", value = \"1\", expireAt = (DateTime?) DateTime.UtcNow.AddMinutes(60) },\n                    new { key = \"set-2\", value = \"2\", expireAt = (DateTime?) null }\n                });\n\n                // Act\n                var result = connection.GetSetTtl(\"set-1\");\n\n                // Assert\n                Assert.True(TimeSpan.FromMinutes(59) < result);\n                Assert.True(result < TimeSpan.FromMinutes(61));\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetJobData_ReturnsResult_WhenJobIdIsLongValue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].Job ON;\ninsert into [{Constants.DefaultSchema}].Job (Id, InvocationData, Arguments, StateName, CreatedAt)\nvalues (@jobId, @invocationData, '[''Arguments'']', 'Succeeded', getutcdate());\";\n\n            UseConnections((sql, connection) =>\n            {\n                var job = Job.FromExpression(() => SampleMethod(\"hello\"));\n\n                sql.Query(\n                    arrangeSql,\n                    new\n                    {\n                        jobId = int.MaxValue + 1L,\n                        invocationData = JobHelper.ToJson(InvocationData.Serialize(job)),\n                    });\n\n                var result = connection.GetJobData((int.MaxValue + 1L).ToString());\n\n                Assert.NotNull(result);\n                Assert.NotNull(result.Job);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetStateData_ReturnsCorrectData_WhenJobIdAndStateIdAreLongValues(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].Job ON\ninsert into [{Constants.DefaultSchema}].Job (Id, InvocationData, Arguments, StateName, CreatedAt)\nvalues (@jobId, '', '', '', getutcdate());\ninsert into [{Constants.DefaultSchema}].State (JobId, Name, CreatedAt)\nvalues (@jobId, 'old-state', getutcdate());\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].Job OFF\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].State ON\ninsert into [{Constants.DefaultSchema}].State (Id, JobId, Name, Data, CreatedAt)\nvalues (@stateId, @jobId, 'Name', @data, getutcdate());\nupdate [{Constants.DefaultSchema}].Job set StateId = @stateId;\";\n\n            UseConnections((sql, connection) =>\n            {\n                var data = new Dictionary<string, string>\n                {\n                    { \"Key\", \"Value\" }\n                };\n\n                sql.Query(\n                    arrangeSql,\n                    new\n                    {\n                        jobId = int.MaxValue + 1L,\n                        stateId = int.MaxValue + 1L,\n                        data = JobHelper.ToJson(data)\n                    });\n\n                var result = connection.GetStateData((int.MaxValue + 1L).ToString());\n\n                Assert.NotNull(result);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void CreateExpiredJob_HandlesJobIdCanExceedInt32Max(bool useMicrosoftDataSqlClient)\n        {\n            UseConnections((sql, connection) =>\n            {\n                // Arrange\n                sql.Query($\"DBCC CHECKIDENT('[{Constants.DefaultSchema}].Job', RESEED, {int.MaxValue + 1L});\");\n\n                // Act\n                var createdAt = new DateTime(2012, 12, 12);\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => SampleMethod(\"Hello\")),\n                    new Dictionary<string, string>(),\n                    createdAt,\n                    TimeSpan.FromDays(1));\n\n                // Assert\n                Assert.True(int.MaxValue < long.Parse(jobId));\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void SetJobParameter_CreatesNewParameter_WhenJobIdIsLongValue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].Job ON\ninsert into [{Constants.DefaultSchema}].Job (Id, InvocationData, Arguments, CreatedAt)\nvalues (@jobId, '', '', getutcdate())\";\n\n            UseConnections((sql, connection) =>\n            {\n                sql.Query(\n                    arrangeSql,\n                    new { jobId =  int.MaxValue + 1L});\n\n                connection.SetJobParameter((int.MaxValue + 1L).ToString(), \"Name\", \"Value\");\n\n                var parameter = sql.Query(\n                    $\"select * from [{Constants.DefaultSchema}].JobParameter where JobId = @id and Name = @name\",\n                    new { id = int.MaxValue + 1L, name = \"Name\" }).Single();\n\n                Assert.NotNull(parameter);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetJobParameter_ReturnsParameterValue_WhenJobIdIsLong(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].Job ON\ninsert into [{Constants.DefaultSchema}].Job (Id, InvocationData, Arguments, CreatedAt)\nvalues (@jobId, '', '', getutcdate())\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].Job OFF\ninsert into [{Constants.DefaultSchema}].JobParameter (JobId, Name, Value)\nvalues (@jobId, @name, @value)\";\n\n            UseConnections((sql, connection) =>\n            {\n                sql.Query(\n                    arrangeSql, \n                    new\n                    {\n                        jobId = int.MaxValue + 1L,\n                        name = \"name\", value = \"value\"\n                    });\n\n                var value = connection.GetJobParameter((int.MaxValue + 1L).ToString(), \"name\");\n\n                Assert.Equal(\"value\", value);\n            }, useBatching: false, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void GetUtcDateTime_ReturnsCurrentUtcDateTime(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var dateTime = connection.GetUtcDateTime();\n                var currentDateTime = DateTime.UtcNow;\n\n                Assert.Equal(DateTimeKind.Utc, dateTime.Kind);\n                Assert.True(currentDateTime.AddMinutes(-1) < dateTime, dateTime.ToString(CultureInfo.CurrentCulture));\n                Assert.True(dateTime < currentDateTime.AddMinutes(1), dateTime.ToString(CultureInfo.CurrentCulture));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Fact, CleanSerializerSettings]\n        public void HandlesChangingProcessOfStateDataSerialization()\n        {\n            GlobalConfiguration.Configuration.UseSerializerSettings(SerializerSettingsHelper.DangerousSettings);\n            var stateData = new Dictionary<string, string>\n            {\n                { \"key1\", \"value1\" },\n                { \"key2\", null }\n            };\n            var serializedData = SerializationHelper.Serialize(stateData, SerializationOption.User);\n\n            var deserializedStateData = SerializationHelper.Deserialize<Dictionary<string, string>>(serializedData);\n\n            Assert.NotNull(deserializedStateData);\n            Assert.Equal(2, deserializedStateData.Count);\n\n            Assert.Equal(\"value1\", deserializedStateData[\"key1\"]);\n            Assert.Null(deserializedStateData[\"key2\"]);\n        }\n\n        [Fact, CleanSerializerSettings]\n        public void HandlesChangingProcessOfInvocationDataSerialization()\n        {\n            GlobalConfiguration.Configuration.UseSerializerSettings(SerializerSettingsHelper.DangerousSettings);\n\n            var initialJob = Job.FromExpression(() => Console.WriteLine());\n            var invocationData = InvocationData.Serialize(initialJob);\n\n            var serializedInvocationData = SerializationHelper.Serialize(invocationData, SerializationOption.User);\n\n            var deserializedStateData = SerializationHelper.Deserialize<InvocationData>(serializedInvocationData);\n            var deserializedJob = deserializedStateData.Deserialize();\n\n            Assert.Equal(initialJob.Args, deserializedJob.Args);\n            Assert.Equal(initialJob.Method, deserializedJob.Method);\n            Assert.Equal(initialJob.Type, deserializedJob.Type);\n        }\n\n        private void UseConnections(Action<DbConnection, SqlServerConnection> action, bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            using (var sqlConnection = ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient))\n            {\n                var storage = new SqlServerStorage(\n                    () => ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient),\n                    new SqlServerStorageOptions { CommandBatchMaxTimeout = useBatching ? TimeSpan.FromMinutes(1) : (TimeSpan?)null });\n\n                using (var connection = new SqlServerConnection(storage))\n                {\n                    action(sqlConnection, connection);\n                }\n            }\n        }\n\n        private void UseConnection(Action<SqlServerConnection> action, bool useMicrosoftDataSqlClient)\n        {\n            var storage = new Mock<SqlServerStorage>((Func<DbConnection>)(() => ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient)));\n            storage.Setup(x => x.QueueProviders).Returns(_providers);\n\n            using (var connection = new SqlServerConnection(storage.Object))\n            {\n                action(connection);\n            }\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void SampleMethod(string arg){ }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/SqlServerDistributedLockFacts.cs",
    "content": "﻿extern alias ReferencedDapper;\n\nusing System;\nusing System.Data.Common;\nusing System.Linq;\nusing System.Reflection;\nusing System.Threading;\nusing ReferencedDapper::Dapper;\nusing Hangfire.Storage;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class SqlServerDistributedLockFacts\n    {\n        private readonly TimeSpan _timeout = TimeSpan.FromSeconds(5);\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerDistributedLock(null, \"hello\", _timeout));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Ctor_ThrowsAnException_WhenResourceIsNullOrEmpty(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var storage = CreateStorage(connection);\n\n                var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerDistributedLock(storage, \"\", _timeout));\n\n                Assert.Equal(\"resource\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Ctor_AcquiresExclusiveApplicationLock_OnSession(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(sql =>\n            {\n                // ReSharper disable once UnusedVariable\n                var storage = CreateStorage(sql);\n                using (new SqlServerDistributedLock(storage, \"hello\", _timeout))\n                {\n                    var lockMode = sql.Query<string>(\n                        \"select applock_mode('public', 'hello', 'session')\").Single();\n\n                    Assert.Equal(\"Exclusive\", lockMode);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Ctor_ThrowsAnException_IfLockCanNotBeGranted(bool useMicrosoftDataSqlClient)\n        {\n            var releaseLock = new ManualResetEventSlim(false);\n            var lockAcquired = new ManualResetEventSlim(false);\n\n            var thread = new Thread(\n                () => UseConnection(connection1 =>\n                {\n                    var storage = CreateStorage(connection1);\n                    using (new SqlServerDistributedLock(storage, \"exclusive\", TimeSpan.Zero))\n                    {\n                        lockAcquired.Set();\n                        releaseLock.Wait();\n                    }\n                }, useMicrosoftDataSqlClient));\n            thread.Start();\n\n            lockAcquired.Wait();\n\n            UseConnection(connection2 =>\n            {\n                var storage = CreateStorage(connection2);\n                Assert.Throws<DistributedLockTimeoutException>(\n                    () =>\n                    {\n                        using (new SqlServerDistributedLock(storage, \"exclusive\", TimeSpan.FromSeconds(1)))\n                        {\n                        }\n                    });\n            }, useMicrosoftDataSqlClient);\n\n            releaseLock.Set();\n            thread.Join();\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dispose_ReleasesExclusiveApplicationLock(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(sql =>\n            {\n                var storage = CreateStorage(sql);\n                var distributedLock = new SqlServerDistributedLock(storage, \"hello\", _timeout);\n                distributedLock.Dispose();\n\n                var lockMode = sql.Query<string>(\n                    \"select applock_mode('public', 'hello', 'session')\").Single();\n\n                Assert.Equal(\"NoLock\", lockMode);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Fact, CleanDatabase]\n        public void DistributedLocks_AreReEntrant_FromTheSameThread_OnTheSameResource()\n        {\n            var storage = new SqlServerStorage(ConnectionUtils.GetConnectionString());\n            \n            using (new SqlServerDistributedLock(storage, \"hello\", TimeSpan.FromMinutes(5)))\n            using (new SqlServerDistributedLock(storage, \"hello\", TimeSpan.FromMinutes(5)))\n            {\n                Assert.True(true);\n            }\n        }\n\n        [Fact, CleanDatabase]\n        public void InnerDistributedLock_DoesNotConsumeADatabaseConnection()\n        {\n            // Arrange\n            var storage = new SqlServerStorage(ConnectionUtils.GetConnectionString());\n\n            // Act\n            using (var outer = new SqlServerDistributedLock(storage, \"hello\", TimeSpan.FromMinutes(5)))\n            using (var inner = new SqlServerDistributedLock(storage, \"hello\", TimeSpan.FromMinutes(5)))\n            {\n                // Assert\n                var field = typeof(SqlServerDistributedLock).GetField(\"_connection\",\n                    BindingFlags.Instance | BindingFlags.NonPublic);\n                Assert.NotNull(field);\n\n                Assert.NotNull(field.GetValue(outer));\n                Assert.Null(field.GetValue(inner));\n            }\n        }\n\n        private static SqlServerStorage CreateStorage(DbConnection connection)\n        {\n            return new SqlServerStorage(connection);\n        }\n\n        private static void UseConnection(Action<DbConnection> action, bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient))\n            {\n                action(connection);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/SqlServerJobQueueFacts.cs",
    "content": "﻿extern alias ReferencedDapper;\n\nusing System;\nusing System.Data.Common;\nusing System.Linq;\nusing System.Threading;\nusing Hangfire.Annotations;\nusing ReferencedDapper::Dapper;\nusing Xunit;\n// ReSharper disable ArgumentsStyleLiteral\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class SqlServerJobQueueFacts\n    {\n        private static readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(5);\n        private static readonly string[] DefaultQueues = { \"default\" };\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerJobQueue(null, new SqlServerStorageOptions()));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenOptionsValueIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerJobQueue(new SqlServerStorage(ConnectionUtils.GetConnectionString()), null));\n\n            Assert.Equal(\"options\", exception.ParamName);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_ShouldThrowAnException_WhenQueuesCollectionIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => queue.Dequeue(null, CreateTimingOutCancellationToken()));\n\n                Assert.Equal(\"queues\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_ShouldThrowAnException_WhenQueuesCollectionIsEmpty(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                var exception = Assert.Throws<ArgumentException>(\n                    () => queue.Dequeue(new string[0], CreateTimingOutCancellationToken()));\n\n                Assert.Equal(\"queues\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_ThrowsOperationCanceled_WhenCancellationTokenIsSetAtTheBeginning(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var cts = new CancellationTokenSource();\n                cts.Cancel();\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                Assert.Throws<OperationCanceledException>(\n                    () => queue.Dequeue(DefaultQueues, cts.Token));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_ShouldWaitIndefinitely_WhenThereAreNoJobs(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var cts = new CancellationTokenSource(200);\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                Assert.Throws<OperationCanceledException>(\n                    () => queue.Dequeue(DefaultQueues, cts.Token));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_ShouldFetchAJob_FromTheSpecifiedQueue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (@jobId, @queue);\nselect scope_identity() as Id;\";\n\n            // Arrange\n            UseConnection(connection =>\n            {\n                // ReSharper disable once UnusedVariable\n                var id = (int)connection.Query(\n                    arrangeSql,\n                    new { jobId = 1, queue = \"default\" }).Single().Id;\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                // Act\n                using (var payload = (SqlServerTransactionJob) queue.Dequeue(\n                    DefaultQueues,\n                    CreateTimingOutCancellationToken()))\n                {\n                    // Assert\n                    Assert.Equal(\"1\", payload.JobId);\n                    Assert.Equal(\"default\", payload.Queue);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_FetchesAJob_WhenJobIdIsLongValue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (@jobId, @queue);\nselect scope_identity() as Id;\";\n\n            // Arrange\n            UseConnection(connection =>\n            {\n                // ReSharper disable once UnusedVariable\n                var id = (int)connection.Query(\n                    arrangeSql,\n                    new { jobId = int.MaxValue + 1L, queue = \"default\" }).Single().Id;\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                // Act\n                using (var payload = (SqlServerTransactionJob) queue.Dequeue(\n                    DefaultQueues,\n                    CreateTimingOutCancellationToken()))\n                {\n                    // Assert\n                    Assert.Equal((int.MaxValue + 1L).ToString(), payload.JobId);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_ShouldDeleteAJob(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues (@invocationData, @arguments, getutcdate())\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (scope_identity(), @queue)\";\n\n            // Arrange\n            UseConnection(connection =>\n            {\n                connection.Execute(\n                    arrangeSql,\n                    new { invocationData = \"\", arguments = \"\", queue = \"default\" });\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                // Act\n                using (var payload = queue.Dequeue(\n                    DefaultQueues,\n                    CreateTimingOutCancellationToken()))\n                {\n                    // Assert\n                    Assert.NotNull(payload);\n\n                    UseConnection(connection2 =>\n                    {\n                        var jobInQueue = connection2.Query($\"select * from [{Constants.DefaultSchema}].JobQueue with (readpast)\").SingleOrDefault();\n                        Assert.Null(jobInQueue);\n                    }, useMicrosoftDataSqlClient);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_ShouldFetchTimedOutJobs_FromTheSpecifiedQueue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues (@invocationData, @arguments, dateadd(minute, -60, getutcdate()))\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue, FetchedAt)\nvalues (scope_identity(), @queue, @fetchedAt)\";\n\n            // Arrange\n            UseConnection(connection =>\n            {\n                connection.Execute(\n                    arrangeSql,\n                    new\n                    {\n                        queue = \"default\",\n                        fetchedAt = DateTime.UtcNow.AddDays(-1),\n                        invocationData = \"\",\n                        arguments = \"\"\n                    });\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                // Act\n                using (var payload = queue.Dequeue(\n                    DefaultQueues,\n                    CreateTimingOutCancellationToken()))\n                {\n                    // Assert\n                    Assert.NotEmpty(payload.JobId);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_ShouldSetFetchedAt_OnlyForTheFetchedJob(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues (@invocationData, @arguments, getutcdate())\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (scope_identity(), @queue)\";\n\n            // Arrange\n            UseConnection(connection =>\n            {\n                connection.Execute(\n                    arrangeSql,\n                    new[]\n                    {\n                        new { queue = \"default\", invocationData = \"\", arguments = \"\" },\n                        new { queue = \"default\", invocationData = \"\", arguments = \"\" }\n                    });\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                // Act\n                using (var payload = queue.Dequeue(\n                    DefaultQueues,\n                    CreateTimingOutCancellationToken()))\n                {\n                    // Assert\n                    UseConnection(connection2 =>\n                    {\n                        var otherJobFetchedAt = connection2.Query<DateTime?>(\n                            $\"select FetchedAt from [{Constants.DefaultSchema}].JobQueue with (readpast) where JobId != @id\",\n                            new { id = payload.JobId }).Single();\n\n                        Assert.Null(otherJobFetchedAt);\n                    }, useMicrosoftDataSqlClient);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_ShouldFetchJobs_OnlyFromSpecifiedQueues(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues (@invocationData, @arguments, getutcdate())\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (scope_identity(), @queue)\";\n\n            UseConnection(connection =>\n            {\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                connection.Execute(\n                    arrangeSql,\n                    new { queue = \"critical\", invocationData = \"\", arguments = \"\" });\n                \n                Assert.Throws<OperationCanceledException>(\n                    () => queue.Dequeue(\n                        DefaultQueues,\n                        CreateTimingOutCancellationToken()));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_ShouldFetchJobs_FromMultipleQueues(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues (@invocationData, @arguments, getutcdate())\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (scope_identity(), @queue)\";\n\n            UseConnection(connection =>\n            {\n                connection.Execute(\n                    arrangeSql,\n                    new[]\n                    {\n                        new { queue = \"default\", invocationData = \"\", arguments = \"\" },\n                        new { queue = \"critical\", invocationData = \"\", arguments = \"\" }\n                    });\n\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                using (var critical = (SqlServerTransactionJob) queue.Dequeue(\n                    new[] { \"critical\", \"default\" },\n                    CreateTimingOutCancellationToken()))\n                {\n                    Assert.NotNull(critical.JobId);\n                    Assert.Equal(\"critical\", critical.Queue);\n                    critical.RemoveFromQueue();\n                }\n\n                using (var @default = (SqlServerTransactionJob) queue.Dequeue(\n                    new[] { \"critical\", \"default\" },\n                    CreateTimingOutCancellationToken()))\n                {\n                    Assert.NotNull(@default.JobId);\n                    Assert.Equal(\"default\", @default.Queue);\n                    @default.RemoveFromQueue();\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        //---\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ShouldThrowAnException_WhenQueuesCollectionIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => queue.Dequeue(null, CreateTimingOutCancellationToken()));\n\n                Assert.Equal(\"queues\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ShouldThrowAnException_WhenQueuesCollectionIsEmpty(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                var exception = Assert.Throws<ArgumentException>(\n                    () => queue.Dequeue(new string[0], CreateTimingOutCancellationToken()));\n\n                Assert.Equal(\"queues\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ThrowsOperationCanceled_WhenCancellationTokenIsSetAtTheBeginning(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var cts = new CancellationTokenSource();\n                cts.Cancel();\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                Assert.Throws<OperationCanceledException>(\n                    () => queue.Dequeue(DefaultQueues, cts.Token));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ShouldWaitIndefinitely_WhenThereAreNoJobs(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var cts = new CancellationTokenSource(200);\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                Assert.Throws<OperationCanceledException>(\n                    () => queue.Dequeue(DefaultQueues, cts.Token));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ShouldFetchAJob_FromTheSpecifiedQueue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (@jobId, @queue);\nselect scope_identity() as Id;\";\n\n            // Arrange\n            UseConnection(connection =>\n            {\n                var id = (int)connection.Query(\n                    arrangeSql,\n                    new { jobId = 1, queue = \"default\" }).Single().Id;\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                // Act\n                var payload = (SqlServerTimeoutJob)queue.Dequeue(\n                    DefaultQueues,\n                    CreateTimingOutCancellationToken());\n\n                // Assert\n                Assert.Equal(id, payload.Id);\n                Assert.Equal(\"1\", payload.JobId);\n                Assert.Equal(\"default\", payload.Queue);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ShouldFetchTheFirstJob_FromTheSpecifiedQueue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\noutput inserted.Id\nvalues (@jobId1, @queue), (@jobId2, @queue);\";\n\n            // Arrange\n            UseConnection(connection =>\n            {\n                var id = (int)connection.Query(\n                    arrangeSql,\n                    new { jobId1 = 1, jobId2 = 2, queue = \"default\" }).First().Id;\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                // Act\n                var payload = (SqlServerTimeoutJob)queue.Dequeue(\n                    DefaultQueues,\n                    CreateTimingOutCancellationToken());\n\n                // Assert\n                Assert.Equal(id, payload.Id);\n                Assert.Equal(\"1\", payload.JobId);\n                Assert.Equal(\"default\", payload.Queue);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ShouldLeaveJobInTheQueue_ButSetItsFetchedAtValue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues (@invocationData, @arguments, getutcdate())\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (scope_identity(), @queue)\";\n\n            // Arrange\n            UseConnection(connection =>\n            {\n                connection.Execute(\n                    arrangeSql,\n                    new { invocationData = \"\", arguments = \"\", queue = \"default\" });\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                // Act\n                var payload = queue.Dequeue(\n                    DefaultQueues,\n                    CreateTimingOutCancellationToken());\n\n                // Assert\n                Assert.NotNull(payload);\n\n                var fetchedAt = connection.Query<DateTime?>(\n                    $\"select FetchedAt from [{Constants.DefaultSchema}].JobQueue where JobId = @id\",\n                    new { id = payload.JobId }).Single();\n\n                Assert.NotNull(fetchedAt);\n                Assert.True(fetchedAt > DateTime.UtcNow.AddMinutes(-1));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues (@invocationData, @arguments, getutcdate())\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue, FetchedAt)\nvalues (scope_identity(), @queue, @fetchedAt)\";\n\n            // Arrange\n            UseConnection(connection =>\n            {\n                connection.Execute(\n                    arrangeSql,\n                    new\n                    {\n                        queue = \"default\",\n                        fetchedAt = DateTime.UtcNow.AddDays(-1),\n                        invocationData = \"\",\n                        arguments = \"\"\n                    });\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                // Act\n                var payload = queue.Dequeue(\n                    DefaultQueues,\n                    CreateTimingOutCancellationToken());\n\n                // Assert\n                Assert.NotEmpty(payload.JobId);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ShouldSetFetchedAt_OnlyForTheFetchedJob(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues (@invocationData, @arguments, getutcdate())\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (scope_identity(), @queue)\";\n\n            // Arrange\n            UseConnection(connection =>\n            {\n                connection.Execute(\n                    arrangeSql,\n                    new[]\n                    {\n                        new { queue = \"default\", invocationData = \"\", arguments = \"\" },\n                        new { queue = \"default\", invocationData = \"\", arguments = \"\" }\n                    });\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                // Act\n                var payload = queue.Dequeue(\n                    DefaultQueues,\n                    CreateTimingOutCancellationToken());\n\n                // Assert\n                var otherJobFetchedAt = connection.Query<DateTime?>(\n                    $\"select FetchedAt from [{Constants.DefaultSchema}].JobQueue where JobId != @id\",\n                    new { id = payload.JobId }).Single();\n\n                Assert.Null(otherJobFetchedAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ShouldFetchJobs_OnlyFromSpecifiedQueues(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues (@invocationData, @arguments, getutcdate())\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (scope_identity(), @queue)\";\n\n            UseConnection(connection =>\n            {\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                connection.Execute(\n                    arrangeSql,\n                    new { queue = \"critical\", invocationData = \"\", arguments = \"\" });\n\n                Assert.Throws<OperationCanceledException>(\n                    () => queue.Dequeue(\n                        DefaultQueues,\n                        CreateTimingOutCancellationToken()));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dequeue_InvisibilityTimeout_ShouldFetchJobs_FromMultipleQueues(bool useMicrosoftDataSqlClient)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues (@invocationData, @arguments, getutcdate())\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue)\nvalues (scope_identity(), @queue)\";\n\n            UseConnection(connection =>\n            {\n                connection.Execute(\n                    arrangeSql,\n                    new[]\n                    {\n                        new { queue = \"default\", invocationData = \"\", arguments = \"\" },\n                        new { queue = \"critical\", invocationData = \"\", arguments = \"\" }\n                    });\n\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: DefaultTimeout);\n\n                var critical = (SqlServerTimeoutJob)queue.Dequeue(\n                    new[] { \"critical\", \"default\" },\n                    CreateTimingOutCancellationToken());\n\n                Assert.NotNull(critical.JobId);\n                Assert.Equal(\"critical\", critical.Queue);\n\n                var @default = (SqlServerTimeoutJob)queue.Dequeue(\n                    new[] { \"critical\", \"default\" },\n                    CreateTimingOutCancellationToken());\n\n                Assert.NotNull(@default.JobId);\n                Assert.Equal(\"default\", @default.Queue);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Enqueue_AddsAJobToTheQueue(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n#if NETCOREAPP\n                using (var transaction = connection.BeginTransaction())\n                {\n                    queue.Enqueue(connection, transaction, \"default\", \"1\");\n                    transaction.Commit();\n                }\n#else\n                queue.Enqueue(connection, \"default\", \"1\");\n#endif\n\n                var record = connection.Query($\"select * from [{Constants.DefaultSchema}].JobQueue\").Single();\n                Assert.Equal(\"1\", record.JobId.ToString());\n                Assert.Equal(\"default\", record.Queue);\n                Assert.Null(record.FetchedAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Enqueue_ThrowsAnException_WhenTheGivenQueueIsTooLong(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var queueName = \"some-really-long-queue-name-that-should-cause-an-exception-to-be-thrown-and-not-ignored\";\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n\n                var exception = Assert.ThrowsAny<DbException>(() =>\n                {\n#if NETCOREAPP\n                    using (var transaction = connection.BeginTransaction())\n                    {\n                        queue.Enqueue(connection, transaction, queueName, \"1\");\n                        transaction.Commit();\n                    }\n#else\n                    queue.Enqueue(connection, queueName, \"1\");\n#endif\n                });\n\n                var record = connection.Query($\"select * from [{Constants.DefaultSchema}].JobQueue\").SingleOrDefault();\n                Assert.Null(record);\n                Assert.StartsWith(\"String or binary data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Enqueue_AddsAJob_WhenIdIsLongValue(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection(connection =>\n            {\n                var queue = CreateJobQueue(useMicrosoftDataSqlClient, invisibilityTimeout: null);\n                \n#if NETCOREAPP\n                using (var transaction = connection.BeginTransaction())\n                {\n                    queue.Enqueue(connection, transaction, \"default\", (int.MaxValue + 1L).ToString());\n                    transaction.Commit();\n                }\n#else\n                queue.Enqueue(connection, \"default\", (int.MaxValue + 1L).ToString());\n#endif\n\n                var record = connection.Query($\"select * from [{Constants.DefaultSchema}].JobQueue\").Single();\n                Assert.Equal((int.MaxValue + 1L).ToString(), record.JobId.ToString());\n            }, useMicrosoftDataSqlClient);\n        }\n\n        private static CancellationToken CreateTimingOutCancellationToken()\n        {\n            var source = new CancellationTokenSource(TimeSpan.FromSeconds(1));\n            return source.Token;\n        }\n\n        private static SqlServerJobQueue CreateJobQueue(bool useMicrosoftDataSqlClient, TimeSpan? invisibilityTimeout)\n        {\n            var storage = new SqlServerStorage(() => ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient));\n            return new SqlServerJobQueue(storage, new SqlServerStorageOptions { SlidingInvisibilityTimeout = invisibilityTimeout });\n        }\n\n        private static void UseConnection([InstantHandle] Action<DbConnection> action, bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient))\n            {\n                action(connection);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/SqlServerMonitoringApiFacts.cs",
    "content": "extern alias ReferencedDapper;\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data.Common;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Reflection;\nusing ReferencedDapper::Dapper;\nusing Hangfire.Common;\nusing Hangfire.Server;\nusing Hangfire.SqlServer.Entities;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable UnusedVariable\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class SqlServerMonitoringApiFacts\n    {\n        private readonly DateTime _utcNow = DateTime.UtcNow;\n\n        [Fact, CleanSerializerSettings]\n        public void HandlesChangingProcessOfServerDataSerialization()\n        {\n            GlobalConfiguration.Configuration.UseSerializerSettings(SerializerSettingsHelper.DangerousSettings);\n\n            var serverData = new ServerData\n            {\n                WorkerCount = 5,\n                Queues = new[] { \"default\", \"critical\" },\n                StartedAt = new DateTime(2016, 12, 01, 14, 33, 00)\n            };\n            var serializedServerData = SerializationHelper.Serialize(serverData, SerializationOption.User);\n\n            var deserializedServerData = SerializationHelper.Deserialize<ServerData>(serializedServerData);\n\n            Assert.Equal(5, deserializedServerData.WorkerCount);\n            Assert.Equal(new[] { \"default\", \"critical\" }, deserializedServerData.Queues);\n            Assert.Equal(new DateTime(2016, 12, 01, 14, 33, 00), deserializedServerData.StartedAt);\n        }\n\n        [Fact, CleanDatabase]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerMonitoringApi(null, null));\n            \n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact, CleanDatabase]\n        public void Queues_ReturnsEmptyCollection_WhenThereAreNoQueues()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.Queues();\n\n            Assert.NotNull(result);\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void Queues_ReturnsCorrectJobs_WhichWillBeDequeuedNext()\n        {\n            // Arrange\n            var jobId = SimpleEnqueueJob(\n                \"critical\",\n                job: Job.FromExpression(() => Empty()),\n                state: new EnqueuedState());\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.Queues();\n\n            // Assert\n            var criticalQueue = result.Single();\n            var queuedJob = criticalQueue.FirstJobs.Single();\n\n            Assert.Equal(\"critical\", criticalQueue.Name);\n            Assert.Equal(1, criticalQueue.Length);\n            Assert.Equal(jobId, queuedJob.Key);\n\n            Assert.True(queuedJob.Value.InEnqueuedState);\n            Assert.Equal(\"Enqueued\", queuedJob.Value.State, StringComparer.OrdinalIgnoreCase);\n            AssertWithinSecond(_utcNow, queuedJob.Value.EnqueuedAt);\n\n            Assert.Equal(typeof(SqlServerMonitoringApiFacts), queuedJob.Value.Job.Type);\n            Assert.Equal(\"Empty\", queuedJob.Value.Job.Method.Name);\n        }\n\n        [Fact, CleanDatabase]\n        public void Queues_IsAbleToHandle_JobIdWithoutCorrespondingBackgroundJobEntry()\n        {\n            // Arrange\n            SimpleEnqueueJob(\"default\", jobId: \"41423\", noState: true);\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.Queues();\n\n            // Assert\n            var someJob = result.Single().FirstJobs.Single();\n\n            Assert.Equal(\"41423\", someJob.Key);\n            Assert.Null(someJob.Value);\n        }\n\n        [Fact, CleanDatabase]\n        public void Queues_IsAbleToHandle_BackgroundJobEntry_WithNullState()\n        {\n            // Arrange\n            var jobId = SimpleEnqueueJob(\"test\", noState: true);\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.Queues();\n\n            // Assert\n            var someJob = result.Single().FirstJobs.Single();\n\n            Assert.Equal(jobId, someJob.Key);\n            Assert.False(someJob.Value.InEnqueuedState);\n            Assert.Null(someJob.Value.State);\n            Assert.Null(someJob.Value.EnqueuedAt);\n        }\n\n        [Fact, CleanDatabase]\n        public void Queues_IsAbleToHandle_BackgroundJobEntry_WithAnotherState()\n        {\n            // Arrange\n            var jobId = SimpleEnqueueJob(\"default\", state: new DeletedState());\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.Queues();\n\n            // Assert\n            var someJob = result.Single().FirstJobs.Single();\n\n            Assert.Equal(jobId, someJob.Key);\n            Assert.False(someJob.Value.InEnqueuedState);\n            Assert.Equal(\"Deleted\", someJob.Value.State, StringComparer.OrdinalIgnoreCase);\n            Assert.Null(someJob.Value.EnqueuedAt);\n        }\n\n        [Fact, CleanDatabase]\n        public void Queues_IsAbleToHandle_EnqueuedLikeStates_AsEnqueued()\n        {\n            // Arrange\n            var state = new Mock<IState>();\n            state.SetupGet(x => x.Name).Returns(\"EnQUEued\");\n\n            var jobId = SimpleEnqueueJob(\"default\", state: state.Object);\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.Queues();\n\n            // Assert\n            var someJob = result.Single().FirstJobs.Single();\n\n            Assert.Equal(jobId, someJob.Key);\n            Assert.True(someJob.Value.InEnqueuedState);\n            Assert.Equal(\"EnQUEued\", someJob.Value.State);\n            AssertWithinSecond(_utcNow, someJob.Value.EnqueuedAt);\n        }\n\n        [Fact, CleanDatabase]\n        public void Queues_ReturnsTop5Jobs_FromItsHead()\n        {\n            // Arrange\n            var jobId1 = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n            var jobId2 = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n            var jobId3 = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n            var jobId4 = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n            var jobId5 = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n            var jobId6 = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n            var jobId7 = SimpleEnqueueJob(\"critical\", state: new EnqueuedState());\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.Queues();\n\n            // Assert\n            var defaultQueue = result.Single(x => x.Name == \"default\");\n            Assert.Equal(6, defaultQueue.Length);\n            Assert.Equal(5, defaultQueue.FirstJobs.Count);\n            Assert.Equal(jobId1, defaultQueue.FirstJobs.First().Key);\n            Assert.Equal(jobId5, defaultQueue.FirstJobs.Last().Key);\n\n            var criticalQueue = result.Single(x => x.Name == \"critical\");\n            Assert.Equal(1, criticalQueue.Length);\n            Assert.Single(criticalQueue.FirstJobs);\n            Assert.Equal(jobId7, criticalQueue.FirstJobs.Single().Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void Queues_IsAbleToHandleSerializationProblems_InJobs()\n        {\n            // Arrange\n            var jobId = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n\n            UseSqlConnection(connection =>\n            {\n                var wrongData = new InvocationData(\"asfasf\", \"232\", \"afasf\", \"gg\");\n                var payload = wrongData.SerializePayload(excludeArguments: true);\n                connection.Execute(\n                    $\"update [{Constants.DefaultSchema}].Job set InvocationData = @data, Arguments = @args where Id = @jobId\",\n                    new { jobId, data = payload, args = wrongData.Arguments });\n            });\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.Queues();\n\n            // Assert\n            var queuedJob = result.Single().FirstJobs.Single();\n            Assert.Equal(jobId, queuedJob.Key);\n            Assert.Null(queuedJob.Value.Job);\n            Assert.True(queuedJob.Value.InEnqueuedState);\n            Assert.Equal(\"Enqueued\", queuedJob.Value.State, StringComparer.OrdinalIgnoreCase);\n        }\n\n        [Fact, CleanDatabase]\n        public void Queues_ProducesSortedList_RegardlessOfActualEnqueueOrder()\n        {\n            // Arrange\n            SimpleEnqueueJob(\"b-queue\", state: new EnqueuedState());\n            SimpleEnqueueJob(\"a-queue\", state: new EnqueuedState());\n            SimpleEnqueueJob(\"c-queue\", state: new EnqueuedState());\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.Queues().ToArray();\n\n            // Assert\n            Assert.Equal(\"a-queue\", result[0].Name);\n            Assert.Equal(\"b-queue\", result[1].Name);\n            Assert.Equal(\"c-queue\", result[2].Name);\n        }\n\n        [Fact, CleanDatabase]\n        public void Servers_ReturnsEmptyCollection_WhenThereAreNoServers()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.Servers();\n\n            Assert.NotNull(result);\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void Servers_ReturnsAllTheRegisteredServers_WithCorrectDetails()\n        {\n            // Arrange\n            UseConnection(connection =>\n            {\n                connection.AnnounceServer(\"server1\", new ServerContext { Queues = new []{ \"default\" }, WorkerCount = 100 });\n                connection.AnnounceServer(\"server2\", new ServerContext { Queues = new[] { \"critical\" }, WorkerCount = 17 });\n                connection.AnnounceServer(\"server3\", new ServerContext { Queues = new[] { \"alpha\", \"beta\" }, WorkerCount = 0 });\n                return true;\n            });\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.Servers();\n\n            // Assert\n            Assert.Equal(3, result.Count);\n\n            var server1 = result.Single(x => x.Name == \"server1\");\n            Assert.Equal(new [] { \"default\" }, server1.Queues);\n            Assert.Equal(100, server1.WorkersCount);\n            AssertWithinSecond(_utcNow, server1.StartedAt);\n            AssertWithinSecond(_utcNow, server1.Heartbeat);\n\n            var server2 = result.Single(x => x.Name == \"server2\");\n            Assert.Equal(new[] { \"critical\" }, server2.Queues);\n            Assert.Equal(17, server2.WorkersCount);\n            AssertWithinSecond(_utcNow, server2.StartedAt);\n            AssertWithinSecond(_utcNow, server2.Heartbeat);\n\n            var server3 = result.Single(x => x.Name == \"server3\");\n            Assert.Equal(new[] { \"alpha\", \"beta\" }, server3.Queues);\n            Assert.Equal(0, server3.WorkersCount);\n            AssertWithinSecond(_utcNow, server3.StartedAt);\n            AssertWithinSecond(_utcNow, server3.Heartbeat);\n        }\n\n        [Fact, CleanDatabase]\n        public void Servers_ProducesSortedList_RegardlessOfActualAnnouncementOrder()\n        {\n            // Arrange\n            UseConnection(connection =>\n            {\n                connection.AnnounceServer(\"server3\", new ServerContext { Queues = new[] { \"default\" }, WorkerCount = 4 });\n                connection.AnnounceServer(\"server1\", new ServerContext { Queues = new[] { \"default\" }, WorkerCount = 4 });\n                connection.AnnounceServer(\"server2\", new ServerContext { Queues = new[] { \"default\" }, WorkerCount = 4 });\n                return true;\n            });\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.Servers().ToArray();\n\n            // Assert\n            Assert.Equal(\"server1\", result[0].Name);\n            Assert.Equal(\"server2\", result[1].Name);\n            Assert.Equal(\"server3\", result[2].Name);\n        }\n\n        [Fact, CleanDatabase]\n        public void JobDetails_ThrowsAnException_WhenJobIdIsNull()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => monitoring.JobDetails(null));\n\n            Assert.Equal(\"jobId\", exception.ParamName);\n        }\n\n        [Fact, CleanDatabase]\n        public void JobDetails_ReturnsNull_WhenTargetJobDoesNotExist()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.JobDetails(\"1412\");\n\n            Assert.Null(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void JobDetails_CorrectlyHandles_CreatedButNotInitializedJob()\n        {\n            // Arrange\n            var jobId = UseConnection(connection => connection.CreateExpiredJob(\n                    Job.FromExpression(() => Empty()),\n                    new Dictionary<string, string> { { \"CurrentCulture\", \"en-US\" }, { \"RetryCount\", \"5\" } },\n                    _utcNow,\n                    TimeSpan.FromMinutes(37)));\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.JobDetails(jobId);\n\n            // Assert\n            Assert.NotNull(result);\n            Assert.Equal(typeof(SqlServerMonitoringApiFacts), result.Job.Type);\n            Assert.Equal(\"Empty\", result.Job.Method.Name);\n            Assert.Equal(\"en-US\", result.Properties[\"CurrentCulture\"]);\n            Assert.Equal(\"5\", result.Properties[\"RetryCount\"]);\n            AssertWithinSecond(_utcNow, result.CreatedAt);\n            AssertWithinSecond(_utcNow.AddMinutes(37), result.ExpireAt);\n        }\n\n        [Fact, CleanDatabase]\n        public void JobDetails_CorrectlyShowsCreated_AndInitializedJob()\n        {\n            // Arrange\n            var createdId = UseConnection(connection =>\n            {\n                var jobId = connection.CreateExpiredJob(\n                    Job.FromExpression(() => Empty()),\n                    new Dictionary<string, string>(), \n                    _utcNow,\n                    TimeSpan.FromMinutes(1));\n\n                using (var transaction = connection.CreateWriteTransaction())\n                {\n                    transaction.SetJobState(jobId, new EnqueuedState(\"critical\") { Reason = \"Some reason\" });\n                    transaction.Commit();\n                }\n\n                using (var transaction = connection.CreateWriteTransaction())\n                {\n                    transaction.SetJobState(jobId, new DeletedState());\n                    transaction.Commit();\n                }\n\n                return jobId;\n            });\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.JobDetails(createdId);\n\n            // Assert\n            Assert.NotNull(result);\n            Assert.Equal(\"Deleted\", result.History.First().StateName);\n            Assert.Null(result.History.First().Reason);\n            AssertWithinSecond(_utcNow, result.History.First().CreatedAt);\n\n            Assert.Equal(\"Enqueued\", result.History.Last().StateName, StringComparer.OrdinalIgnoreCase);\n            Assert.Equal(\"Some reason\", result.History.Last().Reason);\n            Assert.Equal(\"critical\", result.History.Last().Data[\"Queue\"]);\n            AssertWithinSecond(_utcNow.Add(TimeSpan.FromMinutes(-1)), result.History.Last().CreatedAt);\n        }\n\n        [Fact, CleanDatabase]\n        public void JobDetails_IsAbleToHandleSerializationProblems()\n        {\n            // Arrange\n            var jobId = UseConnection(connection => connection.CreateExpiredJob(\n                Job.FromExpression(() => Empty()),\n                new Dictionary<string, string>(),\n                _utcNow,\n                TimeSpan.FromMinutes(1)));\n\n            UseSqlConnection(connection =>\n            {\n                var wrongData = new InvocationData(\"asfasf\", \"232\", \"afasf\", \"gg\");\n                var payload = wrongData.SerializePayload(excludeArguments: true);\n                connection.Execute(\n                    $\"update [{Constants.DefaultSchema}].Job set InvocationData = @data, Arguments = @args where Id = @jobId\",\n                    new { jobId, data = payload, args = wrongData.Arguments });\n            });\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.JobDetails(jobId);\n\n            // Assert\n            Assert.NotNull(result);\n            Assert.Null(result.Job);\n            Assert.All(result.Properties, pair => Assert.StartsWith(\"DBG_\", pair.Key));\n            Assert.Empty(result.History);\n            AssertWithinSecond(_utcNow, result.CreatedAt);\n            AssertWithinSecond(_utcNow, result.ExpireAt);\n        }\n\n        [Fact, CleanDatabase]\n        public void GetStatistics_ReturnsEmptyStatistics_WhenNothingIsCreatedYet()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.GetStatistics();\n\n            Assert.Equal(0, result.Deleted);\n            Assert.Equal(0, result.Enqueued);\n            Assert.Equal(0, result.Failed);\n            Assert.Equal(0, result.Processing);\n            Assert.Equal(0, result.Queues);\n            Assert.Equal(0, result.Recurring);\n            Assert.Equal(0, result.Scheduled);\n            Assert.Equal(0, result.Servers);\n            Assert.Equal(0, result.Succeeded);\n#if !HANGFIRE_170 \n            Assert.Equal(0, result.Retries);\n#endif\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_ThrowsAnException_WhenQueueNamesIsNull()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => monitoring.EnqueuedJobs(null, 0, 10));\n\n            Assert.Equal(\"queue\", exception.ParamName);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_ReturnsEmptyCollection_WhenThereIsNoSuchQueue()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.EnqueuedJobs(\"critical\", 0, 10);\n\n            Assert.NotNull(result);\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_ReturnsCorrectJobs_WhichWillBeDequeuedNext()\n        {\n            // Arrange\n            var jobId = SimpleEnqueueJob(\n                \"critical\",\n                job: Job.FromExpression(() => Empty()),\n                state: new EnqueuedState());\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.EnqueuedJobs(\"critical\", 0, 10);\n\n            // Assert\n            var queuedJob = result.Single();\n\n            Assert.Equal(jobId, queuedJob.Key);\n            Assert.True(queuedJob.Value.InEnqueuedState);\n            Assert.Equal(\"Enqueued\", queuedJob.Value.State, StringComparer.OrdinalIgnoreCase);\n            AssertWithinSecond(_utcNow, queuedJob.Value.EnqueuedAt);\n\n            Assert.Equal(typeof(SqlServerMonitoringApiFacts), queuedJob.Value.Job.Type);\n            Assert.Equal(\"Empty\", queuedJob.Value.Job.Method.Name);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_ReturnsJobs_InTheAscendingOrder()\n        {\n            // Arrange\n            var jobId1 = SimpleEnqueueJob(\"critical\");\n            var jobId2 = SimpleEnqueueJob(\"critical\");\n            var jobId3 = SimpleEnqueueJob(\"critical\");\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.EnqueuedJobs(\"critical\", 0, 10).ToArray();\n\n            // Assert\n            Assert.Equal(jobId1, result[0].Key);\n            Assert.Equal(jobId2, result[1].Key);\n            Assert.Equal(jobId3, result[2].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_IsAbleToHandle_JobIdWithoutCorrespondingBackgroundJobEntry()\n        {\n            // Arrange\n            SimpleEnqueueJob(\"default\", jobId: \"12345\", noState: true);\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.EnqueuedJobs(\"default\", 0, 10);\n\n            // Assert\n            var someJob = result.Single();\n\n            Assert.Equal(\"12345\", someJob.Key);\n            Assert.Null(someJob.Value);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_IsAbleToHandle_BackgroundJobEntry_WithNullState()\n        {\n            // Arrange\n            var jobId = SimpleEnqueueJob(\"test\", noState: true);\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.EnqueuedJobs(\"test\", 0, 10);\n\n            // Assert\n            var someJob = result.Single();\n\n            Assert.Equal(jobId, someJob.Key);\n            Assert.False(someJob.Value.InEnqueuedState);\n            Assert.Null(someJob.Value.State);\n            Assert.Null(someJob.Value.EnqueuedAt);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_IsAbleToHandle_MultipleEntriesWithTheSameJobId()\n        {\n            // Arrange\n            var jobId = SimpleEnqueueJob(\"default\");\n            var sameId = SimpleEnqueueJob(\"default\", jobId: jobId);\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var enqueued = monitoring.EnqueuedJobs(\"default\", 0, 10).ToArray();\n\n            // Assert\n            Assert.Equal(2, enqueued.Length);\n            Assert.All(enqueued, x => Assert.Equal(jobId, x.Key));\n            Assert.All(enqueued, x => Assert.Equal(\"Enqueued\", x.Value.State));\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_IsAbleToHandle_BackgroundJobEntry_WithAnotherState()\n        {\n            // Arrange\n            var jobId = SimpleEnqueueJob(\"default\", state: new DeletedState());\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.EnqueuedJobs(\"default\", 0, 10);\n\n            // Assert\n            var someJob = result.Single();\n\n            Assert.Equal(jobId, someJob.Key);\n            Assert.False(someJob.Value.InEnqueuedState);\n            Assert.Equal(\"Deleted\", someJob.Value.State, StringComparer.OrdinalIgnoreCase);\n            Assert.Null(someJob.Value.EnqueuedAt);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_IsAbleToHandle_EnqueuedLikeStates_AsEnqueued()\n        {\n            // Arrange\n            var state = new Mock<IState>();\n            state.SetupGet(x => x.Name).Returns(\"EnQUEued\");\n\n            var jobId = SimpleEnqueueJob(\"default\", state: state.Object);\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.EnqueuedJobs(\"default\", 0, 10);\n\n            // Assert\n            var someJob = result.Single();\n\n            Assert.Equal(jobId, someJob.Key);\n            Assert.True(someJob.Value.InEnqueuedState);\n            Assert.Equal(\"EnQUEued\", someJob.Value.State);\n            AssertWithinSecond(_utcNow, someJob.Value.EnqueuedAt);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_ReturnsJobs_WithinTheGivenRange_FromTheGivenQueue()\n        {\n            // Arrange\n            var jobId1 = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n            var jobId2 = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n            var jobIdX = SimpleEnqueueJob(\"critical\", state: new EnqueuedState());\n            var jobId3 = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n            var jobId4 = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.EnqueuedJobs(\"default\", 1, 2);\n\n            // Assert\n            Assert.Equal(2, result.Count);\n            Assert.Equal(jobId2, result.First().Key);\n            Assert.Equal(jobId3, result.Last().Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedJobs_IsAbleToHandleSerializationProblems_InJobs()\n        {\n            // Arrange\n            var jobId = SimpleEnqueueJob(\"default\", state: new EnqueuedState());\n\n            UseSqlConnection(connection =>\n            {\n                var wrongData = new InvocationData(\"asfasf\", \"232\", \"afasf\", \"gg\");\n                var payload = wrongData.SerializePayload(excludeArguments: true);\n                connection.Execute(\n                    $\"update [{Constants.DefaultSchema}].Job set InvocationData = @data, Arguments = @args where Id = @jobId\",\n                    new { jobId, data = payload, args = wrongData.Arguments });\n            });\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.EnqueuedJobs(\"default\", 0, 10);\n\n            // Assert\n            var queuedJob = result.Single();\n            Assert.Equal(jobId, queuedJob.Key);\n            Assert.Null(queuedJob.Value.Job);\n            Assert.True(queuedJob.Value.InEnqueuedState);\n            Assert.Equal(\"Enqueued\", queuedJob.Value.State, StringComparer.OrdinalIgnoreCase);\n        }\n\n        [Fact, CleanDatabase]\n        public void FetchedJobs_ThrowsAnException_WhenQueueIsNull()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => monitoring.FetchedJobs(null, 0, 10));\n\n            Assert.Equal(\"queue\", exception.ParamName);\n        }\n\n        [Fact, CleanDatabase]\n        public void FetchedJobs_ReturnsEmptyCollection()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.FetchedJobs(\"default\", 0, 10);\n\n            Assert.NotNull(result);\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void ProcessingJobs_ReturnsEmptyCollection_WhenThereAreNoProcessingJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.ProcessingJobs(0, 10);\n\n            Assert.NotNull(result);\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void ProcessingJobs_ReturnsCorrectJobs_InTheProcessingState()\n        {\n            // Arrange\n            var jobId = SimpleProcessingJob(\"server-1\", \"worker-1\", job: Job.FromExpression(() => Empty()));\n            var monitoring = CreateMonitoringApi();\n            \n            // Act\n            var result = monitoring.ProcessingJobs(0, 10);\n\n            // Assert\n            var processingJob = result.Single();\n            \n            Assert.Equal(jobId, processingJob.Key);\n            Assert.True(processingJob.Value.InProcessingState);\n            Assert.Equal(\"server-1\", processingJob.Value.ServerId);\n            AssertWithinSecond(_utcNow, processingJob.Value.StartedAt);\n            \n            Assert.Equal(typeof(SqlServerMonitoringApiFacts), processingJob.Value.Job.Type);\n            Assert.Equal(\"Empty\", processingJob.Value.Job.Method.Name);\n        }\n\n        [Fact, CleanDatabase]\n        public void ProcessingJobs_ReturnsJobs_InTheAscendingOrder()\n        {\n            // Arrange\n            var jobId1 = SimpleProcessingJob();\n            var jobId2 = SimpleProcessingJob();\n            var jobId3 = SimpleProcessingJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.ProcessingJobs(0, 10).ToArray();\n\n            // Assert\n            Assert.Equal(jobId1, result[0].Key);\n            Assert.Equal(jobId2, result[1].Key);\n            Assert.Equal(jobId3, result[2].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void ProcessingJobs_ReturnsJobs_WithinTheGivenRange()\n        {\n            // Arrange\n            var jobId1 = SimpleProcessingJob();\n            var jobId2 = SimpleProcessingJob();\n            var jobId3 = SimpleProcessingJob();\n            var jobId4 = SimpleProcessingJob();\n            var jobId5 = SimpleProcessingJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.ProcessingJobs(1, 2).ToArray();\n\n            // Assert\n            Assert.Equal(2, result.Length);\n            Assert.Equal(jobId2, result[0].Key);\n            Assert.Equal(jobId3, result[1].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void ScheduledJobs_ReturnsEmptyCollection_WhenThereAreNoScheduledJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.ScheduledJobs(0, 10);\n\n            Assert.NotNull(result);\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void ScheduledJobs_ReturnsCorrectJobs_InTheScheduledState_WithNonCompositeIndex()\n        {\n            // Arrange\n            var jobId = SimpleScheduledJob(\n                TimeSpan.FromDays(1),\n                job: Job.FromExpression(() => Empty()));\n\n            var monitoring = CreateMonitoringApi();\n            \n            // Act\n            var result = monitoring.ScheduledJobs(0, 10);\n\n            // Assert\n            var scheduledJob = result.Single();\n\n            Assert.Equal(jobId, scheduledJob.Key);\n            Assert.True(scheduledJob.Value.InScheduledState);\n            AssertWithinSecond(_utcNow.Add(TimeSpan.FromDays(1)), scheduledJob.Value.EnqueueAt);\n            AssertWithinSecond(_utcNow, scheduledJob.Value.ScheduledAt);\n\n            Assert.Equal(typeof(SqlServerMonitoringApiFacts), scheduledJob.Value.Job.Type);\n            Assert.Equal(\"Empty\", scheduledJob.Value.Job.Method.Name);\n        }\n\n        [Fact, CleanDatabase]\n        public void ScheduledJobs_ReturnsCorrectJobs_InTheScheduledState_WithCompositeIndex()\n        {\n            // Arrange\n            var jobId = SimpleScheduledJob(\n                TimeSpan.FromHours(1),\n                job: Job.FromExpression(() => Empty(), \"critical\"));\n\n            var monitoring = CreateMonitoringApi();\n            \n            // Act\n            var result = monitoring.ScheduledJobs(0, 10);\n\n            // Assert\n            var scheduledJob = result.Single();\n            Assert.Equal(jobId, scheduledJob.Key);\n            Assert.Equal(\"critical\", scheduledJob.Value.Job.Queue);\n        }\n\n        [Fact, CleanDatabase]\n        public void ScheduledJobs_ReturnsJobs_InTheAscendingOrder()\n        {\n            // Arrange\n            var jobId1 = SimpleScheduledJob();\n            var jobId2 = SimpleScheduledJob();\n            var jobId3 = SimpleScheduledJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.ScheduledJobs(0, 10).ToArray();\n\n            // Assert\n            Assert.Equal(jobId1, result[0].Key);\n            Assert.Equal(jobId2, result[1].Key);\n            Assert.Equal(jobId3, result[2].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void ScheduledJobs_ReturnsJobs_WithinTheGivenRange()\n        {\n            // Arrange\n            var jobId1 = SimpleScheduledJob();\n            var jobId2 = SimpleScheduledJob();\n            var jobId3 = SimpleScheduledJob();\n            var jobId4 = SimpleScheduledJob();\n            var jobId5 = SimpleScheduledJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.ScheduledJobs(1, 2).ToArray();\n\n            // Assert\n            Assert.Equal(2, result.Length);\n            Assert.Equal(jobId2, result[0].Key);\n            Assert.Equal(jobId3, result[1].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void SucceededJobs_ReturnsEmptyCollection_WhenThereAreNoSucceededJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.SucceededJobs(0, 10);\n\n            Assert.NotNull(result);\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void SucceededJobs_ReturnsCorrectJobs_InTheSucceededState()\n        {\n            // Arrange\n            var jobId = SimpleSucceededJob(\n                \"hello\", 123, 456,\n                job: Job.FromExpression(() => Empty()));\n\n            var monitoring = CreateMonitoringApi();\n            \n            // Act\n            var result = monitoring.SucceededJobs(0, 10);\n\n            // Assert\n            var succeededJob = result.Single();\n            \n            Assert.Equal(jobId, succeededJob.Key);\n            Assert.True(succeededJob.Value.InSucceededState);\n            Assert.Equal(\"\\\"hello\\\"\", succeededJob.Value.Result);\n            Assert.Equal(123 + 456, succeededJob.Value.TotalDuration);\n            AssertWithinSecond(_utcNow, succeededJob.Value.SucceededAt);\n            \n            Assert.Equal(typeof(SqlServerMonitoringApiFacts), succeededJob.Value.Job.Type);\n            Assert.Equal(\"Empty\", succeededJob.Value.Job.Method.Name);\n        }\n\n        [Fact, CleanDatabase]\n        public void SucceededJobs_ReturnsJobs_InTheDescendingOrder()\n        {\n            // Arrange\n            var jobId1 = SimpleSucceededJob();\n            var jobId2 = SimpleSucceededJob();\n            var jobId3 = SimpleSucceededJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.SucceededJobs(0, 10).ToArray();\n\n            // Assert\n            Assert.Equal(jobId3, result[0].Key);\n            Assert.Equal(jobId2, result[1].Key);\n            Assert.Equal(jobId1, result[2].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void SucceededJobs_ReturnsJobs_WithinTheGivenRange()\n        {\n            // Arrange\n            var jobId1 = SimpleSucceededJob();\n            var jobId2 = SimpleSucceededJob();\n            var jobId3 = SimpleSucceededJob();\n            var jobId4 = SimpleSucceededJob();\n            var jobId5 = SimpleSucceededJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.SucceededJobs(1, 2).ToArray();\n\n            // Assert\n            Assert.Equal(2, result.Length);\n            Assert.Equal(jobId4, result[0].Key);\n            Assert.Equal(jobId3, result[1].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void SucceededJobs_IsAbleToHandle_ExpiredJobEntry()\n        {\n            // Arrange\n            var jobId1 = SimpleSucceededJob(expireIn: TimeSpan.FromHours(-1));\n            UseSqlConnection(connection => connection.Execute(\n                $\"delete from[{Constants.DefaultSchema}].Job where ExpireAt is not null\"));\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var someJob = monitoring.SucceededJobs(0, 10).SingleOrDefault();\n\n            // Assert\n            Assert.True(someJob.Key == null || someJob.Key == jobId1, someJob.Key);\n            Assert.Null(someJob.Value);\n        }\n\n        [Fact, CleanDatabase]\n        public void FailedJobs_ReturnsEmptyCollection_WhenThereAreNoFailedJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.FailedJobs(0, 10);\n\n            Assert.NotNull(result);\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void FailedJobs_ReturnsCorrectJobs_InTheFailedState()\n        {\n            // Arrange\n            var jobId = SimpleJob(\n                job: Job.FromExpression(() => Empty()),\n                state: new FailedState(new InvalidOperationException(\"Hello, world!\")) { Reason = \"Some reason\" });\n\n            var monitoring = CreateMonitoringApi();\n            \n            // Act\n            var result = monitoring.FailedJobs(0, 10);\n\n            // Assert\n            var failedJob = result.Single();\n            \n            Assert.Equal(jobId, failedJob.Key);\n            Assert.True(failedJob.Value.InFailedState);\n            Assert.Equal(\"Some reason\", failedJob.Value.Reason);\n            Assert.Equal(typeof(InvalidOperationException).FullName, failedJob.Value.ExceptionType);\n            Assert.Equal(\"Hello, world!\", failedJob.Value.ExceptionMessage);\n            AssertWithinSecond(_utcNow, failedJob.Value.FailedAt);\n            \n            Assert.Equal(typeof(SqlServerMonitoringApiFacts), failedJob.Value.Job.Type);\n            Assert.Equal(\"Empty\", failedJob.Value.Job.Method.Name);\n        }\n\n        [Fact, CleanDatabase]\n        public void FailedJobs_ReturnsJobs_InTheDescendingOrder()\n        {\n            // Arrange\n            var jobId1 = SimpleFailedJob(new InvalidOperationException());\n            var jobId2 = SimpleFailedJob(new OperationCanceledException());\n            var jobId3 = SimpleFailedJob(new NotSupportedException());\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.FailedJobs(0, 10).ToArray();\n\n            // Assert\n            Assert.Equal(jobId3, result[0].Key);\n            Assert.Equal(jobId2, result[1].Key);\n            Assert.Equal(jobId1, result[2].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void FailedJobs_ReturnsJobs_WithinTheGivenRange()\n        {\n            // Arrange\n            var jobId1 = SimpleFailedJob();\n            var jobId2 = SimpleFailedJob();\n            var jobId3 = SimpleFailedJob();\n            var jobId4 = SimpleFailedJob();\n            var jobId5 = SimpleFailedJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.FailedJobs(1, 2).ToArray();\n\n            // Assert\n            Assert.Equal(2, result.Length);\n            Assert.Equal(jobId4, result[0].Key);\n            Assert.Equal(jobId3, result[1].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void DeletedJobs_ReturnsEmptyCollection_WhenThereAreNoDeletedJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.DeletedJobs(0, 10);\n\n            Assert.NotNull(result);\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void DeletedJobs_ReturnsCorrectJobs_InTheDeletedState()\n        {\n            // Arrange\n            var jobId = SimpleDeletedJob(\n                exception: new InvalidOperationException(\"Hello, world!\"), \n                job: Job.FromExpression(() => Empty()));\n\n            var monitoring = CreateMonitoringApi();\n            \n            // Act\n            var result = monitoring.DeletedJobs(0, 10);\n\n            // Assert\n            var deletedJob = result.Single();\n            \n            Assert.Equal(jobId, deletedJob.Key);\n            Assert.True(deletedJob.Value.InDeletedState);\n            AssertWithinSecond(_utcNow, deletedJob.Value.DeletedAt);\n            \n            Assert.Equal(typeof(SqlServerMonitoringApiFacts), deletedJob.Value.Job.Type);\n            Assert.Equal(\"Empty\", deletedJob.Value.Job.Method.Name);\n        }\n\n        [Fact, CleanDatabase]\n        public void DeletedJobs_ReturnsJobs_InTheDescendingOrder()\n        {\n            // Arrange\n            var jobId1 = SimpleDeletedJob(new InvalidOperationException());\n            var jobId2 = SimpleDeletedJob(new NotSupportedException());\n            var jobId3 = SimpleDeletedJob(new ArgumentNullException());\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.DeletedJobs(0, 10).ToArray();\n\n            // Assert\n            Assert.Equal(jobId3, result[0].Key);\n            Assert.Equal(jobId2, result[1].Key);\n            Assert.Equal(jobId1, result[2].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void DeletedJobs_ReturnsJobs_WithinTheGivenRange()\n        {\n            // Arrange\n            var jobId1 = SimpleDeletedJob();\n            var jobId2 = SimpleDeletedJob();\n            var jobId3 = SimpleDeletedJob();\n            var jobId4 = SimpleDeletedJob();\n            var jobId5 = SimpleDeletedJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.DeletedJobs(1, 2).ToArray();\n\n            // Assert\n            Assert.Equal(2, result.Length);\n            Assert.Equal(jobId4, result[0].Key);\n            Assert.Equal(jobId3, result[1].Key);\n        }\n        \n        [Fact, CleanDatabase]\n        public void DeletedJobs_IsAbleToHandle_ExpiredJobEntry()\n        {\n            // Arrange\n            var jobId1 = SimpleDeletedJob(expireIn: TimeSpan.FromHours(-1));\n            UseSqlConnection(connection => connection.Execute(\n                $\"delete from[{Constants.DefaultSchema}].Job where ExpireAt is not null\"));\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var someJob = monitoring.DeletedJobs(0, 10).SingleOrDefault();\n\n            // Assert\n            Assert.True(someJob.Key == null || someJob.Key == jobId1, someJob.Key);\n            Assert.Null(someJob.Value);\n        }\n\n        [Fact, CleanDatabase]\n        public void AwaitingJobs_ReturnsEmptyCollection_WhenThereAreNoAwaitingJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.AwaitingJobs(0, 10);\n\n            Assert.NotNull(result);\n            Assert.Empty(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void AwaitingJobs_ReturnsCorrectJobs_InTheAwaitingState()\n        {\n            // Arrange\n            var processingId = SimpleProcessingJob(\"server-1\", \"worker-1\");\n            var jobId = SimpleAwaitingJob(\n                processingId,\n                job: Job.FromExpression(() => Empty()));\n\n            var monitoring = CreateMonitoringApi();\n            \n            // Act\n            var result = monitoring.AwaitingJobs(0, 10);\n\n            // Assert\n            var awaitingJob = result.Single();\n            \n            Assert.Equal(jobId, awaitingJob.Key);\n            Assert.True(awaitingJob.Value.InAwaitingState);\n            Assert.Equal(\"Processing\", awaitingJob.Value.ParentStateName);\n            AssertWithinSecond(_utcNow, awaitingJob.Value.AwaitingAt);\n            \n            Assert.Equal(typeof(SqlServerMonitoringApiFacts), awaitingJob.Value.Job.Type);\n            Assert.Equal(\"Empty\", awaitingJob.Value.Job.Method.Name);\n        }\n\n        [Fact, CleanDatabase]\n        public void AwaitingJobs_DoesNotFailWhenSeveralJobs_PointToTheSameParentJob()\n        {\n            // Arrange\n            var processingId = SimpleProcessingJob(\"server-1\", \"worker-1\");\n            var jobId1 = SimpleAwaitingJob(processingId);\n            var jobId2 = SimpleAwaitingJob(processingId);\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.AwaitingJobs(0, 10);\n\n            // Assert\n            var awaiting = result.ToArray();\n\n            Assert.Equal(2, awaiting.Length);\n            Assert.Contains(jobId1, awaiting.Select(x => x.Key));\n            Assert.Contains(jobId2, awaiting.Select(x => x.Key));\n            Assert.All(awaiting, x => Assert.Equal(\"Processing\", x.Value.ParentStateName));\n        }\n\n        [Fact, CleanDatabase]\n        public void AwaitingJobs_ReturnsJobs_InTheAscendingOrder()\n        {\n            // Arrange\n            var jobId1 = SimpleAwaitingJob(\"123\");\n            var jobId2 = SimpleAwaitingJob(\"456\");\n            var jobId3 = SimpleAwaitingJob(\"789\");\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.AwaitingJobs(0, 10).ToArray();\n\n            // Assert\n            Assert.Equal(jobId1, result[0].Key);\n            Assert.Equal(jobId2, result[1].Key);\n            Assert.Equal(jobId3, result[2].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void AwaitingJobs_ReturnsJobs_WithinTheGivenRange()\n        {\n            // Arrange\n            var jobId1 = SimpleAwaitingJob(\"1\");\n            var jobId2 = SimpleAwaitingJob(\"1\");\n            var jobId3 = SimpleAwaitingJob(\"2\");\n            var jobId4 = SimpleAwaitingJob(\"1\");\n            var jobId5 = SimpleAwaitingJob(\"1\");\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.AwaitingJobs(1, 2).ToArray();\n\n            // Assert\n            Assert.Equal(2, result.Length);\n            Assert.Equal(jobId2, result[0].Key);\n            Assert.Equal(jobId3, result[1].Key);\n        }\n\n        [Fact, CleanDatabase]\n        public void ScheduledCount_ReturnsZero_WhenThereAreNoScheduledJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.ScheduledCount();\n\n            Assert.Equal(0, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void ScheduledCount_ReturnsTheCorrectNumber_OfScheduledJobs()\n        {\n            // Arrange\n            SimpleScheduledJob(TimeSpan.FromHours(1));\n            SimpleScheduledJob(TimeSpan.FromHours(2));\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.ScheduledCount();\n\n            // Assert\n            Assert.Equal(2, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedCount_ThrowsAnException_WhenQueueNameIsNull()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => monitoring.EnqueuedCount(null));\n\n            Assert.Equal(\"queue\", exception.ParamName);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedCount_ReturnsZero_WhenTargetQueueDoesNotExist()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.EnqueuedCount(\"critical\");\n\n            Assert.Equal(0, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void EnqueuedCount_ReturnsTheCorrectNumber_OfEnqueuedJobs_OfTheGivenQueue()\n        {\n            // Arrange\n            SimpleEnqueueJob(\"critical\");\n            SimpleEnqueueJob(\"default\");\n            SimpleEnqueueJob(\"critical\");\n            SimpleEnqueueJob(\"critical\");\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.EnqueuedCount(\"critical\");\n\n            // Assert\n            Assert.Equal(3, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void FetchedCount_ThrowsAnException_WhenQueueIsNull()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => monitoring.FetchedCount(null));\n\n            Assert.Equal(\"queue\", exception.ParamName);\n        }\n\n        [Fact, CleanDatabase]\n        public void FetchedCount_ReturnsZero_WhenTargetQueueDoesNotExist()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.FetchedCount(\"critical\");\n\n            Assert.Equal(0, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void FailedCount_ReturnsZero_WhenThereAreNoFailedJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.FailedCount();\n\n            Assert.Equal(0, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void FailedCount_ReturnsTheCorrectNumber_OfFailedJobs()\n        {\n            // Arrange\n            SimpleFailedJob();\n            SimpleFailedJob();\n            SimpleFailedJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.FailedCount();\n\n            // Assert\n            Assert.Equal(3, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void ProcessingCount_ReturnsZero_WhenThereAreNoProcessingJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.ProcessingCount();\n\n            Assert.Equal(0, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void ProcessingCount_ReturnsTheCorrectNumber_OfProcessingJobs()\n        {\n            // Arrange\n            SimpleProcessingJob(\"1\", \"1\");\n            SimpleProcessingJob(\"2\", \"2\");\n            SimpleProcessingJob(\"3\", \"3\");\n            SimpleProcessingJob(\"1\", \"1\");\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.ProcessingCount();\n\n            // Assert\n            Assert.Equal(4, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void SucceededListCount_ReturnsZero_WhenThereAreNoSucceededJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.SucceededListCount();\n\n            Assert.Equal(0, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void SucceededListCount_ReturnsTheCorrectNumber_OfSucceededJobs_CurrentlyInIndex()\n        {\n            // Arrange\n            SimpleSucceededJob();\n            SimpleSucceededJob();\n            SimpleSucceededJob();\n            SimpleSucceededJob();\n            SimpleSucceededJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.SucceededListCount();\n\n            // Assert\n            Assert.Equal(5, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void DeletedListCount_ReturnsZero_WhenThereAreNoDeletedJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.DeletedListCount();\n\n            Assert.Equal(0, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void DeletedListCount_ReturnsTheCorrectNumber_OfDeletedJobs_CurrentlyInIndex()\n        {\n            // Arrange\n            SimpleDeletedJob();\n            SimpleDeletedJob();\n            SimpleDeletedJob();\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.DeletedListCount();\n\n            // Assert\n            Assert.Equal(3, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void AwaitingCount_ReturnsZero_WhenThereAreNoAwaitingJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.AwaitingCount();\n\n            Assert.Equal(0, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void AwaitingCount_ReturnsTheCorrectNumber_OfAwaitingJobs()\n        {\n            // Arrange\n            SimpleAwaitingJob(\"1\");\n            SimpleAwaitingJob(\"2\");\n            SimpleAwaitingJob(\"3\");\n            SimpleAwaitingJob(\"1\");\n\n            var monitoring = CreateMonitoringApi();\n\n            // Act\n            var result = monitoring.AwaitingCount();\n\n            // Assert\n            Assert.Equal(4, result);\n        }\n\n        [Fact, CleanDatabase]\n        public void SucceededByDatesCount_ReturnsEntriesForTheWholeWeek_EvenWhenThereAreNoSucceededJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.SucceededByDatesCount();\n            var date = _utcNow.Date;\n\n            Assert.Equal(7, result.Count);\n            Assert.Equal(0, result[date]);\n            Assert.Equal(0, result[date.AddDays(-1)]);\n            Assert.Equal(0, result[date.AddDays(-2)]);\n            Assert.Equal(0, result[date.AddDays(-3)]);\n            Assert.Equal(0, result[date.AddDays(-4)]);\n            Assert.Equal(0, result[date.AddDays(-5)]);\n            Assert.Equal(0, result[date.AddDays(-6)]);\n        }\n\n        [Fact, CleanDatabase]\n        public void FailedByDatesCount_ReturnsEntriesForTheWholeWeek_EvenWhenThereAreNoFailedJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.FailedByDatesCount();\n            var date = _utcNow.Date;\n\n            Assert.Equal(7, result.Count);\n            Assert.Equal(0, result[date]);\n            Assert.Equal(0, result[date.AddDays(-1)]);\n            Assert.Equal(0, result[date.AddDays(-2)]);\n            Assert.Equal(0, result[date.AddDays(-3)]);\n            Assert.Equal(0, result[date.AddDays(-4)]);\n            Assert.Equal(0, result[date.AddDays(-5)]);\n            Assert.Equal(0, result[date.AddDays(-6)]);\n        }\n\n        [Fact, CleanDatabase]\n        public void DeletedByDatesCount_ReturnsEntriesForTheWholeWeek_EvenWhenThereAreNoDeletedJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.DeletedByDatesCount();\n            var date = _utcNow.Date;\n\n            Assert.Equal(7, result.Count);\n            Assert.Equal(0, result[date]);\n            Assert.Equal(0, result[date.AddDays(-1)]);\n            Assert.Equal(0, result[date.AddDays(-2)]);\n            Assert.Equal(0, result[date.AddDays(-3)]);\n            Assert.Equal(0, result[date.AddDays(-4)]);\n            Assert.Equal(0, result[date.AddDays(-5)]);\n            Assert.Equal(0, result[date.AddDays(-6)]);\n        }\n\n        [Fact, CleanDatabase]\n        public void HourlySucceededJobs_ReturnsEntriesForTheWholeDay_EvenWhenThereAreNoSucceededJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.HourlySucceededJobs();\n\n            Assert.Equal(24, result.Count);\n            Assert.Equal(0, result[result.Keys.ElementAt(0)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(3)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(6)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(9)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(12)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(15)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(18)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(21)]);\n        }\n\n        [Fact, CleanDatabase]\n        public void HourlyFailedJobs_ReturnsEntriesForTheWholeDay_EvenWhenThereAreNoFailedJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.HourlyFailedJobs();\n\n            Assert.Equal(24, result.Count);\n            Assert.Equal(0, result[result.Keys.ElementAt(0)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(3)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(6)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(9)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(12)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(15)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(18)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(21)]);\n        }\n\n        [Fact, CleanDatabase]\n        public void HourlyDeletedJobs_ReturnsEntriesForTheWholeDay_EvenWhenThereAreNoDeletedJobs()\n        {\n            var monitoring = CreateMonitoringApi();\n\n            var result = monitoring.HourlyDeletedJobs();\n\n            Assert.Equal(24, result.Count);\n            Assert.Equal(0, result[result.Keys.ElementAt(0)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(3)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(6)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(9)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(12)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(15)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(18)]);\n            Assert.Equal(0, result[result.Keys.ElementAt(21)]);\n        }\n\n        private string SimpleProcessingJob(string serverId = null, string workerId = null, Job job = null)\n        {\n            var type = typeof(ProcessingState);\n            var ctor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);\n            var processingState = (ProcessingState)ctor.First().Invoke(new object[] { serverId ?? \"server-1\", workerId ?? \"worker-1\" });\n\n            return SimpleJob(state: processingState, job: job);\n        }\n\n        private string SimpleScheduledJob(TimeSpan? delay = null, Job job = null)\n        {\n            var state = new ScheduledState(delay ?? TimeSpan.FromHours(1));\n            return SimpleJob(job: job, state: state, transactionAction: (transaction, assignedJobId, assignedJob) =>\n            {\n                transaction.AddToSet(\n                    \"schedule\",\n                    assignedJob.Queue != null ? $\"{assignedJob.Queue}:{assignedJobId}\" : assignedJobId,\n                    JobHelper.ToTimestamp(state.EnqueueAt));\n            });\n        }\n\n        private string SimpleEnqueueJob(string queue, string jobId = null, IState state = null, Job job = null, bool noState = false)\n        {\n            return SimpleJob(jobId, job, !noState ? state ?? new EnqueuedState() : null, transactionAction: (transaction, assignedJobId, _) =>\n            {\n                transaction.AddToQueue(queue, assignedJobId);\n            });\n        }\n\n        private string SimpleSucceededJob(object result = null, long latency = 0, long duration = 0, Job job = null, TimeSpan? expireIn = null)\n        {\n            return SimpleJob(\n                job: job,\n                state: new SucceededState(result, latency, duration),\n                expireIn: expireIn);\n        }\n\n        private string SimpleFailedJob(Exception exception = null, Job job = null)\n        {\n            return SimpleJob(\n                job: job,\n                state: new FailedState(exception ?? new InvalidOperationException()));\n        }\n\n        private string SimpleDeletedJob(Exception exception = null, Job job = null, TimeSpan? expireIn = null)\n        {\n            return SimpleJob(\n                job: job,\n                state: new DeletedState(\n                    new ExceptionInfo(exception ?? new InvalidOperationException())\n                ),\n                expireIn: expireIn);\n        }\n\n        private string SimpleAwaitingJob(string parentId, Job job = null)\n        {\n            return SimpleJob(\n                job: job,\n                state: new AwaitingState(parentId));\n        }\n\n        private string SimpleJob(string jobId = null, Job job = null, IState state = null, TimeSpan? expireIn = null, Action<IWriteOnlyTransaction, string, Job> transactionAction = null)\n        {\n            var createdId = UseConnection(connection =>\n            {\n                job = job ?? Job.FromExpression(() => Empty()); \n                jobId = jobId ?? connection.CreateExpiredJob(\n                    job,\n                    new Dictionary<string, string>(),\n                    _utcNow,\n                    TimeSpan.FromMinutes(1));\n\n                using (var transaction = connection.CreateWriteTransaction())\n                {\n                    if (state != null) transaction.SetJobState(jobId, state);\n                    transactionAction?.Invoke(transaction, jobId, job);\n                    if (expireIn != null) transaction.ExpireJob(jobId, expireIn.Value);\n                    transaction.Commit();\n                }\n\n                return jobId;\n            });\n\n            return createdId;\n        }\n\n        private SqlServerMonitoringApi CreateMonitoringApi(bool useMicrosoftDataSqlClient = true)\n        {\n            var storage = new SqlServerStorage(() => ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient));\n            return new SqlServerMonitoringApi(storage, 1_000);\n        }\n\n        private T UseConnection<T>(Func<SqlServerConnection, T> action, bool useMicrosoftDataSqlClient = true)\n        {\n            var storage = new SqlServerStorage(() => ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient));\n            using (var connection = new SqlServerConnection(storage))\n            {\n                return action(connection);\n            }\n        }\n\n        private void UseSqlConnection(Action<DbConnection> action, bool useMicrosoftDataSqlClient = true)\n        {\n            using (var connection = ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient))\n            {\n                action(connection);\n            }\n        }\n\n        private static void AssertWithinSecond(DateTime date1, DateTime? date2)\n        {\n            Assert.NotNull(date2);\n            Assert.True((date1 - date2.Value).TotalSeconds <= 1.0D);\n        }\n\n        [SuppressMessage(\"Usage\", \"xUnit1013:Public method should be marked as test\")]\n        [SuppressMessage(\"ReSharper\", \"MemberCanBePrivate.Global\")]\n        public static void Empty()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/SqlServerStorageFacts.cs",
    "content": "﻿using Moq;\nusing System;\nusing System.Data.Common;\nusing System.Linq;\nusing Xunit;\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class SqlServerStorageFacts\n    {\n        private readonly SqlServerStorageOptions _options;\n\n        public SqlServerStorageFacts()\n        {\n            _options = new SqlServerStorageOptions { PrepareSchemaIfNecessary = false };\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenConnectionStringIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerStorage((string)null));\n\n            Assert.Equal(\"nameOrConnectionString\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenOptionsValueIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerStorage(\"hello\", null));\n\n            Assert.Equal(\"options\", exception.ParamName);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Ctor_CanCreateSqlServerStorage_WithExistingConnection(bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient))\n            {\n                var storage = new SqlServerStorage(connection);\n                Assert.NotNull(storage);\n            }\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenConnectionFactoryIsNull()\n        {\n            Func<DbConnection> connectionFactory = null;\n\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerStorage(connectionFactory));\n\n            Assert.Equal(\"connectionFactory\", exception.ParamName);\n        }\n\n        [Theory]\n        [InlineData(false), InlineData(true)]\n        public void Ctor_ThrowsAnException_WhenOptionsValueIsNull_WithConnectionFactory(bool useMicrosoftDataSqlClient)\n        {\n            Func<DbConnection> connectionFactory = () => ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient);\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerStorage(connectionFactory, null));\n\n            Assert.Equal(\"options\", exception.ParamName);\n        }\n\n        [Fact]\n        public void CreateAndOpenConnection_UsesConnectionFactory()\n        {\n            var connection = new Mock<DbConnection>();\n            var storage = new SqlServerStorage(() => connection.Object, _options);\n\n            Assert.Same(connection.Object, storage.CreateAndOpenConnection());\n        }\n\n        [Fact, CleanDatabase]\n        public void GetMonitoringApi_ReturnsNonNullInstance()\n        {\n            var storage = CreateStorage();\n            var api = storage.GetMonitoringApi();\n            Assert.NotNull(api);\n        }\n\n        [Fact, CleanDatabase]\n        public void GetConnection_ReturnsNonNullInstance()\n        {\n            var storage = CreateStorage();\n            using (var connection = (SqlServerConnection)storage.GetConnection())\n            {\n                Assert.NotNull(connection);\n            }\n        }\n\n#if NET452 || NET461\n        [Fact, CleanDatabase]\n        public void UseConnection_UsesSystemDataSqlClient_ByDefault_OnNet452Only()\n        {\n            var storage = CreateStorage();\n            storage.UseConnection(null, (_, connection) =>\n            {\n                Assert.IsType<System.Data.SqlClient.SqlConnection>(connection);\n            });\n        }\n#else\n        [Fact, CleanDatabase]\n        public void UseConnection_UsesMicrosoftDataSqlClient_ByDefault()\n        {\n            var storage = CreateStorage();\n            storage.UseConnection(null, (_, connection) =>\n            {\n                Assert.IsType<Microsoft.Data.SqlClient.SqlConnection>(connection);\n            });\n        }\n#endif\n\n#if !NET452\n        [Fact, CleanDatabase]\n        public void UseConnection_UsesSystemDataSqlClient_WhenSqlClientFactoryIsSet()\n        {\n            _options.SqlClientFactory = System.Data.SqlClient.SqlClientFactory.Instance;\n            var storage = CreateStorage();\n            storage.UseConnection(null, (_, connection) =>\n            {\n                Assert.IsType<System.Data.SqlClient.SqlConnection>(connection);\n            });\n        }\n#endif\n\n        [Fact, CleanDatabase]\n        public void GetComponents_ReturnsAllNeededComponents()\n        {\n            var storage = CreateStorage();\n\n            var components = storage.GetComponents();\n\n            var componentTypes = components.Select(x => x.GetType()).ToArray();\n            Assert.Contains(typeof(ExpirationManager), componentTypes);\n        }\n\n        [Fact, CleanDatabase]\n        public void HasFeature_Connection_GetUtcDateTime_ReturnsTrue()\n        {\n            var storage = CreateStorage();\n\n            var result = storage.HasFeature(\"Connection.GetUtcDateTime\");\n\n            Assert.True(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void HasFeature_Connection_GetSetContains_ReturnsTrue()\n        {\n            var storage = CreateStorage();\n\n            var result = storage.HasFeature(\"Connection.GetSetContains\");\n\n            Assert.True(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void HasFeature_Connection_GetSetCount_Limited_ReturnsTrue()\n        {\n            var storage = CreateStorage();\n\n            var result = storage.HasFeature(\"Connection.GetSetCount.Limited\");\n\n            Assert.True(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void HasFeature_Connection_BatchedGetFirstByLowestScoreFromSet_ReturnsTrue()\n        {\n            var storage = CreateStorage();\n\n            var result = storage.HasFeature(\"Connection.BatchedGetFirstByLowestScoreFromSet\");\n\n            Assert.True(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void HasFeature_Job_Queue_ReturnsTrue()\n        {\n            var storage = CreateStorage();\n\n            var result = storage.HasFeature(\"Job.Queue\");\n\n            Assert.True(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void HasFeature_Storage_ExtendedApi_ReturnsTrue()\n        {\n            var storage = CreateStorage();\n\n            var result = storage.HasFeature(\"Storage.ExtendedApi\");\n\n            Assert.True(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void HasFeature_Transaction_AcquireDistributedLock_ReturnsTrue()\n        {\n            var storage = CreateStorage();\n\n            var result = storage.HasFeature(\"Transaction.AcquireDistributedLock\");\n\n            Assert.True(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void HasFeature_Monitoring_AwaitingJobs_ReturnsTrue()\n        {\n            var storage = CreateStorage();\n\n            var result = storage.HasFeature(\"Monitoring.AwaitingJobs\");\n\n            Assert.True(result);\n        }\n\n        [Fact, CleanDatabase]\n        public void HasFeature_Monitoring_DeletedStateGraphs_ReturnsTrue()\n        {\n            var storage = CreateStorage();\n\n            var result = storage.HasFeature(\"Monitoring.DeletedStateGraphs\");\n\n            Assert.True(result);\n        }\n\n        private SqlServerStorage CreateStorage()\n        {\n            return new SqlServerStorage(\n                ConnectionUtils.GetConnectionString(),\n                _options);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/SqlServerTimeoutJobFacts.cs",
    "content": "extern alias ReferencedDapper;\n\nusing System;\nusing System.Data;\nusing System.Linq;\nusing System.Threading;\nusing ReferencedDapper::Dapper;\nusing Xunit;\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class SqlServerTimeoutJobFacts\n    {\n        private const string JobId = \"id\";\n        private const string Queue = \"queue\";\n        private static readonly DateTime FetchedAt = DateTime.UtcNow;\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenConnectionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerTimeoutJob(null, 1, JobId, Queue, FetchedAt));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Ctor_ThrowsAnException_WhenJobIdIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection((sql, storage) =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => new SqlServerTimeoutJob(storage, 1, null, Queue, FetchedAt));\n\n                Assert.Equal(\"jobId\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Ctor_ThrowsAnException_WhenQueueIsNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection((sql, storage) =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => new SqlServerTimeoutJob(storage, 1, JobId, null, FetchedAt));\n\n                Assert.Equal(\"queue\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Ctor_CorrectlySets_AllInstanceProperties(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection((sql, storage) =>\n            {\n                using (var fetchedJob = new SqlServerTimeoutJob(storage, 1, JobId, Queue, FetchedAt))\n                {\n                    Assert.Equal(1, fetchedJob.Id);\n                    Assert.Equal(JobId, fetchedJob.JobId);\n                    Assert.Equal(Queue, fetchedJob.Queue);\n                    Assert.Equal(FetchedAt, fetchedJob.FetchedAt);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void RemoveFromQueue_ReallyDeletesTheJobFromTheQueue(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection((sql, storage) =>\n            {\n                // Arrange\n                var id = CreateJobQueueRecord(sql, \"1\", \"default\", FetchedAt);\n                using (var processingJob = new SqlServerTimeoutJob(storage, id, \"1\", \"default\", FetchedAt))\n                {\n                    processingJob.DisposeTimer();\n\n                    // Act\n                    processingJob.RemoveFromQueue();\n\n                    // Assert\n                    var count = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].JobQueue\").Single();\n                    Assert.Equal(0, count);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void RemoveFromQueue_DoesNotDelete_UnrelatedJobs(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection((sql, storage) =>\n            {\n                // Arrange\n                CreateJobQueueRecord(sql, \"1\", \"default\", FetchedAt);\n                CreateJobQueueRecord(sql, \"1\", \"critical\", FetchedAt);\n                CreateJobQueueRecord(sql, \"2\", \"default\", FetchedAt);\n\n                using (var fetchedJob = new SqlServerTimeoutJob(storage, 999, \"1\", \"default\", FetchedAt))\n                {\n                    fetchedJob.DisposeTimer();\n\n                    // Act\n                    fetchedJob.RemoveFromQueue();\n\n                    // Assert\n                    var count = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].JobQueue\").Single();\n                    Assert.Equal(3, count);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Requeue_SetsFetchedAtValueToNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection((sql, storage) =>\n            {\n                // Arrange\n                var id = CreateJobQueueRecord(sql, \"1\", \"default\", FetchedAt);\n                using (var processingJob = new SqlServerTimeoutJob(storage, id, \"1\", \"default\", FetchedAt))\n                {\n                    processingJob.DisposeTimer();\n\n                    // Act\n                    processingJob.Requeue();\n\n                    // Assert\n                    var record = sql.Query($\"select * from [{Constants.DefaultSchema}].JobQueue\").Single();\n                    Assert.Null(record.FetchedAt);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Timer_UpdatesFetchedAtColumn(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection((sql, storage) =>\n            {\n                // Arrange\n                var id = CreateJobQueueRecord(sql, \"1\", \"default\", FetchedAt);\n                using (var processingJob = new SqlServerTimeoutJob(storage, id, \"1\", \"default\", FetchedAt))\n                {\n                    processingJob.DisposeTimer();\n                    Thread.Sleep(TimeSpan.FromSeconds(2));\n                    processingJob.ExecuteKeepAliveQueryIfRequired();\n\n                    var record = sql.Query($\"select * from [{Constants.DefaultSchema}].JobQueue\").Single();\n\n                    Assert.NotNull(processingJob.FetchedAt);\n                    Assert.Equal<DateTime?>(processingJob.FetchedAt, record.FetchedAt);\n                    var now = DateTime.UtcNow;\n                    Assert.True(now.AddSeconds(-5) < record.FetchedAt, (now - record.FetchedAt).ToString());\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void RemoveFromQueue_AfterTimer_RemovesJobFromTheQueue(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection((sql, storage) =>\n            {\n                // Arrange\n                var id = CreateJobQueueRecord(sql, \"1\", \"default\", FetchedAt);\n                using (var processingJob = new SqlServerTimeoutJob(storage, id, \"1\", \"default\", FetchedAt))\n                {\n                    Thread.Sleep(TimeSpan.FromSeconds(1));\n                    processingJob.DisposeTimer();\n\n                    // Act\n                    processingJob.RemoveFromQueue();\n\n                    // Assert\n                    var count = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].JobQueue\").Single();\n                    Assert.Equal(0, count);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void RequeueQueue_AfterTimer_SetsFetchedAtValueToNull(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection((sql, storage) =>\n            {\n                // Arrange\n                var id = CreateJobQueueRecord(sql, \"1\", \"default\", FetchedAt);\n                using (var processingJob = new SqlServerTimeoutJob(storage, id, \"1\", \"default\", FetchedAt))\n                {\n                    Thread.Sleep(TimeSpan.FromSeconds(1));\n                    processingJob.DisposeTimer();\n\n                    // Act\n                    processingJob.Requeue();\n\n                    // Assert\n                    var record = sql.Query($\"select * from [{Constants.DefaultSchema}].JobQueue\").Single();\n                    Assert.Null(record.FetchedAt);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false), InlineData(true)]\n        public void Dispose_SetsFetchedAtValueToNull_IfThereWereNoCallsToComplete(bool useMicrosoftDataSqlClient)\n        {\n            UseConnection((sql, storage) =>\n            {\n                // Arrange\n                var id = CreateJobQueueRecord(sql, \"1\", \"default\", FetchedAt);\n                using (var processingJob = new SqlServerTimeoutJob(storage, id, \"1\", \"default\", FetchedAt))\n                {\n                    // Act\n                    processingJob.Dispose();\n\n                    // Assert\n                    var record = sql.Query($\"select * from [{Constants.DefaultSchema}].JobQueue\").Single();\n                    Assert.Null(record.FetchedAt);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        private static int CreateJobQueueRecord(IDbConnection connection, string jobId, string queue, DateTime? fetchedAt)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].JobQueue (JobId, Queue, FetchedAt)\nvalues (@id, @queue, @fetchedAt);\nselect scope_identity() as Id\";\n\n            return (int)connection.Query(arrangeSql, new { id = jobId, queue, fetchedAt }).Single().Id;\n        }\n\n        private static void UseConnection(Action<IDbConnection, SqlServerStorage> action, bool useMicrosoftDataSqlClient)\n        {\n            var storage = new SqlServerStorage(\n                () => ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient),\n                new SqlServerStorageOptions { SlidingInvisibilityTimeout = TimeSpan.FromSeconds(5) });\n\n            using (var connection = ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient))\n            {\n                action(connection, storage);\n            }\n        }\n    }\n}"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/SqlServerTransactionJobFacts.cs",
    "content": "﻿using System;\nusing System.Data;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class SqlServerTransactionJobFacts\n    {\n        private const string JobId = \"id\";\n        private const string Queue = \"queue\";\n\n        private readonly Mock<IDbConnection> _connection;\n        private readonly Mock<IDbTransaction> _transaction;\n        private readonly Mock<SqlServerStorage> _storage;\n\n        public SqlServerTransactionJobFacts()\n        {\n            _connection = new Mock<IDbConnection>();\n            _transaction = new Mock<IDbTransaction>();\n            _storage = new Mock<SqlServerStorage>(ConnectionUtils.GetConnectionString());\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenStorageIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerTransactionJob(null, _connection.Object, _transaction.Object, JobId, Queue));\n\n            Assert.Equal(\"storage\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenConnectionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerTransactionJob(_storage.Object, null, _transaction.Object, JobId, Queue));\n\n            Assert.Equal(\"connection\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenTransactionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerTransactionJob(_storage.Object, _connection.Object, null, JobId, Queue));\n\n            Assert.Equal(\"transaction\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenJobIdIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerTransactionJob(_storage.Object, _connection.Object, _transaction.Object, null, Queue));\n\n            Assert.Equal(\"jobId\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_WhenQueueIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerTransactionJob(_storage.Object, _connection.Object, _transaction.Object, JobId, null));\n\n            Assert.Equal(\"queue\", exception.ParamName);\n        }\n\n        [Fact]\n        public void Ctor_CorrectlySets_AllInstanceProperties()\n        {\n            var fetchedJob = new SqlServerTransactionJob(_storage.Object, _connection.Object, _transaction.Object, JobId, Queue);\n\n            Assert.Equal(JobId, fetchedJob.JobId);\n            Assert.Equal(Queue, fetchedJob.Queue);\n        }\n\n        [Fact]\n        public void RemoveFromQueue_CommitsTheTransaction()\n        {\n            // Arrange\n            var processingJob = CreateFetchedJob(\"1\", \"default\");\n\n            // Act\n            processingJob.RemoveFromQueue();\n\n            // Assert\n            _transaction.Verify(x => x.Commit());\n        }\n\n        [Fact]\n        public void Requeue_RollsbackTheTransaction()\n        {\n            // Arrange\n            var processingJob = CreateFetchedJob(\"1\", \"default\");\n\n            // Act\n            processingJob.Requeue();\n\n            // Assert\n            _transaction.Verify(x => x.Rollback());\n        }\n\n        [Fact]\n        public void Dispose_DisposesTheTransactionAndConnection()\n        {\n            var processingJob = CreateFetchedJob(\"1\", \"queue\");\n\n            // Act\n            processingJob.Dispose();\n\n            // Assert\n            _transaction.Verify(x => x.Dispose());\n            _connection.Verify(x => x.Dispose());\n        }\n\n        private SqlServerTransactionJob CreateFetchedJob(string jobId, string queue)\n        {\n            return new SqlServerTransactionJob(_storage.Object, _connection.Object, _transaction.Object, jobId, queue);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/SqlServerWriteOnlyTransactionFacts.cs",
    "content": "﻿extern alias ReferencedDapper;\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Linq;\nusing System.Threading;\nusing ReferencedDapper::Dapper;\nusing Hangfire.States;\nusing Hangfire.Storage;\nusing Moq;\nusing Xunit;\n\n// ReSharper disable AssignNullToNotNullAttribute\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class SqlServerWriteOnlyTransactionFacts\n    {\n        private static readonly string TooLongKey = \"123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_12345\";\n        private static readonly string TooLongTruncatedKey = TooLongKey.Substring(0, 100);\n\n        private readonly PersistentJobQueueProviderCollection _queueProviders;\n\n        public SqlServerWriteOnlyTransactionFacts()\n        {\n            var defaultProvider = new Mock<IPersistentJobQueueProvider>();\n            defaultProvider.Setup(x => x.GetJobQueue())\n                .Returns(new Mock<IPersistentJobQueue>().Object);\n\n            _queueProviders = new PersistentJobQueueProviderCollection(defaultProvider.Object);\n        }\n\n        public static IEnumerable<object[]> GetConfiguration()\n        {\n            yield return new object[] { /* useBatching */ false, /* useMicrosoftDataSqlClient */ false, /* disableTransactionScope */ false };\n            yield return new object[] { /* useBatching */ false, /* useMicrosoftDataSqlClient */ true,  /* disableTransactionScope */ false };\n            yield return new object[] { /* useBatching */ true,  /* useMicrosoftDataSqlClient */ false, /* disableTransactionScope */ false };\n            yield return new object[] { /* useBatching */ true,  /* useMicrosoftDataSqlClient */ true,  /* disableTransactionScope */ false };\n#if NET452 || NET461\n            if (IsRunningOnWindows()) // TransactionScope isn't used on non-Windows platforms\n            {\n                yield return new object[] { /* useBatching */ false, /* useMicrosoftDataSqlClient */ false, /* disableTransactionScope */ true };\n                yield return new object[] { /* useBatching */ false, /* useMicrosoftDataSqlClient */ true,  /* disableTransactionScope */ true };\n                yield return new object[] { /* useBatching */ true,  /* useMicrosoftDataSqlClient */ false, /* disableTransactionScope */ true };\n                yield return new object[] { /* useBatching */ true,  /* useMicrosoftDataSqlClient */ true,  /* disableTransactionScope */ true };\n            }\n#endif\n        }\n\n        [Fact]\n        public void Ctor_ThrowsAnException_IfConnectionIsNull()\n        {\n            var exception = Assert.Throws<ArgumentNullException>(\n                () => new SqlServerWriteOnlyTransaction(null));\n\n            Assert.Equal(\"connection\", exception.ParamName);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireJob_ThrowsAnException_WhenJobIdIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.ExpireJob(null, TimeSpan.Zero), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"jobId\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireJob_SetsJobExpirationData(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues ('', '', getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnection(sql =>\n            {\n                var jobId = sql.Query(arrangeSql).Single().Id.ToString();\n                var anotherJobId = sql.Query(arrangeSql).Single().Id.ToString();\n\n                Commit(x => x.ExpireJob(jobId, TimeSpan.FromHours(24)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var job = GetTestJob(sql, jobId);\n                Assert.True(DateTime.UtcNow.AddHours(23) < job.ExpireAt && job.ExpireAt < DateTime.UtcNow.AddHours(25));\n\n                var anotherJob = GetTestJob(sql, anotherJobId);\n                Assert.Null(anotherJob.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistJob_ThrowsAnException_WhenJobIdIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.PersistJob(null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"jobId\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistJob_ClearsTheJobExpirationData(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt, ExpireAt)\nvalues ('', '', getutcdate(), getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnection(sql =>\n            {\n                var jobId = sql.Query(arrangeSql).Single().Id.ToString();\n                var anotherJobId = sql.Query(arrangeSql).Single().Id.ToString();\n\n                Commit(x => x.PersistJob(jobId), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var job = GetTestJob(sql, jobId);\n                Assert.Null(job.ExpireAt);\n\n                var anotherJob = GetTestJob(sql, anotherJobId);\n                Assert.NotNull(anotherJob.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetJobState_ThrowsAnException_WhenJobIdIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.SetJobState(null, new Mock<IState>().Object), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"jobId\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetJobState_ThrowsAnException_WhenStateIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.SetJobState(\"my-job\", null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"state\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetJobState_AppendsAStateAndSetItToTheJob(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues ('', '', getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnection(sql =>\n            {\n                var jobId = sql.Query(arrangeSql).Single().Id.ToString();\n                var anotherJobId = sql.Query(arrangeSql).Single().Id.ToString();\n\n                var state = new Mock<IState>();\n                state.Setup(x => x.Name).Returns(\"State\");\n                state.Setup(x => x.Reason).Returns(\"Reason\");\n                state.Setup(x => x.SerializeData())\n                    .Returns(new Dictionary<string, string> { { \"Name\", \"Value\" } });\n\n                Commit(x => x.SetJobState(jobId, state.Object), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var job = GetTestJob(sql, jobId);\n                Assert.Equal(\"State\", job.StateName);\n                Assert.NotNull(job.StateId);\n\n                var anotherJob = GetTestJob(sql, anotherJobId);\n                Assert.Null(anotherJob.StateName);\n                Assert.Null(anotherJob.StateId);\n\n                var jobState = sql.Query($\"select * from [{Constants.DefaultSchema}].State\").Single();\n                Assert.Equal((string)jobId, jobState.JobId.ToString());\n                Assert.Equal(\"State\", jobState.Name);\n                Assert.Equal(\"Reason\", jobState.Reason);\n                Assert.NotNull(jobState.CreatedAt);\n                Assert.Equal(\"{\\\"Name\\\":\\\"Value\\\"}\", jobState.Data);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetJobState_CanBeCalledWithNullReasonAndData(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues ('', '', getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnection(sql =>\n            {\n                var jobId = sql.Query(arrangeSql).Single().Id.ToString();\n\n                var state = new Mock<IState>();\n                state.Setup(x => x.Name).Returns(\"State\");\n                state.Setup(x => x.Reason).Returns((string)null);\n                state.Setup(x => x.SerializeData()).Returns((Dictionary<string, string>)null);\n\n                Commit(x => x.SetJobState(jobId, state.Object), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var job = GetTestJob(sql, jobId);\n                Assert.Equal(\"State\", job.StateName);\n                Assert.NotNull(job.StateId);\n\n                var jobState = sql.Query($\"select * from [{Constants.DefaultSchema}].State\").Single();\n                Assert.Equal(\"State\", jobState.Name);\n                Assert.Equal(null, jobState.Reason);\n                Assert.Equal(null, jobState.Data);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddJobState_ThrowsAnException_WhenJobIdIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.AddJobState(null, new Mock<IState>().Object), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"jobId\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddJobState_ThrowsAnException_WhenStateIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.AddJobState(\"my-job\", null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"state\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddJobState_JustAddsANewRecordInATable(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues ('', '', getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnection(sql =>\n            {\n                var jobId = sql.Query(arrangeSql).Single().Id.ToString();\n\n                var state = new Mock<IState>();\n                state.Setup(x => x.Name).Returns(\"State\");\n                state.Setup(x => x.Reason).Returns(\"Reason\");\n                state.Setup(x => x.SerializeData())\n                    .Returns(new Dictionary<string, string> { { \"Name\", \"Value\" } });\n\n                Commit(x => x.AddJobState(jobId, state.Object), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var job = GetTestJob(sql, jobId);\n                Assert.Null(job.StateName);\n                Assert.Null(job.StateId);\n\n                var jobState = sql.Query($\"select * from [{Constants.DefaultSchema}].State\").Single();\n                Assert.Equal((string)jobId, jobState.JobId.ToString());\n                Assert.Equal(\"State\", jobState.Name);\n                Assert.Equal(\"Reason\", jobState.Reason);\n                Assert.NotNull(jobState.CreatedAt);\n                Assert.Equal(\"{\\\"Name\\\":\\\"Value\\\"}\", jobState.Data);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddJobState_CanBeCalledWithNullReasonAndData(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Job (InvocationData, Arguments, CreatedAt)\nvalues ('', '', getutcdate())\nselect scope_identity() as Id\";\n\n            UseConnection(sql =>\n            {\n                var jobId = sql.Query(arrangeSql).Single().Id.ToString();\n\n                var state = new Mock<IState>();\n                state.Setup(x => x.Name).Returns(\"State\");\n                state.Setup(x => x.Reason).Returns((string)null);\n                state.Setup(x => x.SerializeData()).Returns((Dictionary<string, string>)null);\n\n                Commit(x => x.AddJobState(jobId, state.Object), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var job = GetTestJob(sql, jobId);\n                Assert.Null(job.StateName);\n                Assert.Null(job.StateId);\n\n                var jobState = sql.Query($\"select * from [{Constants.DefaultSchema}].State\").Single();\n                Assert.Equal((string)jobId, jobState.JobId.ToString());\n                Assert.Equal(\"State\", jobState.Name);\n                Assert.Equal(null, jobState.Reason);\n                Assert.NotNull(jobState.CreatedAt);\n                Assert.Equal(null, jobState.Data);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToQueue_ThrowsAnException_WhenQueueIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.AddToQueue(null, \"my-job\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"queue\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToQueue_ThrowsAnException_WhenJobIdIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.AddToQueue(\"my-queue\", null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"jobId\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false, false)]\n        [InlineData(false, true)]\n        [InlineData(true, false)]\n        [InlineData(true, true)]\n        public void AddToQueue_CallsEnqueue_OnTargetPersistentQueue(bool useBatching, bool useMicrosoftDataSqlClient)\n        {\n            var correctJobQueue = new Mock<IPersistentJobQueue>();\n            var correctProvider = new Mock<IPersistentJobQueueProvider>();\n            correctProvider.Setup(x => x.GetJobQueue())\n                .Returns(correctJobQueue.Object);\n\n            _queueProviders.Add(correctProvider.Object, new[] { \"default\" });\n\n            UseConnection(sql =>\n            {\n                // External providers support DisableTransactionScope parameter in .NET Framework.\n                Commit(x => x.AddToQueue(\"default\", \"1\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope: false);\n\n                correctJobQueue.Verify(x => x.Enqueue(\n#if NETCOREAPP\n                    It.IsNotNull<System.Data.Common.DbConnection>(),\n                    It.IsNotNull<System.Data.Common.DbTransaction>(),\n#else\n                    It.IsNotNull<IDbConnection>(),\n#endif\n                    \"default\", \n                    \"1\"));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToQueue_EnqueuesAJobDirectly_WhenDefaultQueueProviderIsUsed(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            // We are relying on the fact that SqlServerJobQueue.Enqueue method will throw with a negative\n            // timeout. If we don't see this exception, and if the record is inserted, then everything is fine.\n            var options = new SqlServerStorageOptions { PrepareSchemaIfNecessary = false, CommandTimeout = TimeSpan.FromSeconds(-5) };\n            _queueProviders.Add(\n                new SqlServerJobQueueProvider(new Mock<SqlServerStorage>(\"connection=false;\", options).Object, options),\n                new [] { \"default\" });\n\n            UseConnection(sql =>\n            {\n                Commit(x => x.AddToQueue(\"default\", \"1\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].JobQueue\").Single();\n                Assert.Equal(\"1\", record.JobId.ToString());\n                Assert.Equal(\"default\", record.Queue);\n                Assert.Null(record.FetchedAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void Enqueue_ThrowsAnException_WhenTheGivenQueueIsTooLong(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(connection =>\n            {\n                var queueName = \"some-really-long-queue-name-that-should-cause-an-exception-to-be-thrown-and-not-ignored\";\n                // We are relying on the fact that SqlServerJobQueue.Enqueue method will throw with a negative\n                // timeout. If we don't see this exception, and if the record is inserted, then everything is fine.\n                var options = new SqlServerStorageOptions { PrepareSchemaIfNecessary = false, CommandTimeout = TimeSpan.FromSeconds(-5) };\n                _queueProviders.Add(\n                    new SqlServerJobQueueProvider(new Mock<SqlServerStorage>(\"connection=false;\", options).Object, options),\n                    new [] { queueName });\n\n                var exception = Assert.ThrowsAny<DbException>(() =>\n                {\n                    Commit(x => x.AddToQueue(queueName, \"1\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n                });\n\n                var record = connection.Query($\"select * from [{Constants.DefaultSchema}].JobQueue\").SingleOrDefault();\n                Assert.Null(record);\n                Assert.StartsWith(\"String or binary data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        private static dynamic GetTestJob(IDbConnection connection, string jobId)\n        {\n            return connection\n                .Query($\"select * from [{Constants.DefaultSchema}].Job where Id = @id\", new { id = jobId })\n                .Single();\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void IncrementCounter_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.IncrementCounter(null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void IncrementCounter_ThrowsAnException_WhenKeyIsTooLong(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.ThrowsAny<DbException>(() => \n                    Commit(x => x.IncrementCounter(TooLongKey), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Contains(\"data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void IncrementCounter_AddsRecordToCounterTable_WithPositiveValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x => x.IncrementCounter(\"my-key\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].Counter\").Single();\n                \n                Assert.Equal(\"my-key\", record.Key);\n                Assert.Equal(1, record.Value);\n                Assert.Equal((DateTime?)null, record.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void IncrementCounter_WithExpiry_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.IncrementCounter(null, TimeSpan.FromHours(1)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void IncrementCounter_WithExpiry_ThrowsAnException_WhenKeyIsTooLong(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.ThrowsAny<DbException>(() =>\n                    Commit(x => x.IncrementCounter(TooLongKey, TimeSpan.FromHours(1)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Contains(\"data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void IncrementCounter_WithExpiry_AddsARecord_WithExpirationTimeSet(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x => x.IncrementCounter(\"my-key\", TimeSpan.FromDays(1)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].Counter\").Single();\n\n                Assert.Equal(\"my-key\", record.Key);\n                Assert.Equal(1, record.Value);\n                Assert.NotNull(record.ExpireAt);\n\n                var expireAt = (DateTime) record.ExpireAt;\n\n                Assert.True(DateTime.UtcNow.AddHours(23) < expireAt);\n                Assert.True(expireAt < DateTime.UtcNow.AddHours(25));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void IncrementCounter_WithExistingKey_AddsAnotherRecord(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.IncrementCounter(\"my-key\");\n                    x.IncrementCounter(\"my-key\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].Counter\").Single();\n                \n                Assert.Equal(2, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void DecrementCounter_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.DecrementCounter(null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void DecrementCounter_ThrowsAnException_WhenKeyIsTooLong(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.ThrowsAny<DbException>(() =>\n                    Commit(x => x.DecrementCounter(TooLongKey), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Contains(\"data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void DecrementCounter_AddsRecordToCounterTable_WithNegativeValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x => x.DecrementCounter(\"my-key\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].Counter\").Single();\n\n                Assert.Equal(\"my-key\", record.Key);\n                Assert.Equal(-1, record.Value);\n                Assert.Equal((DateTime?)null, record.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void DecrementCounter_WithExpiry_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.DecrementCounter(null, TimeSpan.FromHours(1)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void DecrementCounter_WithExpiry_ThrowsAnException_WhenKeyIsTooLong(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.ThrowsAny<DbException>(() =>\n                    Commit(x => x.DecrementCounter(TooLongKey, TimeSpan.FromHours(1)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Contains(\"data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void DecrementCounter_WithExpiry_AddsARecord_WithExpirationTimeSet(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x => x.DecrementCounter(\"my-key\", TimeSpan.FromDays(1)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].Counter\").Single();\n\n                Assert.Equal(\"my-key\", record.Key);\n                Assert.Equal(-1, record.Value);\n                Assert.NotNull(record.ExpireAt);\n\n                var expireAt = (DateTime)record.ExpireAt;\n\n                Assert.True(DateTime.UtcNow.AddHours(23) < expireAt);\n                Assert.True(expireAt < DateTime.UtcNow.AddHours(25));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void DecrementCounter_WithExistingKey_AddsAnotherRecord(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.DecrementCounter(\"my-key\");\n                    x.DecrementCounter(\"my-key\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].Counter\").Single();\n\n                Assert.Equal(2, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.AddToSet(null, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_ThrowsAnException_WhenValueIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.AddToSet(\"my-set\", null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"value\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_ThrowsAnException_WhenKeyIsTooLong(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.ThrowsAny<DbException>(() =>\n                    Commit(x => x.AddToSet(TooLongKey, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Contains(\"data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_AddsARecord_IfThereIsNo_SuchKeyAndValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x => x.AddToSet(\"my-key\", \"my-value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].[Set]\").Single();\n\n                Assert.Equal(\"my-key\", record.Key);\n                Assert.Equal(\"my-value\", record.Value);\n                Assert.Equal(0.0, record.Score, 2);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_AddsARecord_WhenKeyIsExists_ButValuesAreDifferent(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.AddToSet(\"my-key\", \"my-value\");\n                    x.AddToSet(\"my-key\", \"another-value\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].[Set]\").Single();\n\n                Assert.Equal(2, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_DoesNotAddARecord_WhenBothKeyAndValueAreExist(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.AddToSet(\"my-key\", \"my-value\");\n                    x.AddToSet(\"my-key\", \"my-value\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].[Set]\").Single();\n                \n                Assert.Equal(1, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_WithScore_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.AddToSet(null, \"value\", 1.2D), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_WithScore_ThrowsAnException_WhenValueIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.AddToSet(\"my-set\", null, 1.2D), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"value\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_WithScore_ThrowsAnException_WhenKeyIsTooLong(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.ThrowsAny<DbException>(() =>\n                    Commit(x => x.AddToSet(TooLongKey, \"value\", 1.2D), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Contains(\"data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_WithScore_AddsARecordWithScore_WhenBothKeyAndValueAreNotExist(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x => x.AddToSet(\"my-key\", \"my-value\", 3.2), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].[Set]\").Single();\n\n                Assert.Equal(\"my-key\", record.Key);\n                Assert.Equal(\"my-value\", record.Value);\n                Assert.Equal(3.2, record.Score, 3);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_WithScore_UpdatesAScore_WhenBothKeyAndValueAreExist(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.AddToSet(\"my-key\", \"my-value\");\n                    x.AddToSet(\"my-key\", \"my-value\", 3.2);\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].[Set]\").Single();\n\n                Assert.Equal(3.2, record.Score, 3);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_WithIgnoreDupKeyOption_InsertsNonExistingValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            try\n            {\n                UseConnection(sql =>\n                {\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Set] REBUILD WITH (IGNORE_DUP_KEY = ON)\");\n\n                    Commit(x =>\n                        x.AddToSet(\"my-key\",\"my-value\", 3.2),\n                        useMicrosoftDataSqlClient,\n                        useBatching,\n                        disableTransactionScope,\n                        options => options.UseIgnoreDupKeyOption = true);\n\n                    var record = sql.Query($\"select * from [{Constants.DefaultSchema}].[Set]\").Single();\n                    Assert.Equal(3.2, record.Score, 3);\n                }, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnection(sql => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Set] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_WithIgnoreDupKeyOption_UpdatesExistingValue_WhenIgnoreDupKeyOptionIsSet(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            try\n            {\n                UseConnection(sql =>\n                {\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Set] REBUILD WITH (IGNORE_DUP_KEY = ON)\");\n                    sql.Execute($@\"insert into [{Constants.DefaultSchema}].[Set] ([Key], Value, Score) VALUES\n(N'my-key1', N'value1', 1.2),\n(N'my-key1', N'value2', 1.2),\n(N'my-key2', N'value1', 1.2)\");\n\n                    Commit(x =>\n                        x.AddToSet(\"my-key1\", \"value1\", 2.3), \n                        useMicrosoftDataSqlClient, useBatching, disableTransactionScope, options => options.UseIgnoreDupKeyOption = true);\n\n                    var record1 = sql.Query($\"select * from [{Constants.DefaultSchema}].[Set] where [Key] = N'my-key1' and Value = N'value1'\").Single();\n                    Assert.Equal(2.3, record1.Score, 3);\n\n                    var record2 = sql.Query($\"select * from [{Constants.DefaultSchema}].[Set] where [Key] = N'my-key1' and Value = N'value2'\").Single();\n                    Assert.Equal(1.2, record2.Score, 3);\n\n                    var record3 = sql.Query($\"select * from [{Constants.DefaultSchema}].[Set] where [Key] = N'my-key2' and Value = N'value1'\").Single();\n                    Assert.Equal(1.2, record3.Score, 3);\n                }, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnection(sql => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Set] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddToSet_WithIgnoreDupKeyOption_FailsToUpdateExistingValue_WhenIgnoreDupKeyOptionWasNotSet(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            try\n            {\n                UseConnection(sql =>\n                {\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Set] REBUILD WITH (IGNORE_DUP_KEY = OFF)\");\n                    sql.Execute($\"insert into [{Constants.DefaultSchema}].[Set] ([Key], Value, Score) VALUES (N'key1', N'value1', 1.2)\");\n\n                    var exception = Assert.ThrowsAny<DbException>(() =>\n                        Commit(x => \n                            x.AddToSet(\"key1\", \"value1\"),\n                            useMicrosoftDataSqlClient, useBatching, disableTransactionScope, options => options.UseIgnoreDupKeyOption = true));\n\n                    Assert.Contains(\"Violation of PRIMARY KEY\", exception.Message);\n                }, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnection(sql => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Set] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromSet_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.RemoveFromSet(null, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromSet_ThrowsAnException_WhenValueIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.RemoveFromSet(\"my-set\", null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"value\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromSet_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x => x.AddToSet(TooLongTruncatedKey, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.RemoveFromSet(TooLongKey, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [Value] from [{Constants.DefaultSchema}].[Set] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.Equal(\"value\", result.Value);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromSet_RemovesARecord_WithGivenKeyAndValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.AddToSet(\"my-key\", \"my-value\");\n                    x.RemoveFromSet(\"my-key\", \"my-value\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].[Set]\").Single();\n\n                Assert.Equal(0, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromSet_DoesNotRemoveRecord_WithSameKey_AndDifferentValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.AddToSet(\"my-key\", \"my-value\");\n                    x.RemoveFromSet(\"my-key\", \"different-value\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].[Set]\").Single();\n\n                Assert.Equal(1, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromSet_DoesNotRemoveRecord_WithSameValue_AndDifferentKey(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.AddToSet(\"my-key\", \"my-value\");\n                    x.RemoveFromSet(\"different-key\", \"my-value\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].[Set]\").Single();\n\n                Assert.Equal(1, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void InsertToList_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.InsertToList(null, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void InsertToList_ThrowsAnException_WhenValueIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.InsertToList(\"my-list\", null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"value\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void InsertToList_ThrowsAnException_WhenKeyIsTooLong(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.ThrowsAny<DbException>(() =>\n                    Commit(x => x.InsertToList(TooLongKey, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Contains(\"data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void InsertToList_AddsARecord_WithGivenValues(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x => x.InsertToList(\"my-key\", \"my-value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].List\").Single();\n\n                Assert.Equal(\"my-key\", record.Key);\n                Assert.Equal(\"my-value\", record.Value);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void InsertToList_AddsAnotherRecord_WhenBothKeyAndValueAreExist(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.InsertToList(\"my-key\", \"my-value\");\n                    x.InsertToList(\"my-key\", \"my-value\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].List\").Single();\n\n                Assert.Equal(2, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromList_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.RemoveFromList(null, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromList_ThrowsAnException_WhenValueIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.RemoveFromList(\"my-list\", null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"value\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromList_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x => x.InsertToList(TooLongTruncatedKey, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.RemoveFromList(TooLongKey, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [Value] from [{Constants.DefaultSchema}].[List] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.Equal(\"value\", result.Value);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromList_RemovesAllRecords_WithGivenKeyAndValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.InsertToList(\"my-key\", \"my-value\");\n                    x.InsertToList(\"my-key\", \"my-value\");\n                    x.RemoveFromList(\"my-key\", \"my-value\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].List\").Single();\n\n                Assert.Equal(0, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromList_DoesNotRemoveRecords_WithSameKey_ButDifferentValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.InsertToList(\"my-key\", \"my-value\");\n                    x.RemoveFromList(\"my-key\", \"different-value\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].List\").Single();\n\n                Assert.Equal(1, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveFromList_DoesNotRemoveRecords_WithSameValue_ButDifferentKey(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.InsertToList(\"my-key\", \"my-value\");\n                    x.RemoveFromList(\"different-key\", \"my-value\");\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].List\").Single();\n\n                Assert.Equal(1, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void TrimList_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.TrimList(null, 0, 1), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void TrimList_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x => x.InsertToList(TooLongTruncatedKey, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.TrimList(TooLongKey, 1, 2), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [Value] from [{Constants.DefaultSchema}].[List] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.Equal(\"value\", result.Value);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void TrimList_TrimsAList_ToASpecifiedRange(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.InsertToList(\"my-key\", \"0\");\n                    x.InsertToList(\"my-key\", \"1\");\n                    x.InsertToList(\"my-key\", \"2\");\n                    x.InsertToList(\"my-key\", \"3\");\n                    x.TrimList(\"my-key\", 1, 2);\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var records = sql.Query($\"select * from [{Constants.DefaultSchema}].List\").ToArray();\n\n                Assert.Equal(2, records.Length);\n                Assert.Equal(\"1\", records[0].Value);\n                Assert.Equal(\"2\", records[1].Value);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void TrimList_RemovesRecordsToEnd_IfKeepAndingAt_GreaterThanMaxElementIndex(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.InsertToList(\"my-key\", \"0\");\n                    x.InsertToList(\"my-key\", \"1\");\n                    x.InsertToList(\"my-key\", \"2\");\n                    x.TrimList(\"my-key\", 1, 100);\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].List\").Single();\n\n                Assert.Equal(2, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void TrimList_RemovesAllRecords_WhenStartingFromValue_GreaterThanMaxElementIndex(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.InsertToList(\"my-key\", \"0\");\n                    x.TrimList(\"my-key\", 1, 100);\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].List\").Single();\n\n                Assert.Equal(0, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void TrimList_RemovesAllRecords_IfStartFromGreaterThanEndingAt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.InsertToList(\"my-key\", \"0\");\n                    x.TrimList(\"my-key\", 1, 0);\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].List\").Single();\n\n                Assert.Equal(0, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void TrimList_RemovesRecords_OnlyOfAGivenKey(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x =>\n                {\n                    x.InsertToList(\"my-key\", \"0\");\n                    x.TrimList(\"another-key\", 1, 0);\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var recordCount = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].List\").Single();\n\n                Assert.Equal(1, recordCount);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetRangeInHash_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.SetRangeInHash(null, new Dictionary<string, string>()), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetRangeInHash_ThrowsAnException_WhenKeyValuePairsArgumentIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.SetRangeInHash(\"some-hash\", null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"keyValuePairs\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetRangeInHash_ThrowsAnException_WhenKeyIsTooLong(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.ThrowsAny<DbException>(() =>\n                    Commit(x => x.SetRangeInHash(\n                        TooLongKey,\n                        new Dictionary<string, string> { { \"field\", \"value\" } }), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Contains(\"data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetRangeInHash_MergesAllRecords(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x => x.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                {\n                    { \"Key1\", \"Value1\" },\n                    { \"Key2\", \"Value2\" }\n                }), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var result = sql.Query(\n                    $\"select * from [{Constants.DefaultSchema}].Hash where [Key] = @key\",\n                    new { key = \"some-hash\" })\n                    .ToDictionary(x => (string)x.Field, x => (string)x.Value);\n\n                Assert.Equal(\"Value1\", result[\"Key1\"]);\n                Assert.Equal(\"Value2\", result[\"Key2\"]);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetRangeInHash_CanSetANullValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Commit(x => x.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                {\n                    { \"Key1\", null }\n                }), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var result = sql.Query(\n                        $\"select * from [{Constants.DefaultSchema}].Hash where [Key] = @key\",\n                        new { key = \"some-hash\" })\n                    .ToDictionary(x => (string)x.Field, x => (string)x.Value);\n\n                Assert.Null(result[\"Key1\"]);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetRangeInHash_WithIgnoreDupKeyOption_InsertsNonExistingValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            try\n            {\n                UseConnection(sql =>\n                {\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Hash] REBUILD WITH (IGNORE_DUP_KEY = ON)\");\n\n                    Commit(x => x.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                    {\n                        { \"key\", \"value\" }\n                    }), useMicrosoftDataSqlClient, useBatching, disableTransactionScope, options => options.UseIgnoreDupKeyOption = true);\n\n                    var result = sql\n                        .Query($\"select * from [{Constants.DefaultSchema}].Hash where [Key] = N'some-hash'\")\n                        .ToDictionary(x => (string)x.Field, x => (string)x.Value);\n\n                    Assert.Equal(\"value\", result[\"key\"]);\n                }, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnection(sql => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Hash] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetRangeInHash_WithIgnoreDupKeyOption_UpdatesExistingValue_WhenIgnoreDupKeyOptionIsSet(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            try\n            {\n                UseConnection(sql =>\n                {\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Hash] REBUILD WITH (IGNORE_DUP_KEY = ON)\");\n                    sql.Execute($@\"insert into [{Constants.DefaultSchema}].Hash([Key], Field, Value) VALUES\n(N'some-hash', N'key1', N'value1'),\n(N'some-hash', N'key2', N'value1'),\n(N'othr-hash', N'key1', N'value1')\");\n\n                    Commit(x => x.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                    {\n                        { \"key1\", \"value2\" }\n                    }), useMicrosoftDataSqlClient, useBatching, disableTransactionScope, options => options.UseIgnoreDupKeyOption = true);\n\n                    var someResult = sql\n                        .Query($\"select * from [{Constants.DefaultSchema}].Hash where [Key] = N'some-hash'\")\n                        .ToDictionary(x => (string)x.Field, x => (string)x.Value);\n\n                    Assert.Equal(\"value2\", someResult[\"key1\"]);\n                    Assert.Equal(\"value1\", someResult[\"key2\"]);\n\n                    var othrResult = sql\n                        .Query($\"select * from [{Constants.DefaultSchema}].Hash where [Key] = N'othr-hash'\")\n                        .ToDictionary(x => (string)x.Field, x => (string)x.Value);\n\n                    Assert.Equal(\"value1\", othrResult[\"key1\"]);\n                }, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnection(sql => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Hash] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetRangeInHash_WithIgnoreDupKeyOption_FailsToUpdateExistingValue_WhenIgnoreDupKeyOptionWasNotSet(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            try\n            {\n                UseConnection(sql =>\n                {\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Hash] REBUILD WITH (IGNORE_DUP_KEY = OFF)\");\n                    sql.Execute($\"insert into [{Constants.DefaultSchema}].Hash([Key], Field, Value) VALUES (N'some-hash', N'key', N'value1')\");\n\n                    var exception = Assert.ThrowsAny<DbException>(() =>\n                        Commit(x => x.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                        {\n                            { \"key\", \"value2\" }\n                        }), useMicrosoftDataSqlClient, useBatching, disableTransactionScope, options => options.UseIgnoreDupKeyOption = true));\n\n                    Assert.Contains(\"Violation of PRIMARY KEY\", exception.Message);\n                }, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnection(sql => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Hash] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveHash_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.RemoveHash(null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveHash_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x => x.SetRangeInHash(\n                    TooLongTruncatedKey,\n                    new Dictionary<string, string> {{ \"field\", \"value\" }}), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.RemoveHash(TooLongKey), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [Value] from [{Constants.DefaultSchema}].[Hash] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.Equal(\"value\", result.Value);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveHash_RemovesAllHashRecords(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x => x.SetRangeInHash(\"some-hash\", new Dictionary<string, string>\n                {\n                    { \"Key1\", \"Value1\" },\n                    { \"Key2\", \"Value2\" }\n                }), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.RemoveHash(\"some-hash\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var count = sql.Query<int>($\"select count(*) from [{Constants.DefaultSchema}].Hash\").Single();\n                Assert.Equal(0, count);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddRangeToSet_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.AddRangeToSet(null, new List<string>()), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddRangeToSet_ThrowsAnException_WhenKeyIsTooLong(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.ThrowsAny<DbException>(() =>\n                    Commit(x => x.AddRangeToSet(\n                        TooLongKey,\n                        new List<string> { \"field\" }), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Contains(\"data would be truncated\", exception.Message);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddRangeToSet_ThrowsAnException_WhenItemsValueIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.AddRangeToSet(\"my-set\", null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"items\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddRangeToSet_AddsAllItems_ToAGivenSet(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var items = new List<string> { \"1\", \"2\", \"3\" };\n\n                Commit(x => x.AddRangeToSet(\"my-set\", items), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var records = sql.Query<string>($\"select [Value] from [{Constants.DefaultSchema}].[Set] where [Key] = N'my-set'\");\n                Assert.Equal(items, records);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddRangeToSet_DoesNotFailWithException_WhenIgnoreDupKeyOptionIsSet(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            try\n            {\n                UseConnection(sql =>\n                {\n                    sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Set] REBUILD WITH (IGNORE_DUP_KEY = ON)\");\n                    sql.Execute($@\"insert into [{Constants.DefaultSchema}].[Set] ([Key], Value, Score) VALUES\n(N'my-set', N'2', 1.2),\n(N'my-set', N'3', 1.2),\n(N'my-set', N'4', 1.2)\");\n\n                    var items = new List<string> { \"1\", \"2\", \"3\" };\n\n                    Commit(x => x.AddRangeToSet(\"my-set\", items), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                    var records = sql.Query<string>($\"select [Value] from [{Constants.DefaultSchema}].[Set] where [Key] = N'my-set'\");\n                    Assert.Equal(new List<string> { \"1\", \"2\", \"3\", \"4\" }, records);\n                }, useMicrosoftDataSqlClient);\n            }\n            finally\n            {\n                UseConnection(sql => sql.Execute($\"ALTER TABLE [{Constants.DefaultSchema}].[Set] REBUILD WITH (IGNORE_DUP_KEY = ON)\"), useMicrosoftDataSqlClient);\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveSet_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.RemoveSet(null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveSet_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x => x.AddToSet(TooLongTruncatedKey, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.RemoveSet(TooLongKey), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [Value] from [{Constants.DefaultSchema}].[Set] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.Equal(\"value\", result.Value);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void RemoveSet_RemovesASet_WithAGivenKey(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [Score]) values (@key, @value, 0.0)\";\n\n            UseConnection(sql =>\n            {\n                sql.Execute(arrangeSql, new []\n                {\n                    new { key = \"set-1\", value = \"1\" },\n                    new { key = \"set-2\", value = \"1\" }\n                });\n\n                Commit(x => x.RemoveSet(\"set-1\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].[Set]\").Single();\n                Assert.Equal(\"set-2\", record.Key);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireHash_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.ExpireHash(null, TimeSpan.FromMinutes(5)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireHash_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x => x.SetRangeInHash(\n                    TooLongTruncatedKey,\n                    new Dictionary<string, string> {{ \"field\", \"value\" }}), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.ExpireHash(TooLongKey, TimeSpan.FromHours(1)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [ExpireAt] from [{Constants.DefaultSchema}].[Hash] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.Null(result.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireHash_SetsExpirationTimeOnAHash_WithGivenKey(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Hash ([Key], [Field])\nvalues (@key, @field)\";\n\n            UseConnection(sql =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"hash-1\", field = \"field\" },\n                    new { key = \"hash-2\", field = \"field\" }\n                });\n\n                // Act\n                Commit(x => x.ExpireHash(\"hash-1\", TimeSpan.FromMinutes(60)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var records = sql.Query($\"select * from [{Constants.DefaultSchema}].Hash\").ToDictionary(x => (string)x.Key, x => (DateTime?)x.ExpireAt);\n                Assert.True(DateTime.UtcNow.AddMinutes(59) < records[\"hash-1\"]);\n                Assert.True(records[\"hash-1\"] < DateTime.UtcNow.AddMinutes(61));\n                Assert.Null(records[\"hash-2\"]);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireSet_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.ExpireSet(null, TimeSpan.FromSeconds(45)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireSet_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x => x.AddToSet(TooLongTruncatedKey, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.ExpireSet(TooLongKey, TimeSpan.FromHours(1)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [ExpireAt] from [{Constants.DefaultSchema}].[Set] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.Null(result.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireSet_SetsExpirationTime_OnASet_WithGivenKey(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [Score])\nvalues (@key, @value, 0.0)\";\n\n            UseConnection(sql =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"set-1\", value = \"1\" },\n                    new { key = \"set-2\", value = \"1\" }\n                });\n\n                // Act\n                Commit(x => x.ExpireSet(\"set-1\", TimeSpan.FromMinutes(60)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var records = sql.Query($\"select * from [{Constants.DefaultSchema}].[Set]\").ToDictionary(x => (string)x.Key, x => (DateTime?)x.ExpireAt);\n                Assert.True(DateTime.UtcNow.AddMinutes(59) < records[\"set-1\"]);\n                Assert.True(records[\"set-1\"] < DateTime.UtcNow.AddMinutes(61));\n                Assert.Null(records[\"set-2\"]);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireList_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.ExpireList(null, TimeSpan.FromSeconds(45)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireList_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x => x.InsertToList(TooLongTruncatedKey, \"value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.ExpireList(TooLongKey, TimeSpan.FromHours(1)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [ExpireAt] from [{Constants.DefaultSchema}].[List] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.Null(result.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireList_SetsExpirationTime_OnAList_WithGivenKey(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[List] ([Key]) values (@key)\";\n\n            UseConnection(sql =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"list-1\", value = \"1\" },\n                    new { key = \"list-2\", value = \"1\" }\n                });\n\n                // Act\n                Commit(x => x.ExpireList(\"list-1\", TimeSpan.FromMinutes(60)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var records = sql.Query($\"select * from [{Constants.DefaultSchema}].[List]\").ToDictionary(x => (string)x.Key, x => (DateTime?)x.ExpireAt);\n                Assert.True(DateTime.UtcNow.AddMinutes(59) < records[\"list-1\"]);\n                Assert.True(records[\"list-1\"] < DateTime.UtcNow.AddMinutes(61));\n                Assert.Null(records[\"list-2\"]);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistHash_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.PersistHash(null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistHash_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x =>\n                {\n                    x.SetRangeInHash(TooLongTruncatedKey, new Dictionary<string, string> { { \"field\", \"value\" } });\n                    x.ExpireHash(TooLongTruncatedKey, TimeSpan.FromHours(1));\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.PersistHash(TooLongKey), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [ExpireAt] from [{Constants.DefaultSchema}].[Hash] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.NotNull(result.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistHash_ClearsExpirationTime_OnAGivenHash(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].Hash ([Key], [Field], [ExpireAt])\nvalues (@key, @field, @expireAt)\";\n\n            UseConnection(sql =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"hash-1\", field = \"field\", expireAt = DateTime.UtcNow.AddDays(1) },\n                    new { key = \"hash-2\", field = \"field\", expireAt = DateTime.UtcNow.AddDays(1) }\n                });\n\n                // Act\n                Commit(x => x.PersistHash(\"hash-1\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var records = sql.Query($\"select * from [{Constants.DefaultSchema}].Hash\").ToDictionary(x => (string)x.Key, x => (DateTime?)x.ExpireAt);\n                Assert.Null(records[\"hash-1\"]);\n                Assert.NotNull(records[\"hash-2\"]);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistSet_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.PersistSet(null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistSet_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x =>\n                {\n                    x.AddToSet(TooLongTruncatedKey, \"value\");\n                    x.ExpireSet(TooLongTruncatedKey, TimeSpan.FromHours(1));\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.PersistSet(TooLongKey), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [ExpireAt] from [{Constants.DefaultSchema}].[Set] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.NotNull(result.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistSet_ClearsExpirationTime_OnAGivenHash(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[Set] ([Key], [Value], [ExpireAt], [Score])\nvalues (@key, @value, @expireAt, 0.0)\";\n\n            UseConnection(sql =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"set-1\", value = \"1\", expireAt = DateTime.UtcNow.AddDays(1) },\n                    new { key = \"set-2\", value = \"1\", expireAt = DateTime.UtcNow.AddDays(1) }\n                });\n\n                // Act\n                Commit(x => x.PersistSet(\"set-1\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var records = sql.Query($\"select * from [{Constants.DefaultSchema}].[Set]\").ToDictionary(x => (string)x.Key, x => (DateTime?)x.ExpireAt);\n                Assert.Null(records[\"set-1\"]);\n                Assert.NotNull(records[\"set-2\"]);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistList_ThrowsAnException_WhenKeyIsNull(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                    () => Commit(x => x.PersistList(null), useMicrosoftDataSqlClient, useBatching, disableTransactionScope));\n\n                Assert.Equal(\"key\", exception.ParamName);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistList_DoesNotTruncateKey_BeforeUsingIt(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                // Arrange\n                Commit(x =>\n                {\n                    x.InsertToList(TooLongTruncatedKey, \"value\");\n                    x.ExpireList(TooLongTruncatedKey, TimeSpan.FromHours(1));\n                }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Act\n                Commit(x => x.PersistList(TooLongKey), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var result = sql.Query(\n                    $\"select [ExpireAt] from [{Constants.DefaultSchema}].[List] where [Key] = @key\",\n                    new { key = TooLongTruncatedKey }).Single();\n\n                Assert.NotNull(result.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistList_ClearsExpirationTime_OnAGivenHash(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\ninsert into [{Constants.DefaultSchema}].[List] ([Key], [ExpireAt])\nvalues (@key, @expireAt)\";\n\n            UseConnection(sql =>\n            {\n                // Arrange\n                sql.Execute(arrangeSql, new[]\n                {\n                    new { key = \"list-1\", expireAt = DateTime.UtcNow.AddDays(1) },\n                    new { key = \"list-2\", expireAt = DateTime.UtcNow.AddDays(1) }\n                });\n\n                // Act\n                Commit(x => x.PersistList(\"list-1\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                // Assert\n                var records = sql.Query($\"select * from [{Constants.DefaultSchema}].[List]\").ToDictionary(x => (string)x.Key, x => (DateTime?)x.ExpireAt);\n                Assert.Null(records[\"list-1\"]);\n                Assert.NotNull(records[\"list-2\"]);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void InsertToList_HandlesListIdCanExceedInt32Max(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                sql.Query($\"DBCC CHECKIDENT('[{Constants.DefaultSchema}].List', RESEED, {int.MaxValue + 1L});\");\n\n                Commit(x => x.InsertToList(\"my-key\", \"my-value\"), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var record = sql.Query($\"select * from [{Constants.DefaultSchema}].List\").Single();\n\n                Assert.True(int.MaxValue < record.Id);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void ExpireJob_SetsJobExpirationData_WhenJobIdIsLongValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].Job ON\ninsert into [{Constants.DefaultSchema}].Job (Id, InvocationData, Arguments, CreatedAt)\nvalues (@jobId, '', '', getutcdate())\";\n\n            UseConnection(sql =>\n            {\n                sql.Query(\n                    arrangeSql,\n                    new { jobId = int.MaxValue + 1L });\n\n                Commit(x => x.ExpireJob((int.MaxValue + 1L).ToString(), TimeSpan.FromDays(1)), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var job = GetTestJob(sql, (int.MaxValue + 1L).ToString());\n                Assert.True(DateTime.UtcNow.AddMinutes(-1) < job.ExpireAt && job.ExpireAt <= DateTime.UtcNow.AddDays(2));\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void PersistJob_ClearsTheJobExpirationData_WhenJobIdIsLongValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].Job ON\ninsert into [{Constants.DefaultSchema}].Job (Id, InvocationData, Arguments, CreatedAt, ExpireAt)\nvalues (@jobId, '', '', getutcdate(), getutcdate())\";\n\n            UseConnection(sql =>\n            {\n                sql.Query(\n                    arrangeSql,\n                    new { jobId = int.MaxValue + 1L });\n\n                Commit(x => x.PersistJob((int.MaxValue + 1L).ToString()), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var job = GetTestJob(sql, (int.MaxValue + 1L).ToString());\n                Assert.Null(job.ExpireAt);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void SetJobState_WorksCorrect_WhenJobIdIsLongValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].Job ON\ninsert into [{Constants.DefaultSchema}].Job (Id, InvocationData, Arguments, CreatedAt)\nvalues (@jobId, '', '', getutcdate())\";\n\n            UseConnection(sql =>\n            {\n                sql.Query(\n                    arrangeSql,\n                    new { jobId = int.MaxValue + 1L });\n\n                var state = new Mock<IState>();\n                state.Setup(x => x.Name).Returns(\"State\");\n\n                Commit(x => x.SetJobState((int.MaxValue + 1L).ToString(), state.Object), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n                var job = GetTestJob(sql, (int.MaxValue + 1L).ToString());\n\n                var jobState = sql.Query($\"select * from [{Constants.DefaultSchema}].State\").Single();\n                Assert.Equal(int.MaxValue + 1L, jobState.JobId);\n                Assert.Equal(job.StateId, jobState.Id);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AddJobState_AddsAState_WhenJobIdIsLongValue(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            var arrangeSql = $@\"\nSET IDENTITY_INSERT [{Constants.DefaultSchema}].Job ON\ninsert into [{Constants.DefaultSchema}].Job (Id, InvocationData, Arguments, CreatedAt)\nvalues (@jobId, '', '', getutcdate())\";\n\n            UseConnection(sql =>\n            {\n                sql.Query(\n                   arrangeSql,\n                   new { jobId = int.MaxValue + 1L });\n\n                var state = new Mock<IState>();\n                state.Setup(x => x.Name).Returns(\"State\");\n\n                Commit(x => x.AddJobState((int.MaxValue + 1L).ToString(), state.Object), useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n\n                var jobState = sql.Query($\"select * from [{Constants.DefaultSchema}].State\").Single();\n\n                Assert.Equal(int.MaxValue + 1L, jobState.JobId);\n            }, useMicrosoftDataSqlClient);\n        }\n        \n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AcquireDistributedLock_ThrowsAnException_WhenResourceIsNullOrEmpty(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseSqlServerTransaction(transaction =>\n            {\n                var exception = Assert.Throws<ArgumentNullException>(\n                () => transaction.AcquireDistributedLock(\"\", TimeSpan.FromSeconds(15)));\n\n                Assert.Equal(\"resource\", exception.ParamName);\n            }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AcquireDistributedLock_AcquiresExclusiveApplicationLock_OnSession(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            using (var sql = ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient))\n            {\n                var options = CreateOptions(useBatching, disableTransactionScope);\n                var storage = new SqlServerStorage(sql, options);\n\n                using (var connection = new SqlServerConnection(storage))\n                using (var transaction = new SqlServerWriteOnlyTransaction(connection))\n                {\n                    transaction.AcquireDistributedLock(\"hello\", TimeSpan.FromSeconds(15));\n                    var lockMode = sql.Query<string>(\n                        $\"select applock_mode('public', '{Constants.DefaultSchema}:hello', 'session')\").Single();\n\n                    Assert.Equal(\"Exclusive\", lockMode);\n                    transaction.Commit();\n                }\n            }\n        }\n\n        [Theory, CleanDatabase]\n        [InlineData(false)]\n        [InlineData(true)]\n        public void AcquireDistributedLock_ThrowsAnException_IfLockCanNotBeGranted(bool useMicrosoftDataSqlClient)\n        {\n            var releaseLock = new ManualResetEventSlim(false);\n            var lockAcquired = new ManualResetEventSlim(false);\n\n            var thread = new Thread(\n                () => UseSqlServerTransaction(transaction1 =>\n                {\n                    try\n                    {\n                        transaction1.AcquireDistributedLock(\"exclusive\", TimeSpan.Zero);\n\n                        lockAcquired.Set();\n                        if (!releaseLock.Wait(TimeSpan.FromSeconds(30)))\n                            throw new TimeoutException(\"Waiting for too long in transaction1 block\");\n\n                        transaction1.Commit();\n                    }\n                    catch (Exception ex)\n                    {\n                        try\n                        {\n                            Assert.True(false, ex.ToString());\n                        }\n                        catch\n                        {\n                            // Need only message\n                        }\n                    }\n                }, useMicrosoftDataSqlClient, useBatching: true, disableTransactionScope: false));\n            thread.Start();\n\n            if (!lockAcquired.Wait(TimeSpan.FromSeconds(30)))\n            {\n                throw new TimeoutException(\"Waiting for too long in transaction2 block\");\n            }\n\n            UseSqlServerTransaction(transaction2 =>\n            {\n                Assert.Throws<DistributedLockTimeoutException>(\n                    () => transaction2.AcquireDistributedLock(\"exclusive\", TimeSpan.FromSeconds(1)));\n            }, useMicrosoftDataSqlClient, useBatching: true, disableTransactionScope: false);\n\n            releaseLock.Set();\n            thread.Join();\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n\n        public void AcquireDistributedLock_TransactionCommit_ReleasesExclusiveApplicationLock(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var options = CreateOptions(useBatching, disableTransactionScope);\n                var storage = new SqlServerStorage(sql, options);\n\n                using (var connection = new SqlServerConnection(storage))\n                using (var transaction = new SqlServerWriteOnlyTransaction(connection))\n                {\n                    transaction.AcquireDistributedLock(\"hello\", TimeSpan.FromSeconds(15));\n                    transaction.Commit();\n                }\n\n                var lockMode = sql.Query<string>($\"select applock_mode('public', '{Constants.DefaultSchema}:hello', 'session')\").Single();\n\n                Assert.Equal(\"NoLock\", lockMode);\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AcquireDistributedLock_TransactionCommit_DoesNotReleaseLock_IfItsOwnedByConnection(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseConnection(sql =>\n            {\n                var options = CreateOptions(useBatching, disableTransactionScope);\n                var storage = new SqlServerStorage(sql, options);\n\n                using (var connection = new SqlServerConnection(storage))\n                using (connection.AcquireDistributedLock(\"hello\", TimeSpan.FromSeconds(15)))\n                {\n                    using (var transaction = new SqlServerWriteOnlyTransaction(connection))\n                    {\n                        transaction.AcquireDistributedLock(\"hello\", TimeSpan.FromSeconds(15));\n                        transaction.Commit();\n                    }\n\n                    var lockMode = sql\n                        .Query<string>($\"select applock_mode('public', '{Constants.DefaultSchema}:hello', 'session')\")\n                        .Single();\n\n                    Assert.Equal(\"Exclusive\", lockMode);\n                }\n            }, useMicrosoftDataSqlClient);\n        }\n\n        [Theory, CleanDatabase]\n        [MemberData(nameof(GetConfiguration))]\n        public void AcquireDistributedLock_IsReentrant_FromTheSameTransaction_OnTheSameResource(bool useBatching, bool useMicrosoftDataSqlClient, bool disableTransactionScope)\n        {\n            UseSqlServerTransaction(transaction =>\n            {\n                transaction.AcquireDistributedLock(\"hello\", TimeSpan.FromSeconds(15));\n                transaction.AcquireDistributedLock(\"hello\", TimeSpan.FromSeconds(15));\n                transaction.Commit();\n            }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope);\n        }\n\n        private static void UseConnection(Action<DbConnection> action, bool useMicrosoftDataSqlClient)\n        {\n            using (var connection = ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient))\n            {\n                action(connection);\n            }\n        }\n\n        private void UseSqlServerConnection(\n            Action<SqlServerConnection> action,\n            bool useMicrosoftDataSqlClient,\n            bool useBatching,\n            bool disableTransactionScope,\n            Action<SqlServerStorageOptions> optionsAction = null)\n        {\n            var options = CreateOptions(useBatching, disableTransactionScope);\n            optionsAction?.Invoke(options);\n\n            var storage = new Mock<SqlServerStorage>((Func<DbConnection>) (() => ConnectionUtils.CreateConnection(useMicrosoftDataSqlClient)), options);\n            storage.Setup(x => x.QueueProviders).Returns(_queueProviders);\n\n            using (var connection = new SqlServerConnection(storage.Object))\n            {\n                action(connection);\n            }\n        }\n\n        private void UseSqlServerTransaction(\n            Action<SqlServerWriteOnlyTransaction> action,\n            bool useMicrosoftDataSqlClient,\n            bool useBatching,\n            bool disableTransactionScope,\n            Action<SqlServerStorageOptions> optionsAction = null)\n        {\n            UseSqlServerConnection(connection =>\n            {\n                using (var transaction = new SqlServerWriteOnlyTransaction(connection))\n                {\n                    action(transaction);\n                }\n            }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope, optionsAction);\n        }\n\n        private void Commit(\n            Action<SqlServerWriteOnlyTransaction> action,\n            bool useMicrosoftDataSqlClient,\n            bool useBatching,\n            bool disableTransactionScope,\n            Action<SqlServerStorageOptions> optionsAction = null)\n        {\n            UseSqlServerTransaction(transaction =>\n            {\n                action(transaction);\n                transaction.Commit();\n            }, useMicrosoftDataSqlClient, useBatching, disableTransactionScope, optionsAction);\n        }\n\n        private static SqlServerStorageOptions CreateOptions(bool useBatching, bool disableTransactionScope)\n        {\n            return new SqlServerStorageOptions\n            {\n#if NET452 || NET461\n                DisableTransactionScope = disableTransactionScope,\n#endif\n                CommandBatchMaxTimeout = useBatching ? (TimeSpan?)TimeSpan.FromMinutes(5) : null\n            };\n        }\n\n        private static bool IsRunningOnWindows()\n        {\n            return Environment.OSVersion.Platform == PlatformID.Win32NT;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/StorageOptionsFacts.cs",
    "content": "﻿using System;\nusing Xunit;\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class StorageOptionsFacts\n    {\n        [Fact]\n        public void Ctor_SetsTheDefaultOptions()\n        {\n            var options = new SqlServerStorageOptions();\n\n            Assert.True(options.QueuePollInterval == TimeSpan.Zero);\n#pragma warning disable 618\n            Assert.True(options.InvisibilityTimeout > TimeSpan.Zero);\n#pragma warning restore 618\n            Assert.True(options.JobExpirationCheckInterval > TimeSpan.Zero);\n            Assert.True(options.PrepareSchemaIfNecessary);\n        }\n\n        [Fact]\n        public void Set_QueuePollInterval_DoesNotThrow_WhenGivenIntervalIsEqualToZero()\n        {\n            var options = new SqlServerStorageOptions();\n            options.QueuePollInterval = TimeSpan.Zero;\n        }\n\n        [Fact]\n        public void Set_QueuePollInterval_ShouldThrowAnException_WhenGivenIntervalIsNegative()\n        {\n            var options = new SqlServerStorageOptions();\n            Assert.Throws<ArgumentException>(\n                () => options.QueuePollInterval = TimeSpan.FromSeconds(-1));\n        }\n\n        [Fact]\n        public void Set_QueuePollInterval_SetsTheValue()\n        {\n            var options = new SqlServerStorageOptions { QueuePollInterval = TimeSpan.FromSeconds(1) };\n            Assert.Equal(TimeSpan.FromSeconds(1), options.QueuePollInterval);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/Utils/CleanDatabaseAttribute.cs",
    "content": "﻿extern alias ReferencedDapper;\n\nusing System;\nusing System.Data.SqlClient;\nusing System.Reflection;\nusing System.Threading;\nusing ReferencedDapper::Dapper;\nusing Xunit.Sdk;\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public class CleanDatabaseAttribute : BeforeAfterTestAttribute\n    {\n        private static readonly object GlobalLock = new object();\n        private static bool _sqlObjectInstalled;\n        \n        public CleanDatabaseAttribute()\n        {\n        }\n\n        public override void Before(MethodInfo methodUnderTest)\n        {\n            Monitor.Enter(GlobalLock);\n\n            if (!_sqlObjectInstalled)\n            {\n                CreateAndInitializeDatabaseIfNotExists();\n                _sqlObjectInstalled = true;\n            }\n\n            using (var connection = new SqlConnection(\n                ConnectionUtils.GetConnectionString()))\n            {\n                connection.Execute(@\"\ntruncate table [HangFire].[AggregatedCounter];\ntruncate table [HangFire].[Counter];\ntruncate table [HangFire].[Hash];\ndelete from [HangFire].[Job];\ndbcc checkident('HangFire.Job', RESEED, 0);\ntruncate table [HangFire].[List];\ntruncate table [HangFire].[JobQueue];\ntruncate table [HangFire].[Set];\ntruncate table [HangFire].[Server];\n\");\n            }\n        }\n\n        public override void After(MethodInfo methodUnderTest)\n        {\n            Monitor.Exit(GlobalLock);\n        }\n\n        private static void CreateAndInitializeDatabaseIfNotExists()\n        {\n            var recreateDatabaseSql = String.Format(\n                @\"if db_id('{0}') is null create database [{0}] COLLATE SQL_Latin1_General_CP1_CS_AS\",\n                ConnectionUtils.GetDatabaseName());\n\n            using (var connection = new SqlConnection(\n                ConnectionUtils.GetMasterConnectionString()))\n            {\n                connection.Execute(recreateDatabaseSql);\n            }\n\n            using (var connection = new SqlConnection(\n                ConnectionUtils.GetConnectionString()))\n            {\n                SqlServerObjectsInstaller.Install(connection, null, true);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/Utils/CleanSerializerSettingsAttribute.cs",
    "content": "﻿using System.Reflection;\nusing Hangfire.Common;\nusing Newtonsoft.Json;\nusing Xunit.Sdk;\n\nnamespace Hangfire.SqlServer.Tests\n{\n    internal sealed class CleanSerializerSettingsAttribute : BeforeAfterTestAttribute\n    {\n        public override void Before(MethodInfo methodUnderTest)\n        {\n            ClearSettings();\n        }\n\n        public override void After(MethodInfo methodUnderTest)\n        {\n            ClearSettings();\n        }\n\n        private static void ClearSettings()\n        {\n#pragma warning disable 618\n            JobHelper.SetSerializerSettings(null);\n#pragma warning restore 618\n            GlobalConfiguration.Configuration.UseSerializerSettings(null);\n#if !NET452 && !NET461\n            JsonConvert.DefaultSettings = null;\n#endif\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/Utils/ConnectionUtils.cs",
    "content": "﻿using System;\nusing System.Data.Common;\nusing System.Data.SqlClient;\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public static class ConnectionUtils\n    {\n        private const string DatabaseVariable = \"Hangfire_SqlServer_DatabaseName\";\n        private const string ConnectionStringTemplateVariable \n            = \"Hangfire_SqlServer_ConnectionStringTemplate\";\n\n        private const string MasterDatabaseName = \"master\";\n        private const string DefaultDatabaseName =\n#if NET452\n                \"Hangfire.SqlServer.Tests.net452\"\n#elif NET461\n                \"Hangfire.SqlServer.Tests.net461\"\n#elif NETCOREAPP3_1\n                \"Hangfire.SqlServer.Tests.netcoreapp3_1\"\n#elif NET6_0\n                \"Hangfire.SqlServer.Tests.net6_0\"\n#elif NET8_0\n                \"Hangfire.SqlServer.Tests.net8_0\"\n#else\n                \"Hangfire.SqlServer.Tests\"\n#endif\n            ;\n        private const string DefaultConnectionStringTemplate\n            = @\"Server=.\\;Database={0};Trusted_Connection=True;TrustServerCertificate=True;\";\n\n        public static string GetDatabaseName()\n        {\n            return Environment.GetEnvironmentVariable(DatabaseVariable) ?? DefaultDatabaseName;\n        }\n\n        public static string GetMasterConnectionString()\n        {\n            return String.Format(GetConnectionStringTemplate(), MasterDatabaseName);\n        }\n\n        public static string GetConnectionString()\n        {\n            return String.Format(GetConnectionStringTemplate(), GetDatabaseName());\n        }\n\n        private static string GetConnectionStringTemplate()\n        {\n            return Environment.GetEnvironmentVariable(ConnectionStringTemplateVariable)\n                   ?? DefaultConnectionStringTemplate;\n        }\n\n        public static DbConnection CreateConnection(bool microsoftDataSqlClient)\n        {\n            var connection =\n#if !NET452 && !NET461\n                microsoftDataSqlClient ? (DbConnection)new Microsoft.Data.SqlClient.SqlConnection(GetConnectionString()) :\n#endif\n                new SqlConnection(GetConnectionString());\n\n            connection.Open();\n\n            return connection;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/Utils/SerializerSettingsHelper.cs",
    "content": "﻿using System.Runtime.Serialization.Formatters;\nusing Newtonsoft.Json;\n\nnamespace Hangfire.SqlServer.Tests\n{\n    public static class SerializerSettingsHelper\n    {\n        public static JsonSerializerSettings DangerousSettings = new JsonSerializerSettings\n        {\n            TypeNameHandling = TypeNameHandling.All,\n\n#if NET452 || NET461 || NETCOREAPP3_1\n            TypeNameAssemblyFormat = FormatterAssemblyStyle.Full,\n#else\n            TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full,\n#endif\n\n            DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,\n\n            Formatting = Formatting.Indented,\n\n            NullValueHandling = NullValueHandling.Ignore,\n            DefaultValueHandling = DefaultValueHandling.Ignore,\n        };\n    }\n}\n"
  },
  {
    "path": "tests/Hangfire.SqlServer.Tests/packages.lock.json",
    "content": "{\n  \"version\": 1,\n  \"dependencies\": {\n    \".NETFramework,Version=v4.5.2\": {\n      \"Dapper\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.60.6, )\",\n        \"resolved\": \"1.60.6\",\n        \"contentHash\": \"mmnJNhKMeF2KhvVXDoVQlFxre8aJAo71YBJrKqFlvuqzYC2QiXUq94/GCDBJzU7paq4GqpkV2glw3308TcGibw==\"\n      },\n      \"Microsoft.NET.Test.Sdk\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[17.8.0, )\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net452\": \"1.0.3\"\n        }\n      },\n      \"Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.10.0, )\",\n        \"resolved\": \"4.10.0\",\n        \"contentHash\": \"YZ1yTTDkFdgjglo9v2Gy4jnWUUIPTQETCul9CDguydLYrVgQXr6L1n3CEJqy/S9kgX+Er0cRQixky2grMwtvxA==\",\n        \"dependencies\": {\n          \"Castle.Core\": \"4.3.1\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\",\n          \"System.ValueTuple\": \"4.4.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[5.0.1, )\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"xunit\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"NL00nGsDsyWc1CWxz5FXXjLpW9oFG18WJoTPCyhNv4KGP/e5iLJqAqgM1uaJZyQ6WaTtmWIy4yjYP3RdcaT7Vw==\",\n        \"dependencies\": {\n          \"xunit.analyzers\": \"0.10.0\",\n          \"xunit.assert\": \"[2.4.0]\",\n          \"xunit.core\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.runner.visualstudio\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"3eq5cGXbEJkqW9nwLuXwtxy9B5gMA8i7HW4rN63AhAvy5UvEcQbZnve23wx/oPrkyg/4CbfNhxkBezS0b1oUdQ==\"\n      },\n      \"Castle.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"8Y/eTr6GTElAGV7eAmJuhfLhGdFpNvaNrQ9UQYDScziLmX+/BLGM+9eQr0IcdNDcPN0ADmbtwT6MgecGKy4obw==\"\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net452\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"kuFOgilYbs29xENHlqQ6aJYa+t56u+OqHx85P7GYLVlo7HL3nsug9IQY2DoPgkOpZ2xb9btYV2EFK7Enll8S3A==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\"\n      },\n      \"System.ValueTuple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==\"\n      },\n      \"xunit.abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.2\",\n        \"contentHash\": \"vItLB0WkaKg0426RgWq+ZdXH6D+YV/uH28C0weWMOBnVx7I+luHuEYss9hoOngpkiN5kUpLvh9VZRx1H2sk59A==\"\n      },\n      \"xunit.analyzers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.10.0\",\n        \"contentHash\": \"4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==\"\n      },\n      \"xunit.assert\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"Swvkm6iTjZr8TiUj5vMnmfG+2dD4s/BIBgsVOzTxxmoq2ndGsmM2WIL4wuqJ8RhxydWIDOPpIaaytjT2pMTEdg==\"\n      },\n      \"xunit.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"BJ/O/tPEcHUCwQYuwqXoYccTMyw6B5dA6yh7WxWWBhKbjqTsG9RWL0nCQXM5yQYJwUuFzBkiXDPN1BO6UdBB4Q==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\",\n          \"xunit.extensibility.execution\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.extensibility.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"qr/KrR6uukHXD9e/lLQjyCPfMEDuvvhNFDzsYzCF2kKlYKiqcADfUvA9Q68rBtKFtwHFeghjWEuv15KoGD2SfA==\",\n        \"dependencies\": {\n          \"xunit.abstractions\": \"2.0.2\"\n        }\n      },\n      \"xunit.extensibility.execution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"252Dzn7i5bMPKtAL15aOP3qJhxKd+57I8ldwIQRJa745JxQuiBu5Da0vtIISVTtc3buRSkBwVnD9iUzsEmCzZA==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      },\n      \"hangfire.sqlserver\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Dapper\": \"[1.60.6, )\",\n          \"Hangfire.Core\": \"[1.0.0, )\"\n        }\n      }\n    },\n    \".NETFramework,Version=v4.6.1\": {\n      \"Dapper\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.60.6, )\",\n        \"resolved\": \"1.60.6\",\n        \"contentHash\": \"mmnJNhKMeF2KhvVXDoVQlFxre8aJAo71YBJrKqFlvuqzYC2QiXUq94/GCDBJzU7paq4GqpkV2glw3308TcGibw==\"\n      },\n      \"Microsoft.NET.Test.Sdk\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[17.8.0, )\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[1.0.3, )\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==\",\n        \"dependencies\": {\n          \"Microsoft.NETFramework.ReferenceAssemblies.net461\": \"1.0.3\"\n        }\n      },\n      \"Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.10.0, )\",\n        \"resolved\": \"4.10.0\",\n        \"contentHash\": \"YZ1yTTDkFdgjglo9v2Gy4jnWUUIPTQETCul9CDguydLYrVgQXr6L1n3CEJqy/S9kgX+Er0cRQixky2grMwtvxA==\",\n        \"dependencies\": {\n          \"Castle.Core\": \"4.3.1\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\",\n          \"System.ValueTuple\": \"4.4.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[5.0.1, )\",\n        \"resolved\": \"5.0.1\",\n        \"contentHash\": \"AuSDf0kpGGLSvFmj1Zia8BxTeUCdQ6lB8lWUZRYVXRnAQLmiEGmoP0M+9KHwJNqBW2FiFwSG8Jkz3G7tS6k7MQ==\"\n      },\n      \"System.Data.SqlClient\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.5.0, )\",\n        \"resolved\": \"4.5.0\",\n        \"contentHash\": \"7xeDd8j7T/j2/OWsbVCx1QmCaHf2AnML0FXslTeo4My3F9tAcdSzrnrLdIi49bzNazHykQIfyU5nb3+jmE/CoQ==\"\n      },\n      \"xunit\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"NL00nGsDsyWc1CWxz5FXXjLpW9oFG18WJoTPCyhNv4KGP/e5iLJqAqgM1uaJZyQ6WaTtmWIy4yjYP3RdcaT7Vw==\",\n        \"dependencies\": {\n          \"xunit.analyzers\": \"0.10.0\",\n          \"xunit.assert\": \"[2.4.0]\",\n          \"xunit.core\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.runner.visualstudio\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"3eq5cGXbEJkqW9nwLuXwtxy9B5gMA8i7HW4rN63AhAvy5UvEcQbZnve23wx/oPrkyg/4CbfNhxkBezS0b1oUdQ==\"\n      },\n      \"Castle.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"8Y/eTr6GTElAGV7eAmJuhfLhGdFpNvaNrQ9UQYDScziLmX+/BLGM+9eQr0IcdNDcPN0ADmbtwT6MgecGKy4obw==\"\n      },\n      \"CronExpressionDescriptor\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.21.0\",\n        \"contentHash\": \"BDusPksr0codp6mgNbXfw8SG/uJKYdflCDkIaLPKD86YIdHPdzgz7hrbWDmlWpkyzJPPZ5uRDQPLaVUJMQIdBQ==\"\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.NETFramework.ReferenceAssemblies.net461\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.3\",\n        \"contentHash\": \"AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==\"\n      },\n      \"Microsoft.Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.2.3\",\n        \"contentHash\": \"uoOKm7Ouj06+ULS7Ss60tRM2E5t0ku7rQ7cJk864jArtE35WTJKMzUxgHxs7gdiqHZYnC3ddZSr9zj8yRjguEA==\",\n        \"dependencies\": {\n          \"Owin\": \"1.0.0\"\n        }\n      },\n      \"Owin\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==\"\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\"\n      },\n      \"System.ValueTuple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==\"\n      },\n      \"xunit.abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.2\",\n        \"contentHash\": \"vItLB0WkaKg0426RgWq+ZdXH6D+YV/uH28C0weWMOBnVx7I+luHuEYss9hoOngpkiN5kUpLvh9VZRx1H2sk59A==\"\n      },\n      \"xunit.analyzers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.10.0\",\n        \"contentHash\": \"4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==\"\n      },\n      \"xunit.assert\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"Swvkm6iTjZr8TiUj5vMnmfG+2dD4s/BIBgsVOzTxxmoq2ndGsmM2WIL4wuqJ8RhxydWIDOPpIaaytjT2pMTEdg==\"\n      },\n      \"xunit.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"BJ/O/tPEcHUCwQYuwqXoYccTMyw6B5dA6yh7WxWWBhKbjqTsG9RWL0nCQXM5yQYJwUuFzBkiXDPN1BO6UdBB4Q==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\",\n          \"xunit.extensibility.execution\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.extensibility.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"qr/KrR6uukHXD9e/lLQjyCPfMEDuvvhNFDzsYzCF2kKlYKiqcADfUvA9Q68rBtKFtwHFeghjWEuv15KoGD2SfA==\",\n        \"dependencies\": {\n          \"xunit.abstractions\": \"2.0.2\"\n        }\n      },\n      \"xunit.extensibility.execution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"252Dzn7i5bMPKtAL15aOP3qJhxKd+57I8ldwIQRJa745JxQuiBu5Da0vtIISVTtc3buRSkBwVnD9iUzsEmCzZA==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"CronExpressionDescriptor\": \"[1.21.0, )\",\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.Owin\": \"[4.2.3, )\",\n          \"Newtonsoft.Json\": \"[5.0.1, )\",\n          \"Owin\": \"[1.0.0, )\"\n        }\n      },\n      \"hangfire.sqlserver\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Dapper\": \"[1.60.6, )\",\n          \"Hangfire.Core\": \"[1.0.0, )\"\n        }\n      }\n    },\n    \"net6.0\": {\n      \"Dapper\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.1.28, )\",\n        \"resolved\": \"2.1.28\",\n        \"contentHash\": \"ha49pzOEDmCPkMxwfPSR/wxa/6RD3r42TESIgpzpi7FXq/gNVUuJVEO+wtUzntNRhtmq3BKCl0s0aAlSZLkBUA==\"\n      },\n      \"Microsoft.Data.SqlClient\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[5.2.2, )\",\n        \"resolved\": \"5.2.2\",\n        \"contentHash\": \"mtoeRMh7F/OA536c/Cnh8L4H0uLSKB5kSmoi54oN7Fp0hNJDy22IqyMhaMH4PkDCqI7xL//Fvg9ldtuPHG0h5g==\",\n        \"dependencies\": {\n          \"Azure.Identity\": \"1.11.4\",\n          \"Microsoft.Data.SqlClient.SNI.runtime\": \"5.2.0\",\n          \"Microsoft.Identity.Client\": \"4.61.3\",\n          \"Microsoft.IdentityModel.JsonWebTokens\": \"6.35.0\",\n          \"Microsoft.IdentityModel.Protocols.OpenIdConnect\": \"6.35.0\",\n          \"Microsoft.SqlServer.Server\": \"1.0.0\",\n          \"System.Configuration.ConfigurationManager\": \"6.0.1\",\n          \"System.Runtime.Caching\": \"6.0.0\"\n        }\n      },\n      \"Microsoft.NET.Test.Sdk\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[17.8.0, )\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==\",\n        \"dependencies\": {\n          \"Microsoft.CodeCoverage\": \"17.8.0\",\n          \"Microsoft.TestPlatform.TestHost\": \"17.8.0\"\n        }\n      },\n      \"Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.10.0, )\",\n        \"resolved\": \"4.10.0\",\n        \"contentHash\": \"YZ1yTTDkFdgjglo9v2Gy4jnWUUIPTQETCul9CDguydLYrVgQXr6L1n3CEJqy/S9kgX+Er0cRQixky2grMwtvxA==\",\n        \"dependencies\": {\n          \"Castle.Core\": \"4.3.1\",\n          \"NETStandard.Library\": \"1.6.1\",\n          \"System.Linq.Queryable\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\",\n          \"System.ValueTuple\": \"4.4.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[13.0.3, )\",\n        \"resolved\": \"13.0.3\",\n        \"contentHash\": \"HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==\"\n      },\n      \"System.Data.SqlClient\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.8.6, )\",\n        \"resolved\": \"4.8.6\",\n        \"contentHash\": \"2Ij/LCaTQRyAi5lAv7UUTV9R2FobC8xN9mE0fXBZohum/xLl8IZVmE98Rq5ugQHjCgTBRKqpXRb4ORulRdA6Ig==\",\n        \"dependencies\": {\n          \"Microsoft.Win32.Registry\": \"4.7.0\",\n          \"System.Security.Principal.Windows\": \"4.7.0\",\n          \"runtime.native.System.Data.SqlClient.sni\": \"4.7.0\"\n        }\n      },\n      \"xunit\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"NL00nGsDsyWc1CWxz5FXXjLpW9oFG18WJoTPCyhNv4KGP/e5iLJqAqgM1uaJZyQ6WaTtmWIy4yjYP3RdcaT7Vw==\",\n        \"dependencies\": {\n          \"xunit.analyzers\": \"0.10.0\",\n          \"xunit.assert\": \"[2.4.0]\",\n          \"xunit.core\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.runner.visualstudio\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"3eq5cGXbEJkqW9nwLuXwtxy9B5gMA8i7HW4rN63AhAvy5UvEcQbZnve23wx/oPrkyg/4CbfNhxkBezS0b1oUdQ==\",\n        \"dependencies\": {\n          \"Microsoft.NET.Test.Sdk\": \"15.0.0\"\n        }\n      },\n      \"Azure.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.38.0\",\n        \"contentHash\": \"IuEgCoVA0ef7E4pQtpC3+TkPbzaoQfa77HlfJDmfuaJUCVJmn7fT0izamZiryW5sYUFKizsftIxMkXKbgIcPMQ==\",\n        \"dependencies\": {\n          \"Microsoft.Bcl.AsyncInterfaces\": \"1.1.1\",\n          \"System.ClientModel\": \"1.0.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"6.0.1\",\n          \"System.Memory.Data\": \"1.0.2\",\n          \"System.Numerics.Vectors\": \"4.5.0\",\n          \"System.Text.Encodings.Web\": \"4.7.2\",\n          \"System.Text.Json\": \"4.7.2\",\n          \"System.Threading.Tasks.Extensions\": \"4.5.4\"\n        }\n      },\n      \"Azure.Identity\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.11.4\",\n        \"contentHash\": \"Sf4BoE6Q3jTgFkgBkx7qztYOFELBCo+wQgpYDwal/qJ1unBH73ywPztIJKXBXORRzAeNijsuxhk94h0TIMvfYg==\",\n        \"dependencies\": {\n          \"Azure.Core\": \"1.38.0\",\n          \"Microsoft.Identity.Client\": \"4.61.3\",\n          \"Microsoft.Identity.Client.Extensions.Msal\": \"4.61.3\",\n          \"System.Memory\": \"4.5.4\",\n          \"System.Security.Cryptography.ProtectedData\": \"4.7.0\",\n          \"System.Text.Json\": \"4.7.2\",\n          \"System.Threading.Tasks.Extensions\": \"4.5.4\"\n        }\n      },\n      \"Castle.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"8Y/eTr6GTElAGV7eAmJuhfLhGdFpNvaNrQ9UQYDScziLmX+/BLGM+9eQr0IcdNDcPN0ADmbtwT6MgecGKy4obw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\",\n          \"System.Collections.Specialized\": \"4.3.0\",\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.ComponentModel.TypeConverter\": \"4.3.0\",\n          \"System.Diagnostics.TraceSource\": \"4.3.0\",\n          \"System.Dynamic.Runtime\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Xml.XmlDocument\": \"4.3.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.Bcl.AsyncInterfaces\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.1\",\n        \"contentHash\": \"yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==\"\n      },\n      \"Microsoft.CodeCoverage\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.0\",\n        \"contentHash\": \"kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ==\"\n      },\n      \"Microsoft.Data.SqlClient.SNI.runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"5.2.0\",\n        \"contentHash\": \"po1jhvFd+8pbfvJR/puh+fkHi0GRanAdvayh/0e47yaM6CXWZ6opUjCMFuYlAnD2LcbyvQE7fPJKvogmaUcN+w==\"\n      },\n      \"Microsoft.Identity.Client\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.61.3\",\n        \"contentHash\": \"naJo/Qm35Caaoxp5utcw+R8eU8ZtLz2ALh8S+gkekOYQ1oazfCQMWVT4NJ/FnHzdIJlm8dMz0oMpMGCabx5odA==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Abstractions\": \"6.35.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"6.0.1\"\n        }\n      },\n      \"Microsoft.Identity.Client.Extensions.Msal\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.61.3\",\n        \"contentHash\": \"PWnJcznrSGr25MN8ajlc2XIDW4zCFu0U6FkpaNLEWLgd1NgFCp5uDY3mqLDgM8zCN8hqj8yo5wHYfLB2HjcdGw==\",\n        \"dependencies\": {\n          \"Microsoft.Identity.Client\": \"4.61.3\",\n          \"System.Security.Cryptography.ProtectedData\": \"4.5.0\"\n        }\n      },\n      \"Microsoft.IdentityModel.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.35.0\",\n        \"contentHash\": \"xuR8E4Rd96M41CnUSCiOJ2DBh+z+zQSmyrYHdYhD6K4fXBcQGVnRCFQ0efROUYpP+p0zC1BLKr0JRpVuujTZSg==\"\n      },\n      \"Microsoft.IdentityModel.JsonWebTokens\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.35.0\",\n        \"contentHash\": \"9wxai3hKgZUb4/NjdRKfQd0QJvtXKDlvmGMYACbEC8DFaicMFCFhQFZq9ZET1kJLwZahf2lfY5Gtcpsx8zYzbg==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Tokens\": \"6.35.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encodings.Web\": \"4.7.2\",\n          \"System.Text.Json\": \"4.7.2\"\n        }\n      },\n      \"Microsoft.IdentityModel.Logging\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.35.0\",\n        \"contentHash\": \"jePrSfGAmqT81JDCNSY+fxVWoGuJKt9e6eJ+vT7+quVS55nWl//jGjUQn4eFtVKt4rt5dXaleZdHRB9J9AJZ7Q==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Abstractions\": \"6.35.0\"\n        }\n      },\n      \"Microsoft.IdentityModel.Protocols\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.35.0\",\n        \"contentHash\": \"BPQhlDzdFvv1PzaUxNSk+VEPwezlDEVADIKmyxubw7IiELK18uJ06RQ9QKKkds30XI+gDu9n8j24XQ8w7fjWcg==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Logging\": \"6.35.0\",\n          \"Microsoft.IdentityModel.Tokens\": \"6.35.0\"\n        }\n      },\n      \"Microsoft.IdentityModel.Protocols.OpenIdConnect\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.35.0\",\n        \"contentHash\": \"LMtVqnECCCdSmyFoCOxIE5tXQqkOLrvGrL7OxHg41DIm1bpWtaCdGyVcTAfOQpJXvzND9zUKIN/lhngPkYR8vg==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Protocols\": \"6.35.0\",\n          \"System.IdentityModel.Tokens.Jwt\": \"6.35.0\"\n        }\n      },\n      \"Microsoft.IdentityModel.Tokens\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.35.0\",\n        \"contentHash\": \"RN7lvp7s3Boucg1NaNAbqDbxtlLj5Qeb+4uSS1TeK5FSBVM40P4DKaTKChT43sHyKfh7V0zkrMph6DdHvyA4bg==\",\n        \"dependencies\": {\n          \"Microsoft.CSharp\": \"4.5.0\",\n          \"Microsoft.IdentityModel.Logging\": \"6.35.0\",\n          \"System.Security.Cryptography.Cng\": \"4.5.0\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==\"\n      },\n      \"Microsoft.SqlServer.Server\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==\"\n      },\n      \"Microsoft.TestPlatform.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==\",\n        \"dependencies\": {\n          \"NuGet.Frameworks\": \"6.5.0\",\n          \"System.Reflection.Metadata\": \"1.6.0\"\n        }\n      },\n      \"Microsoft.TestPlatform.TestHost\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==\",\n        \"dependencies\": {\n          \"Microsoft.TestPlatform.ObjectModel\": \"17.8.0\",\n          \"Newtonsoft.Json\": \"13.0.1\"\n        }\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"Microsoft.Win32.Registry\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.7.0\",\n        \"contentHash\": \"KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==\",\n        \"dependencies\": {\n          \"System.Security.AccessControl\": \"4.7.0\",\n          \"System.Security.Principal.Windows\": \"4.7.0\"\n        }\n      },\n      \"Microsoft.Win32.SystemEvents\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A==\"\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"NuGet.Frameworks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.5.0\",\n        \"contentHash\": \"QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.7.0\",\n        \"contentHash\": \"9kyFSIdN3T0qjDQ2R0HRXYIhS3l5psBzQi6qqhdLz+SzFyEy4sVxNOke+yyYv8Cu8rPER12c3RDjLT8wF3WBYQ==\",\n        \"dependencies\": {\n          \"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni\": \"4.4.0\",\n          \"runtime.win-x64.runtime.native.System.Data.SqlClient.sni\": \"4.4.0\",\n          \"runtime.win-x86.runtime.native.System.Data.SqlClient.sni\": \"4.4.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==\"\n      },\n      \"runtime.win-x64.runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==\"\n      },\n      \"runtime.win-x86.runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==\"\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.ClientModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"I3CVkvxeqFYjIVEP59DnjbeoGNfo/+SZrCLpRz2v/g0gpCHaEMPtWSY0s9k/7jR1rAsLNg2z2u1JRB76tPjnIw==\",\n        \"dependencies\": {\n          \"System.Memory.Data\": \"1.0.2\",\n          \"System.Text.Json\": \"4.7.2\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.NonGeneric\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Specialized\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==\",\n        \"dependencies\": {\n          \"System.Collections.NonGeneric\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==\",\n        \"dependencies\": {\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel.TypeConverter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.NonGeneric\": \"4.3.0\",\n          \"System.Collections.Specialized\": \"4.3.0\",\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.ComponentModel.Primitives\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Configuration.ConfigurationManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.1\",\n        \"contentHash\": \"jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==\",\n        \"dependencies\": {\n          \"System.Security.Cryptography.ProtectedData\": \"6.0.0\",\n          \"System.Security.Permissions\": \"6.0.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.1\",\n        \"contentHash\": \"KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==\",\n        \"dependencies\": {\n          \"System.Runtime.CompilerServices.Unsafe\": \"6.0.0\"\n        }\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.TraceSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VnYp1NxGx8Ww731y2LJ1vpfb/DKVNKEZ8Jsh5SgQTZREL/YpWRArgh9pI8CDLmgHspZmLL697CaLvH85qQpRiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Drawing.Common\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==\",\n        \"dependencies\": {\n          \"Microsoft.Win32.SystemEvents\": \"6.0.0\"\n        }\n      },\n      \"System.Dynamic.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IdentityModel.Tokens.Jwt\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.35.0\",\n        \"contentHash\": \"yxGIQd3BFK7F6S62/7RdZk3C/mfwyVxvh6ngd1VYMBmbJ1YZZA9+Ku6suylVtso0FjI0wbElpJ0d27CdsyLpBQ==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.JsonWebTokens\": \"6.35.0\",\n          \"Microsoft.IdentityModel.Tokens\": \"6.35.0\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Queryable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"In1Bmmvl/j52yPu3xgakQSI0YIckPUr870w4K5+Lak3JCCa8hl+my65lABOuKfYs4ugmZy25ScFerC4nz8+b6g==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Memory\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.4\",\n        \"contentHash\": \"1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==\"\n      },\n      \"System.Memory.Data\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.2\",\n        \"contentHash\": \"JGkzeqgBsiZwKJZ1IxPNsDFZDhUvuEdX8L8BDC8N3KOj+6zMcNU28CNN59TpZE/VJYy9cP+5M+sbxtWJx3/xtw==\",\n        \"dependencies\": {\n          \"System.Text.Encodings.Web\": \"4.7.2\",\n          \"System.Text.Json\": \"4.6.0\"\n        }\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Numerics.Vectors\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.0\",\n        \"contentHash\": \"QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==\"\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Metadata\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.0\",\n        \"contentHash\": \"COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==\"\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.Caching\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"E0e03kUp5X2k+UAoVl6efmI7uU7JRBWi5EIdlQ7cr0NpBGjHG4fWII35PgsBY9T4fJQ8E4QPsL0rKksU9gcL5A==\",\n        \"dependencies\": {\n          \"System.Configuration.ConfigurationManager\": \"6.0.0\"\n        }\n      },\n      \"System.Runtime.CompilerServices.Unsafe\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==\"\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.AccessControl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==\"\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.0\",\n        \"contentHash\": \"WG3r7EyjUe9CMPFSs6bty5doUqT+q9pbI80hlNzo2SkPkZ4VTuZkGWjpp77JB8+uaL4DFPRdBsAY+DX3dBK92A==\"\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.ProtectedData\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ==\"\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Permissions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==\",\n        \"dependencies\": {\n          \"System.Security.AccessControl\": \"6.0.0\",\n          \"System.Windows.Extensions\": \"6.0.0\"\n        }\n      },\n      \"System.Security.Principal.Windows\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.7.0\",\n        \"contentHash\": \"ojD0PX0XhneCsUbAZVKdb7h/70vyYMDYs85lwEI+LngEONe/17A0cFaRFqZU+sOEidcVswYWikYOQ9PPfjlbtQ==\"\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encodings.Web\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.7.2\",\n        \"contentHash\": \"iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==\"\n      },\n      \"System.Text.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.7.2\",\n        \"contentHash\": \"TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==\"\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.4\",\n        \"contentHash\": \"zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==\"\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ValueTuple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==\"\n      },\n      \"System.Windows.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==\",\n        \"dependencies\": {\n          \"System.Drawing.Common\": \"6.0.0\"\n        }\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XmlDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"xunit.abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.2\",\n        \"contentHash\": \"vItLB0WkaKg0426RgWq+ZdXH6D+YV/uH28C0weWMOBnVx7I+luHuEYss9hoOngpkiN5kUpLvh9VZRx1H2sk59A==\"\n      },\n      \"xunit.analyzers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.10.0\",\n        \"contentHash\": \"4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==\"\n      },\n      \"xunit.assert\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"Swvkm6iTjZr8TiUj5vMnmfG+2dD4s/BIBgsVOzTxxmoq2ndGsmM2WIL4wuqJ8RhxydWIDOPpIaaytjT2pMTEdg==\"\n      },\n      \"xunit.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"BJ/O/tPEcHUCwQYuwqXoYccTMyw6B5dA6yh7WxWWBhKbjqTsG9RWL0nCQXM5yQYJwUuFzBkiXDPN1BO6UdBB4Q==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\",\n          \"xunit.extensibility.execution\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.extensibility.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"qr/KrR6uukHXD9e/lLQjyCPfMEDuvvhNFDzsYzCF2kKlYKiqcADfUvA9Q68rBtKFtwHFeghjWEuv15KoGD2SfA==\",\n        \"dependencies\": {\n          \"xunit.abstractions\": \"2.0.2\"\n        }\n      },\n      \"xunit.extensibility.execution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"252Dzn7i5bMPKtAL15aOP3qJhxKd+57I8ldwIQRJa745JxQuiBu5Da0vtIISVTtc3buRSkBwVnD9iUzsEmCzZA==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.CSharp\": \"[4.4.0, )\",\n          \"Newtonsoft.Json\": \"[11.0.1, )\"\n        }\n      },\n      \"hangfire.sqlserver\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Dapper\": \"[2.1.28, )\",\n          \"Hangfire.Core\": \"[1.0.0, )\"\n        }\n      }\n    },\n    \"net8.0\": {\n      \"Dapper\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.1.28, )\",\n        \"resolved\": \"2.1.28\",\n        \"contentHash\": \"ha49pzOEDmCPkMxwfPSR/wxa/6RD3r42TESIgpzpi7FXq/gNVUuJVEO+wtUzntNRhtmq3BKCl0s0aAlSZLkBUA==\"\n      },\n      \"Microsoft.Data.SqlClient\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[6.1.1, )\",\n        \"resolved\": \"6.1.1\",\n        \"contentHash\": \"syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==\",\n        \"dependencies\": {\n          \"Azure.Core\": \"1.47.1\",\n          \"Azure.Identity\": \"1.14.2\",\n          \"Microsoft.Bcl.Cryptography\": \"8.0.0\",\n          \"Microsoft.Data.SqlClient.SNI.runtime\": \"6.0.2\",\n          \"Microsoft.Extensions.Caching.Memory\": \"8.0.1\",\n          \"Microsoft.IdentityModel.JsonWebTokens\": \"7.7.1\",\n          \"Microsoft.IdentityModel.Protocols.OpenIdConnect\": \"7.7.1\",\n          \"Microsoft.SqlServer.Server\": \"1.0.0\",\n          \"System.Configuration.ConfigurationManager\": \"8.0.1\",\n          \"System.Security.Cryptography.Pkcs\": \"8.0.1\",\n          \"System.Text.Json\": \"8.0.5\"\n        }\n      },\n      \"Microsoft.NET.Test.Sdk\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[17.8.0, )\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"BmTYGbD/YuDHmApIENdoyN1jCk0Rj1fJB0+B/fVekyTdVidr91IlzhqzytiUgaEAzL1ZJcYCme0MeBMYvJVzvw==\",\n        \"dependencies\": {\n          \"Microsoft.CodeCoverage\": \"17.8.0\",\n          \"Microsoft.TestPlatform.TestHost\": \"17.8.0\"\n        }\n      },\n      \"Moq\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.10.0, )\",\n        \"resolved\": \"4.10.0\",\n        \"contentHash\": \"YZ1yTTDkFdgjglo9v2Gy4jnWUUIPTQETCul9CDguydLYrVgQXr6L1n3CEJqy/S9kgX+Er0cRQixky2grMwtvxA==\",\n        \"dependencies\": {\n          \"Castle.Core\": \"4.3.1\",\n          \"NETStandard.Library\": \"1.6.1\",\n          \"System.Linq.Queryable\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\",\n          \"System.ValueTuple\": \"4.4.0\"\n        }\n      },\n      \"Newtonsoft.Json\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[13.0.3, )\",\n        \"resolved\": \"13.0.3\",\n        \"contentHash\": \"HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==\"\n      },\n      \"System.Data.SqlClient\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[4.9.0, )\",\n        \"resolved\": \"4.9.0\",\n        \"contentHash\": \"j4KJO+vC62NyUtNHz854njEqXbT8OmAa5jb1nrGfYWBOcggyYUQE0w/snXeaCjdvkSKWuUD+hfvlbN8pTrJTXg==\",\n        \"dependencies\": {\n          \"runtime.native.System.Data.SqlClient.sni\": \"4.4.0\"\n        }\n      },\n      \"xunit\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"NL00nGsDsyWc1CWxz5FXXjLpW9oFG18WJoTPCyhNv4KGP/e5iLJqAqgM1uaJZyQ6WaTtmWIy4yjYP3RdcaT7Vw==\",\n        \"dependencies\": {\n          \"xunit.analyzers\": \"0.10.0\",\n          \"xunit.assert\": \"[2.4.0]\",\n          \"xunit.core\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.runner.visualstudio\": {\n        \"type\": \"Direct\",\n        \"requested\": \"[2.4.0, )\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"3eq5cGXbEJkqW9nwLuXwtxy9B5gMA8i7HW4rN63AhAvy5UvEcQbZnve23wx/oPrkyg/4CbfNhxkBezS0b1oUdQ==\",\n        \"dependencies\": {\n          \"Microsoft.NET.Test.Sdk\": \"15.0.0\"\n        }\n      },\n      \"Azure.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.47.1\",\n        \"contentHash\": \"oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==\",\n        \"dependencies\": {\n          \"Microsoft.Bcl.AsyncInterfaces\": \"8.0.0\",\n          \"System.ClientModel\": \"1.5.1\",\n          \"System.Memory.Data\": \"8.0.1\"\n        }\n      },\n      \"Azure.Identity\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.14.2\",\n        \"contentHash\": \"YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==\",\n        \"dependencies\": {\n          \"Azure.Core\": \"1.46.1\",\n          \"Microsoft.Identity.Client\": \"4.73.1\",\n          \"Microsoft.Identity.Client.Extensions.Msal\": \"4.73.1\",\n          \"System.Memory\": \"4.5.5\"\n        }\n      },\n      \"Castle.Core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.1\",\n        \"contentHash\": \"8Y/eTr6GTElAGV7eAmJuhfLhGdFpNvaNrQ9UQYDScziLmX+/BLGM+9eQr0IcdNDcPN0ADmbtwT6MgecGKy4obw==\",\n        \"dependencies\": {\n          \"NETStandard.Library\": \"1.6.1\",\n          \"System.Collections.Specialized\": \"4.3.0\",\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.ComponentModel.TypeConverter\": \"4.3.0\",\n          \"System.Diagnostics.TraceSource\": \"4.3.0\",\n          \"System.Dynamic.Runtime\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Xml.XmlDocument\": \"4.3.0\"\n        }\n      },\n      \"Cronos\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.11.1\",\n        \"contentHash\": \"5Ug+giPQITSAdTp/METAsofRSSUi3I5p7t4dlcXnzUgUzwZb4HkOBcYfpHuPwAHrnKJjmyW8amVzLD6mfLpaBg==\"\n      },\n      \"Microsoft.Bcl.AsyncInterfaces\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==\"\n      },\n      \"Microsoft.Bcl.Cryptography\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"Y3t/c7C5XHJGFDnohjf1/9SYF3ZOfEU1fkNQuKg/dGf9hN18yrQj2owHITGfNS3+lKJdW6J4vY98jYu57jCO8A==\"\n      },\n      \"Microsoft.CodeCoverage\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"KC8SXWbGIdoFVdlxKk9WHccm0llm9HypcHMLUUFabRiTS3SO2fQXNZfdiF3qkEdTJhbRrxhdRxjL4jbtwPq4Ew==\"\n      },\n      \"Microsoft.CSharp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"vvVR/B08YVghQ4jHEloxqw2ZWzEGE1AOA5E0DioUM3ujbXz6FD3AfB/0Jl2ohJPd0nXYGwmPe1En6HTsSriq1A==\"\n      },\n      \"Microsoft.Data.SqlClient.SNI.runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.2\",\n        \"contentHash\": \"f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==\"\n      },\n      \"Microsoft.Extensions.Caching.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Caching.Memory\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Caching.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Logging.Abstractions\": \"8.0.2\",\n          \"Microsoft.Extensions.Options\": \"8.0.2\",\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.DependencyInjection.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.2\",\n        \"contentHash\": \"3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==\"\n      },\n      \"Microsoft.Extensions.Logging.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.3\",\n        \"contentHash\": \"dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.2\"\n        }\n      },\n      \"Microsoft.Extensions.Options\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.2\",\n        \"contentHash\": \"dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.DependencyInjection.Abstractions\": \"8.0.0\",\n          \"Microsoft.Extensions.Primitives\": \"8.0.0\"\n        }\n      },\n      \"Microsoft.Extensions.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==\"\n      },\n      \"Microsoft.Identity.Client\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.73.1\",\n        \"contentHash\": \"NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Abstractions\": \"6.35.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"6.0.1\"\n        }\n      },\n      \"Microsoft.Identity.Client.Extensions.Msal\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.73.1\",\n        \"contentHash\": \"xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==\",\n        \"dependencies\": {\n          \"Microsoft.Identity.Client\": \"4.73.1\",\n          \"System.Security.Cryptography.ProtectedData\": \"4.5.0\"\n        }\n      },\n      \"Microsoft.IdentityModel.Abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.7.1\",\n        \"contentHash\": \"S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==\"\n      },\n      \"Microsoft.IdentityModel.JsonWebTokens\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.7.1\",\n        \"contentHash\": \"3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Tokens\": \"7.7.1\"\n        }\n      },\n      \"Microsoft.IdentityModel.Logging\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.7.1\",\n        \"contentHash\": \"BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Abstractions\": \"7.7.1\"\n        }\n      },\n      \"Microsoft.IdentityModel.Protocols\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.7.1\",\n        \"contentHash\": \"h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Tokens\": \"7.7.1\"\n        }\n      },\n      \"Microsoft.IdentityModel.Protocols.OpenIdConnect\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.7.1\",\n        \"contentHash\": \"yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Protocols\": \"7.7.1\",\n          \"System.IdentityModel.Tokens.Jwt\": \"7.7.1\"\n        }\n      },\n      \"Microsoft.IdentityModel.Tokens\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.7.1\",\n        \"contentHash\": \"fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.Logging\": \"7.7.1\"\n        }\n      },\n      \"Microsoft.NETCore.Platforms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==\"\n      },\n      \"Microsoft.NETCore.Targets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.1.0\",\n        \"contentHash\": \"aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==\"\n      },\n      \"Microsoft.SqlServer.Server\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.0.0\",\n        \"contentHash\": \"N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==\"\n      },\n      \"Microsoft.TestPlatform.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"AYy6vlpGMfz5kOFq99L93RGbqftW/8eQTqjT9iGXW6s9MRP3UdtY8idJ8rJcjeSja8A18IhIro5YnH3uv1nz4g==\",\n        \"dependencies\": {\n          \"NuGet.Frameworks\": \"6.5.0\",\n          \"System.Reflection.Metadata\": \"1.6.0\"\n        }\n      },\n      \"Microsoft.TestPlatform.TestHost\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"17.8.0\",\n        \"contentHash\": \"9ivcl/7SGRmOT0YYrHQGohWiT5YCpkmy/UEzldfVisLm6QxbLaK3FAJqZXI34rnRLmqqDCeMQxKINwmKwAPiDw==\",\n        \"dependencies\": {\n          \"Microsoft.TestPlatform.ObjectModel\": \"17.8.0\",\n          \"Newtonsoft.Json\": \"13.0.1\"\n        }\n      },\n      \"Microsoft.Win32.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"NETStandard.Library\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.1\",\n        \"contentHash\": \"WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.Win32.Primitives\": \"4.3.0\",\n          \"System.AppContext\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Console\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.Compression.ZipFile\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Net.Http\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Net.Sockets\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.InteropServices.RuntimeInformation\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Timer\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\",\n          \"System.Xml.XDocument\": \"4.3.0\"\n        }\n      },\n      \"NuGet.Frameworks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.5.0\",\n        \"contentHash\": \"QWINE2x3MbTODsWT1Gh71GaGb5icBz4chS8VYvTgsBnsi8esgN6wtHhydd7fvToWECYGq7T4cgBBDiKD/363fg==\"\n      },\n      \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==\"\n      },\n      \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==\"\n      },\n      \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==\"\n      },\n      \"runtime.native.System\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"A8v6PGmk+UGbfWo5Ixup0lPM4swuSwOiayJExZwKIOjTlFFQIsu3QnDXECosBEyrWSPryxBVrdqtJyhK3BaupQ==\",\n        \"dependencies\": {\n          \"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni\": \"4.4.0\",\n          \"runtime.win-x64.runtime.native.System.Data.SqlClient.sni\": \"4.4.0\",\n          \"runtime.win-x86.runtime.native.System.Data.SqlClient.sni\": \"4.4.0\"\n        }\n      },\n      \"runtime.native.System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==\",\n        \"dependencies\": {\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\"\n        }\n      },\n      \"runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==\",\n        \"dependencies\": {\n          \"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==\"\n      },\n      \"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==\"\n      },\n      \"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==\"\n      },\n      \"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==\"\n      },\n      \"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==\"\n      },\n      \"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==\"\n      },\n      \"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==\"\n      },\n      \"runtime.win-arm64.runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==\"\n      },\n      \"runtime.win-x64.runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==\"\n      },\n      \"runtime.win-x86.runtime.native.System.Data.SqlClient.sni\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==\"\n      },\n      \"System.AppContext\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Buffers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.ClientModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.5.1\",\n        \"contentHash\": \"k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==\",\n        \"dependencies\": {\n          \"Microsoft.Extensions.Logging.Abstractions\": \"8.0.3\",\n          \"System.Memory.Data\": \"8.0.1\"\n        }\n      },\n      \"System.Collections\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Concurrent\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.NonGeneric\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Collections.Specialized\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==\",\n        \"dependencies\": {\n          \"System.Collections.NonGeneric\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==\",\n        \"dependencies\": {\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ComponentModel.TypeConverter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.NonGeneric\": \"4.3.0\",\n          \"System.Collections.Specialized\": \"4.3.0\",\n          \"System.ComponentModel\": \"4.3.0\",\n          \"System.ComponentModel.Primitives\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Configuration.ConfigurationManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"gPYFPDyohW2gXNhdQRSjtmeS6FymL2crg4Sral1wtvEJ7DUqFCDWDVbbLobASbzxfic8U1hQEdC7hmg9LHncMw==\",\n        \"dependencies\": {\n          \"System.Diagnostics.EventLog\": \"8.0.1\",\n          \"System.Security.Cryptography.ProtectedData\": \"8.0.0\"\n        }\n      },\n      \"System.Console\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Debug\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.DiagnosticSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.1\",\n        \"contentHash\": \"KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==\",\n        \"dependencies\": {\n          \"System.Runtime.CompilerServices.Unsafe\": \"6.0.0\"\n        }\n      },\n      \"System.Diagnostics.EventLog\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"n1ZP7NM2Gkn/MgD8+eOT5MulMj6wfeQMNS2Pizvq5GHCZfjlFMXV2irQlQmJhwA2VABC57M0auudO89Iu2uRLg==\"\n      },\n      \"System.Diagnostics.Tools\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.TraceSource\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VnYp1NxGx8Ww731y2LJ1vpfb/DKVNKEZ8Jsh5SgQTZREL/YpWRArgh9pI8CDLmgHspZmLL697CaLvH85qQpRiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Diagnostics.Tracing\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Dynamic.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Calendars\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Globalization.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\"\n        }\n      },\n      \"System.IdentityModel.Tokens.Jwt\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"7.7.1\",\n        \"contentHash\": \"rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==\",\n        \"dependencies\": {\n          \"Microsoft.IdentityModel.JsonWebTokens\": \"7.7.1\",\n          \"Microsoft.IdentityModel.Tokens\": \"7.7.1\"\n        }\n      },\n      \"System.IO\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Buffers\": \"4.3.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.IO.Compression\": \"4.3.0\"\n        }\n      },\n      \"System.IO.Compression.ZipFile\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==\",\n        \"dependencies\": {\n          \"System.Buffers\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.Compression\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.IO.FileSystem.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Linq\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Expressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.ObjectModel\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Emit.Lightweight\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Reflection.TypeExtensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Linq.Queryable\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"In1Bmmvl/j52yPu3xgakQSI0YIckPUr870w4K5+Lak3JCCa8hl+my65lABOuKfYs4ugmZy25ScFerC4nz8+b6g==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Linq.Expressions\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Memory\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.5.5\",\n        \"contentHash\": \"XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==\"\n      },\n      \"System.Memory.Data\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==\"\n      },\n      \"System.Net.Http\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.DiagnosticSource\": \"4.3.0\",\n          \"System.Diagnostics.Tracing\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Extensions\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Security.Cryptography.X509Certificates\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Net.Sockets\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Net.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.ObjectModel\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==\",\n        \"dependencies\": {\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.ILGeneration\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Emit.Lightweight\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Emit.ILGeneration\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.Metadata\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"1.6.0\",\n        \"contentHash\": \"COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==\"\n      },\n      \"System.Reflection.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Reflection.TypeExtensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Resources.ResourceManager\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\"\n        }\n      },\n      \"System.Runtime.CompilerServices.Unsafe\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"6.0.0\",\n        \"contentHash\": \"/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==\"\n      },\n      \"System.Runtime.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Handles\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Primitives\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.InteropServices.RuntimeInformation\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==\",\n        \"dependencies\": {\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Reflection.Extensions\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\"\n        }\n      },\n      \"System.Runtime.Numerics\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==\",\n        \"dependencies\": {\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Algorithms\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.Apple\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Cng\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Csp\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Collections.Concurrent\": \"4.3.0\",\n          \"System.Linq\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.OpenSsl\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.Pkcs\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.1\",\n        \"contentHash\": \"CoCRHFym33aUSf/NtWSVSZa99dkd0Hm7OCZUxORBjRB16LNhIEOf8THPqzIYlvKM0nNDAPTRBa1FxEECrgaxxA==\"\n      },\n      \"System.Security.Cryptography.Primitives\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==\",\n        \"dependencies\": {\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Security.Cryptography.ProtectedData\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.0\",\n        \"contentHash\": \"+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==\"\n      },\n      \"System.Security.Cryptography.X509Certificates\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.Globalization.Calendars\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.Handles\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Runtime.Numerics\": \"4.3.0\",\n          \"System.Security.Cryptography.Algorithms\": \"4.3.0\",\n          \"System.Security.Cryptography.Cng\": \"4.3.0\",\n          \"System.Security.Cryptography.Csp\": \"4.3.0\",\n          \"System.Security.Cryptography.Encoding\": \"4.3.0\",\n          \"System.Security.Cryptography.OpenSsl\": \"4.3.0\",\n          \"System.Security.Cryptography.Primitives\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"runtime.native.System\": \"4.3.0\",\n          \"runtime.native.System.Net.Http\": \"4.3.0\",\n          \"runtime.native.System.Security.Cryptography.OpenSsl\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Encoding.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\"\n        }\n      },\n      \"System.Text.Json\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"8.0.5\",\n        \"contentHash\": \"0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==\"\n      },\n      \"System.Text.RegularExpressions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==\",\n        \"dependencies\": {\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Tasks.Extensions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\"\n        }\n      },\n      \"System.Threading.Timer\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==\",\n        \"dependencies\": {\n          \"Microsoft.NETCore.Platforms\": \"1.1.0\",\n          \"Microsoft.NETCore.Targets\": \"1.1.0\",\n          \"System.Runtime\": \"4.3.0\"\n        }\n      },\n      \"System.ValueTuple\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.4.0\",\n        \"contentHash\": \"BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==\"\n      },\n      \"System.Xml.ReaderWriter\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.IO.FileSystem\": \"4.3.0\",\n          \"System.IO.FileSystem.Primitives\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Runtime.InteropServices\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Text.Encoding.Extensions\": \"4.3.0\",\n          \"System.Text.RegularExpressions\": \"4.3.0\",\n          \"System.Threading.Tasks\": \"4.3.0\",\n          \"System.Threading.Tasks.Extensions\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Diagnostics.Tools\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Reflection\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"System.Xml.XmlDocument\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"4.3.0\",\n        \"contentHash\": \"lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==\",\n        \"dependencies\": {\n          \"System.Collections\": \"4.3.0\",\n          \"System.Diagnostics.Debug\": \"4.3.0\",\n          \"System.Globalization\": \"4.3.0\",\n          \"System.IO\": \"4.3.0\",\n          \"System.Resources.ResourceManager\": \"4.3.0\",\n          \"System.Runtime\": \"4.3.0\",\n          \"System.Runtime.Extensions\": \"4.3.0\",\n          \"System.Text.Encoding\": \"4.3.0\",\n          \"System.Threading\": \"4.3.0\",\n          \"System.Xml.ReaderWriter\": \"4.3.0\"\n        }\n      },\n      \"xunit.abstractions\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.0.2\",\n        \"contentHash\": \"vItLB0WkaKg0426RgWq+ZdXH6D+YV/uH28C0weWMOBnVx7I+luHuEYss9hoOngpkiN5kUpLvh9VZRx1H2sk59A==\"\n      },\n      \"xunit.analyzers\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"0.10.0\",\n        \"contentHash\": \"4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==\"\n      },\n      \"xunit.assert\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"Swvkm6iTjZr8TiUj5vMnmfG+2dD4s/BIBgsVOzTxxmoq2ndGsmM2WIL4wuqJ8RhxydWIDOPpIaaytjT2pMTEdg==\"\n      },\n      \"xunit.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"BJ/O/tPEcHUCwQYuwqXoYccTMyw6B5dA6yh7WxWWBhKbjqTsG9RWL0nCQXM5yQYJwUuFzBkiXDPN1BO6UdBB4Q==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\",\n          \"xunit.extensibility.execution\": \"[2.4.0]\"\n        }\n      },\n      \"xunit.extensibility.core\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"qr/KrR6uukHXD9e/lLQjyCPfMEDuvvhNFDzsYzCF2kKlYKiqcADfUvA9Q68rBtKFtwHFeghjWEuv15KoGD2SfA==\",\n        \"dependencies\": {\n          \"xunit.abstractions\": \"2.0.2\"\n        }\n      },\n      \"xunit.extensibility.execution\": {\n        \"type\": \"Transitive\",\n        \"resolved\": \"2.4.0\",\n        \"contentHash\": \"252Dzn7i5bMPKtAL15aOP3qJhxKd+57I8ldwIQRJa745JxQuiBu5Da0vtIISVTtc3buRSkBwVnD9iUzsEmCzZA==\",\n        \"dependencies\": {\n          \"xunit.extensibility.core\": \"[2.4.0]\"\n        }\n      },\n      \"hangfire.core\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Cronos\": \"[0.11.1, )\",\n          \"Microsoft.CSharp\": \"[4.4.0, )\",\n          \"Newtonsoft.Json\": \"[11.0.1, )\"\n        }\n      },\n      \"hangfire.sqlserver\": {\n        \"type\": \"Project\",\n        \"dependencies\": {\n          \"Dapper\": \"[2.1.28, )\",\n          \"Hangfire.Core\": \"[1.0.0, )\"\n        }\n      }\n    }\n  }\n}"
  }
]