Compare commits
1204 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d0ab80bfde | |||
| 9eaa0361be | |||
| 67e90ca21b | |||
| 88b0dccfa1 | |||
| 73ce546f0c | |||
| 964447d546 | |||
| 40097c441a | |||
| 63b3c06b3a | |||
| ccaaa9ab93 | |||
| b08218240c | |||
| e67bbdc25f | |||
| d9ac1868e6 | |||
| a9b7c649ae | |||
| 1095948c27 | |||
| 7bd0a8edc2 | |||
| 4a2b6116f2 | |||
| 2c0fc737a7 | |||
| df53b93ae0 | |||
| 7c3b5d1086 | |||
| dbd6e0adbb | |||
| c607c7e259 | |||
| 29334ed2d5 | |||
| 990e82d183 | |||
| 2092156db3 | |||
| f7ee8c0899 | |||
| a9adc4eee5 | |||
| c59ff8d995 | |||
| 4a8e5935c2 | |||
| 8ed2dcb8bf | |||
| fc6548d87b | |||
| 5944ea20cc | |||
| 8329097974 | |||
| d15f20f560 | |||
| 87c85341fb | |||
| f6da7e4444 | |||
| d11fd67a09 | |||
| 977250dcdd | |||
| 1703c918d9 | |||
| d76f7db700 | |||
| 261ddfbcd6 | |||
| 86b31c2782 | |||
| cb309e7f9e | |||
| ed5023e431 | |||
| fcec180dc4 | |||
| a9045c254e | |||
| 03eaf3aa3d | |||
| 3b12e0b72a | |||
| 123daed990 | |||
| 9df0f384af | |||
| 562855a687 | |||
| 030b626350 | |||
| 78849bb7ff | |||
| c04da30547 | |||
| 0606d1bc8b | |||
| 4655acfdeb | |||
| 37c130194f | |||
| 162e691828 | |||
| 51ad0f75d1 | |||
| de5b32749e | |||
| 19d8796c40 | |||
| 0be5ff64bc | |||
| 8b4315cd46 | |||
| 48e11ab439 | |||
| f98727e08d | |||
| 7863cd3ab7 | |||
| 34c65a94bf | |||
| e2c86a96fe | |||
| b95b02d0c0 | |||
| bcfbab2add | |||
| b260c18056 | |||
| 5b33416b7b | |||
| 5b3f21c541 | |||
| 3f8efed392 | |||
| 68cdbf50b2 | |||
| 47d660041e | |||
| 817ce71e83 | |||
| 4c2ea81c53 | |||
| 7c21a42804 | |||
| e556e39cd1 | |||
| a4a437a746 | |||
| 0947a1a109 | |||
| b4bf1cb01c | |||
| 4b9a13c3a1 | |||
| 48531c6193 | |||
| b82dde240b | |||
| 5bf0b143d9 | |||
| 33bc979cfc | |||
| 50d1b9ddfd | |||
| 1bbb9c2b6e | |||
| 3b60ffeb28 | |||
| 92606e7122 | |||
| 0f85c921e3 | |||
| f752861e10 | |||
| b403e9b3b7 | |||
| eafb9a46f7 | |||
| 1e24981576 | |||
| 4c79269081 | |||
| a1a3267c0b | |||
| 6f9be482be | |||
| 29f2f34f9b | |||
| 6125643c3f | |||
| 9e8e62dc71 | |||
| d2692a2ff5 | |||
| d3b0913047 | |||
| 4290249719 | |||
| ff497b413e | |||
| ea95b4f454 | |||
| bec61a00c9 | |||
| 4054e0ab72 | |||
| f07a2715b4 | |||
| 9579b42330 | |||
| c9936058b5 | |||
| b65dac946f | |||
| f1b23ba0e9 | |||
| 3dab68b70d | |||
| 9f612465cf | |||
| be881ba9ca | |||
| cd566a6397 | |||
| 0061f997e9 | |||
| 1e8b8feee8 | |||
| 2e5967ff9e | |||
| a5947356e7 | |||
| 09255019b2 | |||
| c20e597115 | |||
| 3c65770008 | |||
| e636f12459 | |||
| d3eb48cef9 | |||
| 825b07b823 | |||
| 2d2862517a | |||
| 327b820a25 | |||
| 3c3b9e3946 | |||
| dccea19847 | |||
| 23159ce9a6 | |||
| b13be6840b | |||
| dbab435f1e | |||
| bc987a825a | |||
| d6181b042b | |||
| 80165c3c0f | |||
| 7fd51a298d | |||
| 93dbbd6bbe | |||
| 72f6b2fd11 | |||
| b51d3036e3 | |||
| 30452f0b17 | |||
| 9fba7345aa | |||
| a0cda4075d | |||
| c5819ca702 | |||
| 9be6acf449 | |||
| 6141d88407 | |||
| 4b69a7859d | |||
| 5d15db8468 | |||
| 6874a5486b | |||
| 90a282c083 | |||
| c53258a7d2 | |||
| df61a76868 | |||
| 3a0f80fc7e | |||
| add9ee73db | |||
| 132c3c0c8e | |||
| e88443e549 | |||
| e17d6df023 | |||
| 0a9bc82551 | |||
| 7d17b1be16 | |||
| 59c43fe8cb | |||
| d4b2ac3f1e | |||
| 05a40b32c6 | |||
| bf87ecb818 | |||
| 9acf913872 | |||
| fec046347b | |||
| 7261432461 | |||
| b645cc8e54 | |||
| 712a260159 | |||
| bfc2d53fa4 | |||
| 0c34de4524 | |||
| f5d904c88c | |||
| ab5c33e340 | |||
| a6d0449671 | |||
| d1edc45d4d | |||
| fde9e9b654 | |||
| d38dba1660 | |||
| 0bb69aeeee | |||
| 3fac16eda8 | |||
| 979f7b404c | |||
| 4ab42e0dc7 | |||
| fc6e17f3f9 | |||
| a6d04aa5bb | |||
| 045020e739 | |||
| ed734d0f80 | |||
| c0ab9d9e59 | |||
| bb64141207 | |||
| ad5b166f3f | |||
| 036ca7f1e5 | |||
| c2ff32aaab | |||
| 493ee63cb9 | |||
| c846d69507 | |||
| e540902ee1 | |||
| d7d82ea98d | |||
| 3eb6cd9e21 | |||
| e79e07af77 | |||
| 7614ba42df | |||
| dae8dfdc59 | |||
| 0c4712de56 | |||
| d8fbbeccfe | |||
| 452badab41 | |||
| f74a49f5a3 | |||
| eeaf8d594d | |||
| 4a480ab391 | |||
| 6bf3b19d86 | |||
| 3199aa6693 | |||
| 14c43d76c1 | |||
| bc7748aed6 | |||
| 3f8f1a215e | |||
| 69f7a1b7b9 | |||
| d59bddfe9a | |||
| d45d0a816e | |||
| 34be548aec | |||
| 1a64c0a803 | |||
| 14b97b6abe | |||
| 8d55340325 | |||
| 92d53eb527 | |||
| a6f9598a23 | |||
| 26e0c6cb79 | |||
| ad9c4ec9a5 | |||
| a36b1962c6 | |||
| b65e042b4c | |||
| b558023f9e | |||
| 169221a5da | |||
| 160d01e783 | |||
| 228480a2ef | |||
| 9d9597dbc3 | |||
| 01ecd49256 | |||
| 2ab391eb69 | |||
| a1f72826c7 | |||
| 3ba32dafa5 | |||
| d55262b322 | |||
| 7b3623a9d9 | |||
| 9abd72f623 | |||
| f9e673ca4c | |||
| 2f1c39ab96 | |||
| 7b55d2f1b2 | |||
| 7f9ca716e7 | |||
| d9cb605f22 | |||
| 8c813fa40d | |||
| 0e9eae6f73 | |||
| dcacff0124 | |||
| fe9700b3f7 | |||
| 2036da5e84 | |||
| 74a1bb9123 | |||
| 781f63b805 | |||
| b7a788e219 | |||
| 396fa73283 | |||
| 880faeebd3 | |||
| 1a298be9fa | |||
| 2353863092 | |||
| 5ebe1b19ee | |||
| 8be93f91ba | |||
| 74dcaea879 | |||
| f2e1978ddb | |||
| 4dd51f645a | |||
| f8fd255239 | |||
| 6b2831c606 | |||
| 2a54b2931c | |||
| f28c60ced3 | |||
| a20c75d3c7 | |||
| 1c8b4412e3 | |||
| 5561029e07 | |||
| 7a50856d32 | |||
| d1d9779a71 | |||
| 9571f48bd3 | |||
| 0016962bc2 | |||
| 82ba360435 | |||
| 36d7c26120 | |||
| d852422328 | |||
| 14a216778e | |||
| 06cf8abfee | |||
| 357da8ac6e | |||
| c8d9e59436 | |||
| 69af591b78 | |||
| f1b168fc46 | |||
| fe2af90caa | |||
| 3dce2c870e | |||
| a123bc7084 | |||
| fccfa4f412 | |||
| 31765a7a47 | |||
| 88eb29e199 | |||
| 0cdccc1a9b | |||
| 19633b8d70 | |||
| a331632800 | |||
| 90eae73d9b | |||
| 7bb0eb109a | |||
| c213408b92 | |||
| 0b0371af7e | |||
| b1b71a4444 | |||
| ab77623248 | |||
| bf7372c6ba | |||
| 273441a2fb | |||
| 6ff787046b | |||
| df5c685bc2 | |||
| 6c0321ce72 | |||
| aa20584588 | |||
| 99ec2d18f2 | |||
| 7ba4fd7ad1 | |||
| 2287c0e402 | |||
| e960d06449 | |||
| 134cc176ff | |||
| 126c91fe51 | |||
| cf0095dc1c | |||
| 3371dbaa48 | |||
| f11d22f43a | |||
| f02da1fdd2 | |||
| 08a886b724 | |||
| ae9141f9e7 | |||
| f49516ff13 | |||
| c2f82d184f | |||
| 35b78cc224 | |||
| 16b655fd62 | |||
| ac19aa4f91 | |||
| febc788c20 | |||
| 45bfaee590 | |||
| a025568196 | |||
| 9d3d4b77a0 | |||
| 10fbffcfcc | |||
| cca1393f2b | |||
| 69548aed20 | |||
| 27ce8c8ebc | |||
| 3f080ab277 | |||
| 7656de693c | |||
| 4806920179 | |||
| 2f146934d3 | |||
| 1002113e7d | |||
| 55aec0bf07 | |||
| 9d37e7ceda | |||
| 106c044ba5 | |||
| bda8128e93 | |||
| 4915efbb8d | |||
| c778249fed | |||
| 1340a5ce4b | |||
| 5637c44f69 | |||
| 414f838476 | |||
| d75cace85d | |||
| 2fa2e0109d | |||
| 78c3797e5a | |||
| 5d65ba3147 | |||
| cd5dc410cc | |||
| f94e0fd3ec | |||
| d39045caef | |||
| 99f0e6c05a | |||
| 2c40e46d56 | |||
| d2934f6be4 | |||
| b573b9a50a | |||
| c0026d3c23 | |||
| f0b4f31a9e | |||
| 1897c0f162 | |||
| 34f93331bd | |||
| 2e527163f9 | |||
| 7a778d4c42 | |||
| eef86689c8 | |||
| 2c3a2b29b2 | |||
| e7b0e43d98 | |||
| 66ca8c6018 | |||
| 6bf7db79e3 | |||
| cf1bf123da | |||
| 19686496e6 | |||
| 1f7a0600c6 | |||
| e2d4110685 | |||
| de237c36aa | |||
| 649aa82c63 | |||
| 9bcb2bfb38 | |||
| c272c2ad3b | |||
| fd80aecb20 | |||
| 7d2024d3fd | |||
| d24b7f2aba | |||
| 9cd2e36c4b | |||
| 47bde0f507 | |||
| 7b85ebab56 | |||
| 9c3e501a60 | |||
| 08a8e0793e | |||
| 5135635bf8 | |||
| 89a99e1c5f | |||
| 70f23b2245 | |||
| ee693b63bd | |||
| 86d8bc0c62 | |||
| 22d879ab35 | |||
| c2f51be0a1 | |||
| 052cee31b3 | |||
| 4a5eca3c6a | |||
| 9eabc1fb3f | |||
| 633d9c6fb4 | |||
| 9b4b8b9d86 | |||
| 85d4b4a61d | |||
| d901e14e4f | |||
| 27fd908517 | |||
| d262b524df | |||
| f619b03b04 | |||
| 1f09292ee3 | |||
| 9a0f50291c | |||
| a344a2f240 | |||
| fac25b5971 | |||
| c675708388 | |||
| 117b836cd7 | |||
| 918908a785 | |||
| c2a404c02d | |||
| e410b8b067 | |||
| 27ecd795b2 | |||
| e7181915ff | |||
| 9f79f1833f | |||
| df00cfab40 | |||
| f0611fff90 | |||
| da3a5d35c1 | |||
| 3c24aac81d | |||
| 9b01910d44 | |||
| 6b0158f761 | |||
| 7e8c0ed369 | |||
| cebf1e6c22 | |||
| d174499e50 | |||
| 647d92775c | |||
| a185d56ae5 | |||
| 193e24afd2 | |||
| d31e25281d | |||
| df20feb703 | |||
| ae25e4564e | |||
| 1955409582 | |||
| b709ed4aa3 | |||
| 57fb9888a2 | |||
| 54bb3384b2 | |||
| 9f2477ad75 | |||
| 35a6d721ee | |||
| a4c5b7118d | |||
| 0194ccdad7 | |||
| a2f0ad67ab | |||
| 8d4da215fa | |||
| 52a56c8852 | |||
| 32f228fa7c | |||
| 8d2c1332d3 | |||
| 0b6ba5aa52 | |||
| a13a38a482 | |||
| 7a2f09643a | |||
| c9819b2d89 | |||
| 43a97185b6 | |||
| de8516d2c7 | |||
| 98cee55dda | |||
| dfaa297104 | |||
| 5a896e7417 | |||
| 2c6b5f21b0 | |||
| b1f94c66dc | |||
| 4b3cd91188 | |||
| 19d084e086 | |||
| 66959baabe | |||
| e8cb30fbc4 | |||
| 427ac259f4 | |||
| bb07921363 | |||
| a8e8832a75 | |||
| 11f8192e52 | |||
| 15c197abd3 | |||
| b88ca1db49 | |||
| 6ee8ef0a07 | |||
| bee2776035 | |||
| 1c55f4ed4f | |||
| 8926e28706 | |||
| 6626edbfa8 | |||
| 87a5b63492 | |||
| 0f8784b9f5 | |||
| 51d2c3f351 | |||
| 544b13f05d | |||
| 5f80faa725 | |||
| d5c2e21c4a | |||
| 585abf2902 | |||
| d257396013 | |||
| d9c6d8c568 | |||
| 4067ac7d93 | |||
| 0ffa4702ba | |||
| 3e69ceb487 | |||
| 79eaeef258 | |||
| 37b614a577 | |||
| 874c89656c | |||
| 7257d19445 | |||
| 1ad0c88731 | |||
| e4c644e745 | |||
| 1f79d9e635 | |||
| 38ec97bbe3 | |||
| 74bd8fa0db | |||
| 85da2a94b9 | |||
| ba098f49c4 | |||
| c7bbad3c0c | |||
| a6d3078638 | |||
| 31a6f2d67c | |||
| b9fd04c8b9 | |||
| 5f1ddad835 | |||
| def6d67fcc | |||
| 26f0090fd0 | |||
| 344026ec23 | |||
| db3116ff9c | |||
| 49b2a9a080 | |||
| 6e1a860e77 | |||
| eef6c783ea | |||
| 9e0d2e5873 | |||
| ec8c455635 | |||
| f70df699db | |||
| 3eb01a1557 | |||
| d8751365c8 | |||
| 5842337d02 | |||
| cc897285c9 | |||
| b389e6c07f | |||
| 0480242532 | |||
| 31509beb1c | |||
| fcb3f8e791 | |||
| 03498b3c61 | |||
| 5e9f861991 | |||
| f2cc0eebf5 | |||
| 5d34ee271c | |||
| 26f43cda97 | |||
| 69a3c85bbc | |||
| f8b9fb039f | |||
| d16aa91a5e | |||
| f8b52a57c3 | |||
| ca76a5424a | |||
| d2d852f0f3 | |||
| f10b35780c | |||
| fedf0c78de | |||
| 6f25bc7c37 | |||
| e871392405 | |||
| dcb616157d | |||
| ddace54f45 | |||
| 7acafe67a4 | |||
| 9eb2b28232 | |||
| 832ec68140 | |||
| 78319bae9e | |||
| 01de2eb266 | |||
| ca98fad918 | |||
| d8f0af1a94 | |||
| fc12f2b802 | |||
| 977423e094 | |||
| c405d1b6d1 | |||
| 6983f3d43a | |||
| d380f75d31 | |||
| 8e61276b8a | |||
| a8d5bd6d1f | |||
| e252a0849b | |||
| 45735a5529 | |||
| d9cc1f4cb8 | |||
| ce4fc6654e | |||
| e1175b814e | |||
| 9b9c9ef455 | |||
| 917b83be92 | |||
| b54155d1c9 | |||
| 6d6828eaaf | |||
| e651d5917a | |||
| fd0ee6bb11 | |||
| 1fc2ea9345 | |||
| 9628d6f761 | |||
| c1e81fe40c | |||
| e36ca2e957 | |||
| ad026e25d7 | |||
| e349ead8b5 | |||
| 4981f0e828 | |||
| 7b0acf9844 | |||
| 626b9e8f7b | |||
| 62617537fa | |||
| 70315d4a5f | |||
| c8daf1d8db | |||
| 70010bc344 | |||
| 025d0eae18 | |||
| c840895f90 | |||
| a53a15f4d8 | |||
| 04a4e2d00e | |||
| 2c86e87917 | |||
| 0c07af9a41 | |||
| 53d767e978 | |||
| afd6236406 | |||
| 6fb41e364b | |||
| 76d2421f0c | |||
| 6d0f505e2d | |||
| 706b865a5f | |||
| 9e811ddd50 | |||
| dda9c5183c | |||
| 8ab5142357 | |||
| 3af859e020 | |||
| 358a36dd87 | |||
| ee0c868687 | |||
| 9522ee826f | |||
| 4a0b969777 | |||
| 9407c85d71 | |||
| 735c2ccfd7 | |||
| dd29deec6a | |||
| 1d39c177cc | |||
| f6154a38d9 | |||
| a32c8b3a68 | |||
| 82d6af2013 | |||
| ba4b84d7f4 | |||
| 57902892f9 | |||
| d4664fbae6 | |||
| 252d31a5f5 | |||
| 269273f294 | |||
| 2744afab00 | |||
| 60d86354f3 | |||
| 566895d3cd | |||
| 3fd8c63542 | |||
| d5c6e0c6e7 | |||
| ec57f35374 | |||
| 333c143005 | |||
| 46f7c76c0b | |||
| a502be606b | |||
| 0b19c07756 | |||
| d6855d18f9 | |||
| e1c9b1818e | |||
| 76bece07ef | |||
| ba85681f8c | |||
| 09cc3f89ed | |||
| 2c1ba1d23d | |||
| 07329103b1 | |||
| 7729dc946e | |||
| da58673b44 | |||
| f8b382ea0a | |||
| abbd45c476 | |||
| c56e84af2d | |||
| ed97dbc1a0 | |||
| 1506d8c3fa | |||
| 9e7eb2ad75 | |||
| 3982732b7d | |||
| 2dc9eb3a90 | |||
| 876056f0bb | |||
| 5b285f71fa | |||
| cdf38af171 | |||
| e637854149 | |||
| 6fce8810fc | |||
| 4bf17c8a4e | |||
| 4fbeac48eb | |||
| 8f64d8d70c | |||
| 01e70648ba | |||
| 7fd7d7a582 | |||
| 6f9a63d035 | |||
| 07238b54e1 | |||
| 437385fd61 | |||
| cee1171d39 | |||
| 6dd4d7f475 | |||
| a83ac4a24d | |||
| a7a3fca8a8 | |||
| 59fe603ef7 | |||
| 7a74a329ab | |||
| 0daca21b51 | |||
| 8e597aabf3 | |||
| ec58532f7b | |||
| f952fe7aeb | |||
| 5aba1a6bd2 | |||
| 6e3618f627 | |||
| 9fed610902 | |||
| 1d349f9eb0 | |||
| 27a1e51f80 | |||
| fa8a754f88 | |||
| 8f205e7d8f | |||
| a01c313345 | |||
| f394bed017 | |||
| 1ceab77aef | |||
| b3d6de5994 | |||
| fc29422b8d | |||
| 32c0d04b1b | |||
| 8ea76a92c3 | |||
| 7a68df36a3 | |||
| 537b7e5ce9 | |||
| e42995cf10 | |||
| 4234d316fe | |||
| 42f32e61e9 | |||
| 89a2c1b32d | |||
| 207bcbc10d | |||
| e1f1336205 | |||
| b9812ca5c4 | |||
| 9d2c4d7b08 | |||
| e60417925b | |||
| 5a78c000a0 | |||
| 54faeee837 | |||
| cefa2c9040 | |||
| b6f6a4015a | |||
| adc9957504 | |||
| 74a7fd50dd | |||
| 2ac175135a | |||
| 367fceb3cb | |||
| 2f408e1a01 | |||
| 8b178d1be7 | |||
| aca14c14ca | |||
| 56c4dbf77f | |||
| 5cc0f58f7d | |||
| 591d100726 | |||
| bf9284089d | |||
| 651006fbaf | |||
| 87a3350cd6 | |||
| 640b21d802 | |||
| b2d22f5650 | |||
| 650f668edb | |||
| 2eabdbb55b | |||
| e56ad2d312 | |||
| ae3c8fe08c | |||
| 37b826b82f | |||
| d744d6aabe | |||
| e6bff12337 | |||
| 174db65714 | |||
| 2c3b65c8b1 | |||
| 941ede776b | |||
| bcda4c186f | |||
| 57b8a187b8 | |||
| 98fe3e823c | |||
| 77cf071e04 | |||
| 0d0179cb47 | |||
| effed9efa7 | |||
| e6a99c5097 | |||
| d4c714eccd | |||
| 0bee01e0ee | |||
| d6c2fec5d2 | |||
| 8aaaf4439a | |||
| 1667e914cd | |||
| 67e4703821 | |||
| 4ff714ae94 | |||
| 3f89558ddb | |||
| 6b9ba3c53c | |||
| 356209e013 | |||
| 1a734141f1 | |||
| 9df1773b78 | |||
| 77dd816c51 | |||
| 0160f141f4 | |||
| cfe1b17eec | |||
| 355ac09d90 | |||
| 67c571fb28 | |||
| 5f7bf3a211 | |||
| c0e62c4c5f | |||
| b6a8ad1b6e | |||
| c9ddd783f9 | |||
| 899bb812aa | |||
| 664a892fd5 | |||
| 3ed1b44710 | |||
| c5da10cf10 | |||
| ddcead2658 | |||
| 9ac47d6e85 | |||
| 7bb3588889 | |||
| d170e4b5c3 | |||
| 9f41457f45 | |||
| ba84d95308 | |||
| b5bda0b637 | |||
| a60bdf5c01 | |||
| 35650536c8 | |||
| 8347fd79a3 | |||
| 9ffeb34643 | |||
| ae97ec4277 | |||
| 70574b6564 | |||
| 5f3596c7d9 | |||
| 4303b4eb80 | |||
| 93f991b140 | |||
| e924a8ef83 | |||
| 4ccc468cb6 | |||
| fe0d0ce1c7 | |||
| 00158b42e7 | |||
| b4f2d514be | |||
| c4418185ec | |||
| da079cf393 | |||
| d0016d6a7e | |||
| f56e2f0c72 | |||
| 26b31f1fc8 | |||
| 903d90aff2 | |||
| 572c6989ce | |||
| 31545db91e | |||
| ef3b542ff3 | |||
| 5ebf51e6df | |||
| 8f9813d597 | |||
| 8ad998ab12 | |||
| c71f438e62 | |||
| 5687287453 | |||
| a27577c17d | |||
| e8dead52c7 | |||
| 94da9a6681 | |||
| 90be93d948 | |||
| d4edbc8e79 | |||
| eb3d3e129c | |||
| 4770756621 | |||
| 1bd5127bcf | |||
| 00e951c394 | |||
| d97b100bab | |||
| b34582b35c | |||
| b7c15dc393 | |||
| 561ce94398 | |||
| f9cd22d908 | |||
| f70aea4f8a | |||
| be02d33fd6 | |||
| 0b9705ca3f | |||
| 11ffafacdf | |||
| 2e782a4f11 | |||
| 0436fb3699 | |||
| 5995a7ec84 | |||
| 20d6a3b4b4 | |||
| 16eae7c4fc | |||
| db2a003444 | |||
| 709df804e5 | |||
| f6fdc9ae2d | |||
| 62fcbd8e84 | |||
| 2a36d2625b | |||
| 93ba694106 | |||
| 56ab88983a | |||
| 8c7d29bd02 | |||
| 430c95dfad | |||
| a5ff29ccc7 | |||
| 2d2d0b200b | |||
| ff6a9ba97f | |||
| a5efc25c81 | |||
| 87b4254e39 | |||
| 5b1ca92184 | |||
| bdeee95300 | |||
| 44ab0991ce | |||
| ba38b67b9d | |||
| 0092cac394 | |||
| e27ea962d9 | |||
| 9bc047c03f | |||
| 64469fcb5c | |||
| 1f87ce02a1 | |||
| f7671ec9a7 | |||
| 862d522758 | |||
| 4ad2505bea | |||
| 525175c939 | |||
| 9ba3f47690 | |||
| e2fe1c8260 | |||
| a68b19f1ee | |||
| 8e4090fb84 | |||
| 1ac8bbc570 | |||
| 2ce3249c5e | |||
| a8638f5f43 | |||
| b9430158ce | |||
| 6ee1c96bf0 | |||
| c912feff28 | |||
| 561b359a72 | |||
| db8954e0a5 | |||
| 05f1a24caf | |||
| b08e922600 | |||
| aaedfe0309 | |||
| fbf60fbac4 | |||
| 05fbf6c38a | |||
| c17ee34edb | |||
| eb6b172be2 | |||
| b3585b0083 | |||
| aa6342cc30 | |||
| 65e98928d4 | |||
| 92c278c593 | |||
| 18c90c5325 | |||
| b865c6b476 | |||
| 941baf09ce | |||
| be44741bf9 | |||
| 3b8a570c3c | |||
| 74986d24f1 | |||
| 282ac2b9c5 | |||
| 1224f68297 | |||
| 8d1733fd12 | |||
| 1213551ca9 | |||
| 9968cd257d | |||
| 168febae24 | |||
| b354eda7ac | |||
| f620d0deca | |||
| d350ab0789 | |||
| 0f7cb7c177 | |||
| c94a6b1777 | |||
| d1cfe84723 | |||
| cd6cdcfda4 | |||
| c40d30d2dc | |||
| ef8ffc5149 | |||
| 2ebd7a077c | |||
| c619b79276 | |||
| 291d58900f | |||
| 1c401b9589 | |||
| 5715b26bb7 | |||
| 21ffb5cdb9 | |||
| e88c789b88 | |||
| 8f41fe8d38 | |||
| 29390a3624 | |||
| ca75b2f4d3 | |||
| fded793f8e | |||
| 5a23ca9de7 | |||
| 8f1cd8110d | |||
| 4566b88e37 | |||
| 250c24de40 | |||
| ab565f1d27 | |||
| b6f4ec3cbc | |||
| f02bd4bca4 | |||
| dcf2e4830f | |||
| 9e33581391 | |||
| d0153b1105 | |||
| 586a7a167a | |||
| 798c01c514 | |||
| a3fc321296 | |||
| 0330be4e6b | |||
| ba282ab81a | |||
| 19233c2e5f | |||
| 677311aa49 | |||
| a5630a3448 | |||
| c89b50f0b8 | |||
| 687aa8cc99 | |||
| 74c01af369 | |||
| 7dbd8a3515 | |||
| 2e8a6c88a6 | |||
| 59349dd355 | |||
| 4170faf76e | |||
| cc13b99787 | |||
| 4586a55571 | |||
| 2baf3b5b41 | |||
| 09d28f720a | |||
| ed7c8da81f | |||
| 003907358d | |||
| 95ef6cd249 | |||
| d69f0fe9dd | |||
| 4700d8a69c | |||
| 167084b39e | |||
| 875b7fc354 | |||
| 10d0c79c1d | |||
| 7ed66f41dc | |||
| 7db3644fcd | |||
| b000ead96b | |||
| eafbf6dcae | |||
| 228fca04ec | |||
| d2baf03119 | |||
| 4bf3c1d05b | |||
| 568d5c2583 | |||
| 5cb99bfddc | |||
| 091c9471bb | |||
| d672531b0b | |||
| b0b0477dab | |||
| b8d2309b9c | |||
| 7421378330 | |||
| 8594af3fe3 | |||
| 28d93175d5 | |||
| 50b98a65d3 | |||
| 5f972384b1 | |||
| 944d47f669 | |||
| 2bc63b9700 | |||
| 49668a48bc | |||
| ff085c97ce | |||
| 39028cd240 | |||
| 36541fa77c | |||
| cd1338db82 | |||
| aea41937f7 | |||
| 898f1f2397 | |||
| 10cf10b33a | |||
| fe689e6855 | |||
| 6d808cb121 | |||
| bea740c6e0 | |||
| a809e35e5d | |||
| d41dbce601 | |||
| e27b873a5a | |||
| ec60ba142a | |||
| 1772bc1e05 | |||
| 6e8b00d85c | |||
| fa82e1660b | |||
| b3414166a6 | |||
| 70aa29ec7a | |||
| 9ef8e9afc1 | |||
| 2e558ed58d | |||
| 4b58e34220 | |||
| a955ef2b36 | |||
| 640de1c1a4 | |||
| e73884250a | |||
| e6abfe0720 | |||
| c7598bf647 | |||
| a1ad04e04e | |||
| 0267378bf9 | |||
| 9bbe743686 | |||
| a16e31c671 | |||
| d19a23eec6 | |||
| 0f61de8bcd | |||
| 5b17072d55 | |||
| 51875539b9 | |||
| 030fd4d375 | |||
| 7658fba725 | |||
| caf7a6a1ca | |||
| 349f48aa2d | |||
| 2422000b3e | |||
| e0a38a4d9b | |||
| 33ee239406 | |||
| f2dac9df3c | |||
| 429423c07f | |||
| 086157cff8 | |||
| 89b5984794 | |||
| b9d779e921 | |||
| c6d13ebc39 | |||
| 801e9da028 | |||
| 340a7e5383 | |||
| 4a1ae0573f | |||
| e0511646af | |||
| f7c70bde1e | |||
| 21612454d0 | |||
| d53d0b4271 | |||
| 4564623899 | |||
| 9ce35d25d2 | |||
| cc6c16894c | |||
| 4ca95a11ee | |||
| edd2adc8a0 | |||
| 2ca7bb0cf3 | |||
| 0356e5a224 | |||
| 118ad5361a | |||
| e20e570dc8 | |||
| d7af9d9fca | |||
| 03ce922124 | |||
| c61b5f947d | |||
| 0852b20b31 | |||
| e1906113b9 | |||
| bb6848dd03 | |||
| 8304ada528 | |||
| d50061b4af | |||
| baa37549fb | |||
| 6490f57f70 | |||
| eda46a16d0 | |||
| 986a7f368f | |||
| 4651325d77 | |||
| f1197a6db2 | |||
| 70dd85f708 | |||
| d0623bc494 | |||
| 193bc74f26 | |||
| 3d391fdfb6 | |||
| e12d374ffa | |||
| ec24ebf93c | |||
| acc238f6ef | |||
| 20a65166be | |||
| 2f7772d40a | |||
| 53d7023b40 | |||
| ce3729678e | |||
| 90b0a8b14b | |||
| a411d13d0e | |||
| f390e2979d | |||
| bb5a561932 | |||
| c94cfc0156 | |||
| b35a8b406b | |||
| 8252f4d95a | |||
| c7b49d928a | |||
| 5d3432db51 | |||
| ac3d82b2d8 | |||
| a9aba6731d | |||
| fbf29f9f47 | |||
| 6566a43ef9 | |||
| 7f380347dc | |||
| 2013fd1619 | |||
| 57f462b204 | |||
| c67cb46510 | |||
| 6f5155b0f3 | |||
| e566e240b1 | |||
| 6c25062e4b | |||
| 34dc81058a | |||
| 9409b46ef4 | |||
| 7df0869649 | |||
| 3c399ade16 | |||
| dfe224486c | |||
| 465229fb39 | |||
| c7e6cf44ed | |||
| 452f97de62 | |||
| 17a005c163 | |||
| 68006f78ee | |||
| de86dace70 | |||
| cd8bb8ed99 | |||
| 9df8c71bf4 | |||
| 17d489bb54 | |||
| 046b35e242 | |||
| 3546de3207 | |||
| 849cc2d383 | |||
| 5870db0273 | |||
| 8425c3124d | |||
| 74d9945e09 | |||
| 749a25aabb | |||
| 28c4b2ee7f | |||
| 00428ec2de | |||
| d77796bcac | |||
| e599685684 | |||
| bd505a8574 | |||
| a4bdcf99b1 | |||
| bd455aaede | |||
| 04e8fc7fa3 | |||
| cb7c1f9bbd | |||
| e620d26769 | |||
| b5e12aa29e | |||
| cbc6a2ddfd | |||
| 9b39c6ad5b | |||
| a50a46f9fa | |||
| e8818b36bd | |||
| 7eee1ac76c | |||
| 9f2043c68d | |||
| 880176e91e | |||
| 1452a702e0 | |||
| 28fa60ea46 | |||
| e2455b14d9 | |||
| f6b0b35ff7 | |||
| c62f25c9a8 | |||
| 1d093b4ecb | |||
| b8409cdd2e | |||
| d9dc32edcb | |||
| b1244dfa1b | |||
| b22ae2cf9f | |||
| 818e9f8b4c | |||
| 470585b50d | |||
| cc8cb96140 | |||
| 8275050dee | |||
| 23c4f88ce7 | |||
| 6dd260a825 | |||
| 8183f32399 | |||
| 7c971b8366 | |||
| 8f6af7b41e | |||
| 4a4a897ab1 | |||
| ccd6711a8a | |||
| 2e3cf979d3 | |||
| 04d6704766 | |||
| 75e1c9a0ae | |||
| 355447d4e2 | |||
| 993972fa62 | |||
| 5cc4370dc1 | |||
| 1d5ae45b06 | |||
| 271e01262f | |||
| b25413abcd | |||
| 7bfce6e428 | |||
| ecb4e00516 | |||
| 110ecc5fda | |||
| d0908895dd | |||
| a5e9ce515f | |||
| 21be52c60a | |||
| 7091658a3b | |||
| 2f6e66184f | |||
| c5b7543e54 | |||
| 7a23fdc1ea | |||
| 758eea479d | |||
| 398134251e | |||
| 24a6aecd73 | |||
| ef30c1f005 | |||
| 39bb4f3a1e | |||
| 569241eba3 | |||
| bce6375300 | |||
| 56f1637631 | |||
| ee8da06638 | |||
| 1c9a960f3d | |||
| 436a6df917 | |||
| 45e892a4b1 | |||
| f2667210b7 | |||
| dce485bc6f | |||
| 5b1e7e9b3d | |||
| 397b3ca69f | |||
| abcdfc27c5 | |||
| 634c2072ca | |||
| 25718dbe5a | |||
| 92aa41fe33 | |||
| 3c747a1be6 | |||
| 0dcbc9f303 | |||
| 5084c82ac7 | |||
| 45b5cb0257 | |||
| cb166526dc | |||
| 01c3585bd8 | |||
| e0414414d7 | |||
| 168f4d33d3 | |||
| e92563ffd8 | |||
| 964dd0374d | |||
| 23245a578a | |||
| 2ed048e4e1 | |||
| b54998f0af | |||
| 9f5fe7ddfe | |||
| 1931011152 | |||
| 2f9459f296 | |||
| 78b8e78761 | |||
| 33fae0cd40 | |||
| 2a0d73220a | |||
| 9f484d0fe2 | |||
| 5bbe17af54 | |||
| 603a1c7814 | |||
| 6bee3a421f | |||
| 385bc9c770 | |||
| bad002b58a | |||
| a3a95c0f9d | |||
| 142a319776 | |||
| 756e0bbbe1 | |||
| 1839c5b3a8 | |||
| 3886d47c57 | |||
| 962bdcf1b4 | |||
| 630a8af217 | |||
| 57a7cf3903 | |||
| 4501368cc3 | |||
| 0933443cb3 | |||
| c3cfee7b2c | |||
| d38e4a0442 | |||
| 851c11153f | |||
| 6d7fcb7d64 | |||
| 0844cb15e0 | |||
| 53bdef68f4 | |||
| c7bff0f9ff | |||
| 64384dc8b3 | |||
| ce83e25390 | |||
| 8cd28a62ea | |||
| c403df1990 | |||
| b4164521f7 | |||
| ab3c5d4d52 | |||
| 96d76c211c | |||
| 20cf8a7637 | |||
| b15e8a13db | |||
| c3fa12be6e | |||
| 04f7b14702 | |||
| 08618b653c | |||
| cce70f139c | |||
| e4c7609675 | |||
| cc915d6ef1 | |||
| 38e8feb588 | |||
| fcf8290df9 | |||
| 424ae7bb91 | |||
| 4ce33c4e3d | |||
| 50e3f84759 | |||
| fbd48b0bd7 | |||
| 152ddbd252 | |||
| 3e75c69f10 | |||
| df13af9dc2 | |||
| cae468eaeb | |||
| cb5dca5544 | |||
| fbba60a56b | |||
| 4d66b21084 | |||
| 1a89fea816 |
+4
-1
@@ -34,4 +34,7 @@ dotnet_naming_style.prefix_underscore.required_prefix = _
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
csharp_prefer_braces = false : none
|
||||
csharp_prefer_braces = false : none
|
||||
|
||||
[*.{js,less}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
@@ -104,7 +104,6 @@ There's two big areas that you should know about:
|
||||
You may need to run the following commands to set up gulp properly:
|
||||
```
|
||||
npm cache clean
|
||||
npm install -g bower
|
||||
npm install -g gulp
|
||||
npm install -g gulp-cli
|
||||
npm install
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
_You are browsing the Umbraco v8 branch. Umbraco 8 is currently under development._
|
||||
[](https://pullreminders.com?ref=badge)
|
||||
|
||||
_Looking for Umbraco version 7? [Click here](https://github.com/umbraco/Umbraco-CMS) to go to the v7 branch._
|
||||
|
||||
@@ -6,7 +7,7 @@ _Ready to try out Version 8? [See the quick start guide](V8_GETTING_STARTED.md).
|
||||
|
||||
When is Umbraco 8 coming?
|
||||
=========================
|
||||
When it's ready. We're done with the major parts of the architecture work and are focusing on three seperate tracks to prepare Umbraco 8 for release:
|
||||
When it's ready. We're done with the major parts of the architecture work and are focusing on three separate tracks to prepare Umbraco 8 for release:
|
||||
1) Editor Track (_currently in progress_). Without editors, there's no market for Umbraco. So we want to make sure that Umbraco 8 is full of love for editors.
|
||||
2) Partner Track. Without anyone implementing Umbraco, there's nothing for editors to update. So we want to make sure that Umbraco 8 is a joy to implement
|
||||
3) Contributor Track. Without our fabulous ecosystem of both individual Umbracians and 3rd party ISVs, Umbraco wouldn't be as rich a platform as it is today. We want to make sure that it's easy, straight forward and as backwards-compatible as possible to create packages for Umbraco
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
* Open the `/src/umbraco.sln` Visual Studio solution
|
||||
* Start the solution (easiest way is to use `ctrl + F5`)
|
||||
* When the solution is first built this may take some time since it will restore all nuget, npm and bower packages, build the .net solution and also build the angular solution
|
||||
* When the solution is first built this may take some time since it will restore all nuget and npm packages, build the .net solution and also build the angular solution
|
||||
* When the website starts you'll see the Umbraco installer and just follow the prompts
|
||||
* You're all set!
|
||||
|
||||
|
||||
+2
-1
@@ -13,6 +13,7 @@
|
||||
*.suo
|
||||
*.vs10x
|
||||
*.ndproj
|
||||
*.ignorer.*
|
||||
|
||||
# common directories
|
||||
.DS_Store
|
||||
@@ -156,4 +157,4 @@ build/temp/
|
||||
|
||||
|
||||
|
||||
# eof
|
||||
# eof
|
||||
|
||||
@@ -3,57 +3,57 @@
|
||||
[string]$Directory
|
||||
)
|
||||
$workingDirectory = $Directory
|
||||
CD $workingDirectory
|
||||
CD "$($workingDirectory)"
|
||||
|
||||
# Clone repo
|
||||
$fullGitUrl = "https://$env:GIT_URL/$env:GIT_REPOSITORYNAME.git"
|
||||
git clone $fullGitUrl 2>&1 | % { $_.ToString() }
|
||||
$fullGitUrl = "https://$($env:GIT_URL)/$($env:GIT_REPOSITORYNAME).git"
|
||||
git clone $($fullGitUrl) $($env:GIT_REPOSITORYNAME) 2>&1 | % { $_.ToString() }
|
||||
|
||||
# Remove everything so that unzipping the release later will update everything
|
||||
# Don't remove the readme file nor the git directory
|
||||
Write-Host "Cleaning up git directory before adding new version"
|
||||
Remove-Item -Recurse $workingDirectory\$env:GIT_REPOSITORYNAME\* -Exclude README.md,.git
|
||||
Remove-Item -Recurse "$($workingDirectory)\$($env:GIT_REPOSITORYNAME)\*" -Exclude README.md,.git
|
||||
|
||||
# Find release zip
|
||||
$zipsDir = "$workingDirectory\$env:BUILD_DEFINITIONNAME\zips"
|
||||
$zipsDir = "$($workingDirectory)\$($env:BUILD_DEFINITIONNAME)\zips"
|
||||
$pattern = "UmbracoCms.([0-9]{1,2}.[0-9]{1,3}.[0-9]{1,3}).zip"
|
||||
Write-Host "Searching for Umbraco release files in $workingDirectory\$zipsDir for a file with pattern $pattern"
|
||||
$file = (Get-ChildItem $zipsDir | Where-Object { $_.Name -match "$pattern" })
|
||||
Write-Host "Searching for Umbraco release files in $($zipsDir) for a file with pattern $($pattern)"
|
||||
$file = (Get-ChildItem "$($zipsDir)" | Where-Object { $_.Name -match "$($pattern)" })
|
||||
|
||||
if($file)
|
||||
{
|
||||
# Get release name
|
||||
$version = [regex]::Match($file.Name, $pattern).captures.groups[1].value
|
||||
$releaseName = "Umbraco $version"
|
||||
Write-Host "Found $releaseName"
|
||||
$version = [regex]::Match($($file.Name), $($pattern)).captures.groups[1].value
|
||||
$releaseName = "Umbraco $($version)"
|
||||
Write-Host "Found $($releaseName)"
|
||||
|
||||
# Unzip into repository to update release
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
Write-Host "Unzipping $($file.FullName) to $workingDirectory\$env:GIT_REPOSITORYNAME"
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory("$($file.FullName)", "$workingDirectory\$env:GIT_REPOSITORYNAME")
|
||||
Write-Host "Unzipping $($file.FullName) to $($workingDirectory)\$($env:GIT_REPOSITORYNAME)"
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory("$($file.FullName)", "$($workingDirectory)\$($env:GIT_REPOSITORYNAME)")
|
||||
|
||||
# Telling git who we are
|
||||
git config --global user.email "coffee@umbraco.com" 2>&1 | % { $_.ToString() }
|
||||
git config --global user.name "Umbraco HQ" 2>&1 | % { $_.ToString() }
|
||||
|
||||
# Commit
|
||||
CD $env:GIT_REPOSITORYNAME
|
||||
Write-Host "Committing Umbraco $version Release from Build Output"
|
||||
CD "$($workingDirectory)\$($env:GIT_REPOSITORYNAME)"
|
||||
Write-Host "Committing Umbraco $($version) Release from Build Output"
|
||||
|
||||
git add . 2>&1 | % { $_.ToString() }
|
||||
git commit -m " Release $releaseName from Build Output" 2>&1 | % { $_.ToString() }
|
||||
git commit -m " Release $($releaseName) from Build Output" 2>&1 | % { $_.ToString() }
|
||||
|
||||
# Tag the release
|
||||
git tag -a "v$version" -m "v$version"
|
||||
git tag -a "v$($version)" -m "v$($version)"
|
||||
|
||||
# Push release to master
|
||||
$fullGitAuthUrl = "https://$($env:GIT_USERNAME):$GitHubPersonalAccessToken@$env:GIT_URL/$env:GIT_REPOSITORYNAME.git"
|
||||
git push $fullGitAuthUrl 2>&1 | % { $_.ToString() }
|
||||
$fullGitAuthUrl = "https://$($env:GIT_USERNAME):$($GitHubPersonalAccessToken)@$($env:GIT_URL)/$($env:GIT_REPOSITORYNAME).git"
|
||||
git push $($fullGitAuthUrl) 2>&1 | % { $_.ToString() }
|
||||
|
||||
#Push tag to master
|
||||
git push $fullGitAuthUrl --tags 2>&1 | % { $_.ToString() }
|
||||
git push $($fullGitAuthUrl) --tags 2>&1 | % { $_.ToString() }
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Error "Umbraco release file not found, searched in $workingDirectory\$zipsDir for a file with pattern $pattern - cancelling"
|
||||
Write-Error "Umbraco release file not found, searched in $($workingDirectory)\$($zipsDir) for a file with pattern $($pattern) - canceling"
|
||||
}
|
||||
|
||||
@@ -8,38 +8,42 @@
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<iconUrl>https://umbraco.com/dist/nuget/logo-small.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
|
||||
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<!--
|
||||
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
|
||||
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
|
||||
not want this to happen as the alpha of the next major is, really, the next major already.
|
||||
-->
|
||||
<dependency id="AutoMapper" version="[7.0.1,7.999999)" />
|
||||
<dependency id="LightInject" version="[5.1.2,5.999999)" />
|
||||
<dependency id="LightInject.Annotation" version="[1.1.0,1.999999)" />
|
||||
<dependency id="LightInject.Web" version="[2.0.0,2.999999)" />
|
||||
<dependency id="Microsoft.AspNet.Identity.Core" version="[2.2.2,2.999999)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Client" version="[5.2.6,5.999999)" />
|
||||
<dependency id="Microsoft.Owin" version="[4.0.0,4.999999)" />
|
||||
<dependency id="MiniProfiler" version="[3.2.0.157,3.999999)" />
|
||||
<dependency id="MySql.Data" version="[6.10.7,6.999999)" />
|
||||
<dependency id="Newtonsoft.Json" version="[11.0.2,11.999999)" />
|
||||
<dependency id="Semver" version="[2.0.4,2.999999)" />
|
||||
<dependency id="Serilog" version="[2.7.1,2.999999)" />
|
||||
<dependency id="Serilog.Enrichers.Process" version="[2.0.1,2.999999)" />
|
||||
<dependency id="Serilog.Enrichers.Thread" version="[3.0.0,3.999999)" />
|
||||
<dependency id="Serilog.Filters.Expressions" version="[2.0.0,2.999999)" />
|
||||
<dependency id="Serilog.Formatting.Compact" version="[1.0.0,1.999999)" />
|
||||
<dependency id="Serilog.Settings.AppSettings" version="[2.2.2,2.999999)" />
|
||||
<dependency id="Serilog.Sinks.File" version="[4.0.0,4.999999)" />
|
||||
<dependency id="Umbraco.SqlServerCE" version="[4.0.0.1,4.999999)" />
|
||||
<dependency id="NPoco" version="[3.9.4,3.999999)" />
|
||||
|
||||
<group targetFramework="net472">
|
||||
<!--
|
||||
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
|
||||
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
|
||||
not want this to happen as the alpha of the next major is, really, the next major already.
|
||||
-->
|
||||
<dependency id="AutoMapper" version="[8.0.0,8.999999)" />
|
||||
<dependency id="LightInject" version="[5.4.0,5.999999)" />
|
||||
<dependency id="LightInject.Annotation" version="[1.1.0,1.999999)" />
|
||||
<dependency id="LightInject.Web" version="[2.0.0,2.999999)" />
|
||||
<dependency id="Microsoft.AspNet.Identity.Core" version="[2.2.2,2.999999)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi.Client" version="[5.2.7,5.999999)" />
|
||||
<dependency id="Microsoft.Owin" version="[4.0.1,4.999999)" />
|
||||
<dependency id="MiniProfiler" version="[4.0.138,4.999999)" />
|
||||
<dependency id="Newtonsoft.Json" version="[12.0.1,12.999999)" />
|
||||
<dependency id="Semver" version="[2.0.4,2.999999)" />
|
||||
<dependency id="Serilog" version="[2.8.0,2.999999)" />
|
||||
<dependency id="Serilog.Enrichers.Process" version="[2.0.1,2.999999)" />
|
||||
<dependency id="Serilog.Enrichers.Thread" version="[3.0.0,3.999999)" />
|
||||
<dependency id="Serilog.Filters.Expressions" version="[2.0.0,2.999999)" />
|
||||
<dependency id="Serilog.Formatting.Compact" version="[1.0.0,1.999999)" />
|
||||
<dependency id="Serilog.Formatting.Compact.Reader" version="[1.0.3,1.999999)" />
|
||||
<dependency id="Serilog.Settings.AppSettings" version="[2.2.2,2.999999)" />
|
||||
<dependency id="Serilog.Sinks.File" version="[4.0.0,4.999999)" />
|
||||
<dependency id="Umbraco.SqlServerCE" version="[4.0.0.1,4.999999)" />
|
||||
<dependency id="NPoco" version="[3.9.4,3.999999)" />
|
||||
</group>
|
||||
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
|
||||
@@ -8,37 +8,42 @@
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<iconUrl>https://umbraco.com/dist/nuget/logo-small.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Contains the web assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
|
||||
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<!--
|
||||
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
|
||||
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
|
||||
not want this to happen as the alpha of the next major is, really, the next major already.
|
||||
-->
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="AutoMapper" version="[7.0.1,7.999999)" />
|
||||
<dependency id="ClientDependency" version="[1.9.7,1.999999)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0,1.999999)" />
|
||||
<dependency id="CSharpTest.Net.Collections" version="[14.906.1403.1082,14.999999)" />
|
||||
<dependency id="Examine" version="[1.0.0-beta067,1.999999)" />
|
||||
<dependency id="HtmlAgilityPack" version="[1.8.9,1.999999)" />
|
||||
<dependency id="ImageProcessor" version="[2.6.2.25,2.999999)" />
|
||||
<dependency id="LightInject.Mvc" version="[2.0.0,2.999999)" />
|
||||
<dependency id="LightInject.WebApi" version="[2.0.0,2.999999)" />
|
||||
<dependency id="Markdown" version="[2.2.1,2.999999)" />
|
||||
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.2,2.999999)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[5.2.6,5.999999)" />
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.3,2.999999)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.6,5.999999)" />
|
||||
<dependency id="Microsoft.Owin.Host.SystemWeb" version="[4.0.0,4.999999)" />
|
||||
<dependency id="Microsoft.Owin.Security.Cookies" version="[4.0.0,4.999999)" />
|
||||
<dependency id="Microsoft.Owin.Security.OAuth" version="[4.0.0,4.999999)" />
|
||||
<dependency id="System.Threading.Tasks.Dataflow" version="[4.8.0,4.999999)" />
|
||||
<dependencies>
|
||||
|
||||
<group targetFramework="net472">
|
||||
|
||||
<!--
|
||||
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
|
||||
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
|
||||
not want this to happen as the alpha of the next major is, really, the next major already.
|
||||
-->
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="ClientDependency" version="[1.9.7,1.999999)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0,1.999999)" />
|
||||
<dependency id="CSharpTest.Net.Collections" version="[14.906.1403.1082,14.999999)" />
|
||||
<dependency id="Examine" version="[1.0.0-beta078,1.999999)" />
|
||||
<dependency id="HtmlAgilityPack" version="[1.8.14,1.999999)" />
|
||||
<dependency id="ImageProcessor" version="[2.6.2.25,2.999999)" />
|
||||
<dependency id="LightInject.Mvc" version="[2.0.0,2.999999)" />
|
||||
<dependency id="LightInject.WebApi" version="[2.0.0,2.999999)" />
|
||||
<dependency id="Markdown" version="[2.2.1,2.999999)" />
|
||||
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.2,2.999999)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[5.2.7,5.999999)" />
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.4.0,2.999999)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.7,5.999999)" />
|
||||
<dependency id="Microsoft.Owin.Host.SystemWeb" version="[4.0.1,4.999999)" />
|
||||
<dependency id="Microsoft.Owin.Security.Cookies" version="[4.0.1,4.999999)" />
|
||||
<dependency id="Microsoft.Owin.Security.OAuth" version="[4.0.1,4.999999)" />
|
||||
<dependency id="System.Threading.Tasks.Dataflow" version="[4.9.0,4.999999)" />
|
||||
|
||||
</group>
|
||||
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
|
||||
@@ -8,32 +8,38 @@
|
||||
<owners>Umbraco HQ</owners>
|
||||
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
|
||||
<projectUrl>http://umbraco.com/</projectUrl>
|
||||
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
|
||||
<iconUrl>https://umbraco.com/dist/nuget/logo-small.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Installs Umbraco Cms in your Visual Studio ASP.NET project</description>
|
||||
<summary>Installs Umbraco Cms in your Visual Studio ASP.NET project</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Web" version="[$version$]" />
|
||||
<!--
|
||||
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
|
||||
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
|
||||
not want this to happen as the alpha of the next major is, really, the next major already.
|
||||
-->
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.3, 2.999999)" />
|
||||
<dependency id="Umbraco.ModelsBuilder.Ui" version="[8.0.0-alpha.29]" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.9.3.25,4.999999)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.4.1.19,2.999999)" />
|
||||
<dependency id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="[2.0.1,2.999999)" />
|
||||
<dependency id="Microsoft.Net.Compilers" version="[2.10.0,2.999999)" />
|
||||
|
||||
<group targetFramework="net472">
|
||||
|
||||
<dependency id="UmbracoCms.Web" version="[$version$]" />
|
||||
<!--
|
||||
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
|
||||
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
|
||||
not want this to happen as the alpha of the next major is, really, the next major already.
|
||||
-->
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.4.0, 2.999999)" />
|
||||
<dependency id="Umbraco.ModelsBuilder.Ui" version="[8.0.0-alpha.37]" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.9.3.25,4.999999)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.4.1.19,2.999999)" />
|
||||
<dependency id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="[2.0.1,2.999999)" />
|
||||
<dependency id="Microsoft.Net.Compilers" version="[2.10.0,2.999999)" />
|
||||
|
||||
</group>
|
||||
|
||||
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<!-- files -->
|
||||
<file src="$BuildTmp$\Configs\**" target="Content\Config" exclude="$BuildTmp$\Configs\Web.config.transform" />
|
||||
<file src="$BuildTmp$\WebApp\Views\**" target="Content\Views" exclude="$BuildTmp$\WebApp\Views\Web.config" />
|
||||
<file src="$BuildTmp$\WebApp\default.aspx" target="Content\default.aspx" />
|
||||
<file src="$BuildTmp$\WebApp\Global.asax" target="Content\Global.asax" />
|
||||
<file src="$BuildTmp$\WebApp\config\BackOfficeTours\**" target="Content\Config\BackOfficeTours" />
|
||||
<file src="$BuildTmp$\WebApp\Media\Web.config" target="Content\Media\Web.config" />
|
||||
@@ -52,12 +58,9 @@
|
||||
<!-- config transforms -->
|
||||
<!-- beware! config transforms not supported by PackageReference -->
|
||||
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
|
||||
<file src="tools\applications.config.install.xdt" target="Content\config\applications.config.install.xdt" />
|
||||
<file src="tools\ClientDependency.config.install.xdt" target="Content\config\ClientDependency.config.install.xdt" />
|
||||
<file src="tools\Dashboard.config.install.xdt" target="Content\config\Dashboard.config.install.xdt" />
|
||||
<file src="tools\trees.config.install.xdt" target="Content\config\trees.config.install.xdt" />
|
||||
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
|
||||
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" /> <!-- FIXME Content\ !! and then... transform?! -->
|
||||
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" /> <!-- FIXME: Content\ !! and then... transform?! -->
|
||||
|
||||
<!-- UmbracoCms props and targets -->
|
||||
<file src="build\**" target="build" />
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<dashBoard xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<section alias="StartupSettingsDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Welcome" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
|
||||
<control panelCaption="" xdt:Transform="InsertIfMissing">
|
||||
views/dashboard/settings/settingsdashboardintro.html
|
||||
</control>
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="StartupFormsDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<areas>
|
||||
<area>forms</area>
|
||||
</areas>
|
||||
<tab caption="Install Umbraco Forms">
|
||||
<control panelCaption="">
|
||||
views/dashboard/forms/formsdashboardintro.html
|
||||
</control>
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<areas xdt:Transform="InsertIfMissing">
|
||||
<area xdt:Transform="InsertIfMissing">developer</area>
|
||||
</areas>
|
||||
</section>
|
||||
|
||||
<section alias="StartupDeveloperDashboardSection">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Replace">
|
||||
<control panelCaption="">
|
||||
views/dashboard/developer/developerdashboardvideos.html
|
||||
</control>
|
||||
</tab>
|
||||
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
</section>
|
||||
|
||||
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
|
||||
<control>
|
||||
views/dashboard/developer/examinemanagement.html
|
||||
</control>
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="StartupMediaDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Content" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing" />
|
||||
<tab caption="Content" xdt:Locator="Match(caption)" xdt:Transform="Replace">
|
||||
<control panelCaption="">
|
||||
views/dashboard/media/mediafolderbrowser.html
|
||||
</control>
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="StartupMemberDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Get Started" xdt:Transform="Insert">
|
||||
<control panelCaption="">
|
||||
views/dashboard/members/membersdashboardvideos.html
|
||||
</control>
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="uGoLiveDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="Remove" />
|
||||
|
||||
<section alias="ExamineManagement" xdt:Locator="Match(alias)" xdt:Transform="Remove" />
|
||||
|
||||
<section alias="StartupDashboardSection">
|
||||
<tab caption="Change Password" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Last Edits" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
</section>
|
||||
|
||||
<section alias="RedirectUrlManagement" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<areas>
|
||||
<area>content</area>
|
||||
</areas>
|
||||
<tab caption="Redirect URL Management">
|
||||
<control>
|
||||
views/dashboard/developer/redirecturls.html
|
||||
</control>
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="UmbracoHealthCheck" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<areas>
|
||||
<area>developer</area>
|
||||
</areas>
|
||||
<tab caption="Health Check">
|
||||
<control>
|
||||
views/dashboard/developer/healthcheck.html
|
||||
</control>
|
||||
</tab>
|
||||
</section>
|
||||
</dashBoard>
|
||||
@@ -2,17 +2,11 @@
|
||||
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<configSections xdt:Transform="InsertIfMissing" />
|
||||
<configSections>
|
||||
<section name="FileSystemProviders" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<section name="ExamineLuceneIndexSets" type="Umbraco.Examine.Config.IndexSets, Umbraco.Examine" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="SetAttributes(type,requirePermission)" />
|
||||
|
||||
<sectionGroup name="applicationSettings" xdt:Locator="Match(name)">
|
||||
<section name="umbraco.presentation.Properties.Settings" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
</sectionGroup>
|
||||
<sectionGroup name="applicationSettings" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<sectionGroup name="system.web.webPages.razor" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<sectionGroup name="umbracoConfiguration" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing">
|
||||
<section name="settings" type="Umbraco.Core.Configuration.UmbracoSettings.UmbracoSettingsSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
|
||||
<section name="FileSystemProviders" type="Umbraco.Core.Configuration.FileSystemProvidersSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
|
||||
<section name="dashBoard" type="Umbraco.Core.Configuration.Dashboard.DashboardSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
|
||||
<section name="dashBoard" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<section name="HealthChecks" type="Umbraco.Core.Configuration.HealthChecks.HealthChecksSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
@@ -22,26 +16,18 @@
|
||||
<add key="owin:appStartup" value="UmbracoDefaultOwinStartup" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
|
||||
<add key="Umbraco.ModelsBuilder.Enable" value="false" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
|
||||
<add key="Umbraco.ModelsBuilder.ModelsMode" value="Nothing" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
|
||||
<add key="umbracoDefaultUILanguage" value="en-US" xdt:Locator="Match(key)" xdt:Transform="SetAttributes(value)" />
|
||||
</appSettings>
|
||||
|
||||
<umbracoConfiguration xdt:Transform="InsertIfMissing">
|
||||
<settings configSource="config\umbracoSettings.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
|
||||
<FileSystemProviders configSource="config\FileSystemProviders.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
|
||||
<dashBoard configSource="config\Dashboard.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
|
||||
<dashBoard configSource="config\Dashboard.config" xdt:Locator="Match(configSource)" xdt:Transform="Remove" />
|
||||
<HealthChecks configSource="config\HealthChecks.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
|
||||
</umbracoConfiguration>
|
||||
|
||||
<FileSystemProviders xdt:Transform="Remove" />
|
||||
|
||||
<system.data xdt:Transform="InsertIfMissing">
|
||||
<DbProviderFactories xdt:Transform="InsertIfMissing">
|
||||
<remove invariant="System.Data.SqlServerCe.4.0" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
|
||||
<add name="Microsoft SQL Server Compact Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe" xdt:Locator="Match(invariant)" xdt:Transform="SetAttributes(invariant,description,type)" />
|
||||
<remove invariant="MySql.Data.MySqlClient" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
|
||||
<add invariant="MySql.Data.MySqlClient" xdt:Locator="Match(invariant)" xdt:Transform="Remove" />
|
||||
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
|
||||
<add invariant="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" xdt:Locator="Match(invariant)" xdt:Transform="SetAttributes(type)" />
|
||||
</DbProviderFactories>
|
||||
</system.data>
|
||||
<clientDependency xdt:Transform="RemoveAttributes(version)" />
|
||||
@@ -49,14 +35,9 @@
|
||||
<system.web xdt:Transform="InsertIfMissing" />
|
||||
<system.web>
|
||||
<siteMap xdt:Transform="Remove" />
|
||||
<siteMap xdt:Transform="InsertIfMissing">
|
||||
<providers xdt:Transform="InsertIfMissing">
|
||||
<remove name="MySqlSiteMapProvider" xdt:Transform="InsertIfMissing" />
|
||||
</providers>
|
||||
</siteMap>
|
||||
<httpRuntime xdt:Transform="InsertIfMissing" />
|
||||
<httpRuntime maxRequestLength="51200" fcnMode="Single" xdt:Transform="SetAttributes(fcnMode,maxRequestLength)" />
|
||||
<httpRuntime targetFramework="4.5" xdt:Locator="Condition(count(@targetFramework) != 1)" xdt:Transform="SetAttributes(targetFramework)" />
|
||||
<httpRuntime targetFramework="4.7.2" xdt:Locator="Condition(count(@targetFramework) != 1)" xdt:Transform="SetAttributes(targetFramework)" />
|
||||
|
||||
<membership defaultProvider="DefaultMembershipProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove" />
|
||||
<roleManager defaultProvider="DefaultRoleProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>
|
||||
|
||||
@@ -73,7 +73,6 @@ if ($project) {
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Helpers.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\MiniProfiler.dll) { Remove-Item $umbracoBinFolder\MiniProfiler.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\MySql.Data.dll) { Remove-Item $umbracoBinFolder\MySql.Data.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Newtonsoft.Json.dll) { Remove-Item $umbracoBinFolder\Newtonsoft.Json.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Owin.dll) { Remove-Item $umbracoBinFolder\Owin.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Semver.dll) { Remove-Item $umbracoBinFolder\Semver.dll -Force -Confirm:$false }
|
||||
|
||||
@@ -54,7 +54,7 @@ if ($project) {
|
||||
[xml]$config = Get-Content $destinationWebConfig
|
||||
|
||||
$config.configuration.appSettings.ChildNodes | ForEach-Object {
|
||||
if($_.key -eq "umbracoConfigurationStatus")
|
||||
if($_.key -eq "Umbraco.Core.ConfigurationStatus")
|
||||
{
|
||||
# The web.config has an umbraco-specific appSetting in it
|
||||
# don't overwrite it and let config transforms do their thing
|
||||
@@ -77,10 +77,6 @@ if ($project) {
|
||||
$splashesDestination = Join-Path $projectPath "Config\splashes\"
|
||||
New-Item $splashesDestination -Type directory
|
||||
Copy-Item $splashesSource $splashesDestination -Force
|
||||
|
||||
$umbracoUIXMLSource = Join-Path $installPath "UmbracoFiles\Umbraco\Config\Create\UI.xml"
|
||||
$umbracoUIXMLDestination = Join-Path $projectPath "Umbraco\Config\Create\UI.xml"
|
||||
Copy-Item $umbracoUIXMLSource $umbracoUIXMLDestination -Force
|
||||
} else {
|
||||
# This part only runs for upgrades
|
||||
|
||||
@@ -102,31 +98,6 @@ if ($project) {
|
||||
{
|
||||
# Not a big problem if this fails, let it go
|
||||
}
|
||||
|
||||
Try
|
||||
{
|
||||
$uiXmlConfigPath = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
|
||||
$uiXmlFile = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
|
||||
|
||||
$uiXml = New-Object System.Xml.XmlDocument
|
||||
$uiXml.PreserveWhitespace = $true
|
||||
|
||||
$uiXml.Load($uiXmlFile)
|
||||
$createExists = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks/create")
|
||||
|
||||
if($createExists.Count -eq 0)
|
||||
{
|
||||
$macrosTasksNode = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks")
|
||||
|
||||
#Creating: <create assembly="umbraco" type="macroTasks" />
|
||||
$createNode = $uiXml.CreateElement("create")
|
||||
$createNode.SetAttribute("assembly", "umbraco")
|
||||
$createNode.SetAttribute("type", "macroTasks")
|
||||
$macrosTasksNode.AppendChild($createNode)
|
||||
$uiXml.Save($uiXmlFile)
|
||||
}
|
||||
}
|
||||
Catch { }
|
||||
}
|
||||
|
||||
$installFolder = Join-Path $projectPath "Install"
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<log4net xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<appender name="AsynchronousLog4NetAppender" xdt:Transform="Remove" xdt:Locator="Match(name)" />
|
||||
<appender name="AsynchronousLog4NetAppender" type="Umbraco.Core.Logging.ParallelForwardingAppender,Umbraco.Core" xdt:Transform="Insert" >
|
||||
<appender-ref ref="rollingFile" />
|
||||
</appender>
|
||||
|
||||
<appender name="rollingFile" type="log4net.Appender.RollingFileAppender" xdt:Transform="InsertIfMissing">
|
||||
<file value="App_Data\Logs\UmbracoTraceLog.txt" />
|
||||
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
|
||||
<appendToFile value="true" />
|
||||
<rollingStyle value="Date" />
|
||||
<maximumFileSize value="5MB" />
|
||||
<layout type="log4net.Layout.PatternLayout">
|
||||
<conversionPattern value=" %date [P%property{processId}/D%property{appDomainId}/T%thread] %-5level %logger - %message%newline" />
|
||||
</layout>
|
||||
<encoding value="utf-8" />
|
||||
</appender>
|
||||
</log4net>
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<trees xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<!--Content-->
|
||||
<add alias="content" application="content"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="RemoveAttributes(silent)" />
|
||||
<add initialize="false" sortOrder="0" alias="contentRecycleBin" application="content" title="Recycle Bin" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.cms.presentation.Trees.ContentRecycleBin, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add initialize="true" sortOrder="0" alias="content" application="content" title="Content" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.ContentTreeController, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
|
||||
<!--Media-->
|
||||
<add initialize="true" sortOrder="0" alias="media" application="media" title="Media" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.MediaTreeController, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add initialize="false" sortOrder="0" alias="mediaRecycleBin" application="media" title="Recycle Bin" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.cms.presentation.Trees.MediaRecycleBin, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
|
||||
<!--Settings-->
|
||||
<add application="settings" alias="nodeTypes"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="Remove" />
|
||||
<add initialize="true" sortOrder="0" alias="documentTypes" application="settings" title="Document Types" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.ContentTypeTreeController, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
<add application="settings" alias="stylesheets" title="Stylesheets" type="umbraco.loadStylesheets, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="3"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="settings" alias="stylesheetProperty" title="Stylesheet Property" type="umbraco.loadStylesheetProperty, Umbraco.Web" iconClosed="" iconOpen="" initialize="false" sortOrder="0"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="settings" alias="templates" title="Templates" type="Umbraco.Web.Trees.TemplatesTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="1"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
|
||||
<add application="settings" alias="partialViews" type="Umbraco.Web.Trees.PartialViewsTree, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias,type)"
|
||||
xdt:Transform="Remove()" />
|
||||
<add application="settings" alias="partialViews" title="Partial Views" silent="false" initialize="true" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.PartialViewsTreeController, Umbraco.Web" sortOrder="2"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
|
||||
<add application="settings" alias="scripts" title="Scripts" type="Umbraco.Web.Trees.ScriptTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="settings" alias="dictionary" title="Dictionary" type="Umbraco.Web.Trees.DictionaryTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="8"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add alias="dictionary" application="settings"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="RemoveAttributes(action)" />
|
||||
<add application="settings" alias="languages" title="Languages" type="Umbraco.Web.Trees.LanguageTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="5"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="settings" initialize="true" alias="mediaTypes" title="Media Types" type="Umbraco.Web.Trees.MediaTypeTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="7"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
|
||||
<!--Developer-->
|
||||
<add alias="packager" application="developer"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="Remove" />
|
||||
<add alias="packagerPackages" application="developer"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="Remove" />
|
||||
<add initialize="true" sortOrder="0" alias="packager" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.PackagesTreeController, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
|
||||
<!-- Before 7.4 this tree had the alias 'dataType', without the 's' on the end, this is here to rename it -->
|
||||
<add sortOrder="1" alias="dataTypes" application="developer" type="Umbraco.Web.Trees.DataTypeTreeController, Umbraco.Web"
|
||||
xdt:Locator="Match(application,type)"
|
||||
xdt:Transform="SetAttributes(alias,sortOrder)" />
|
||||
|
||||
<!-- Yes, set the sortOrder again, like above because.. sometimes apparently we already have a dataTypes node and we can't remove more than one.. if they're equal though (same alias,application and sortOrder) it doesn't throw an error -->
|
||||
<add sortOrder="1" alias="dataTypes" application="developer"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes(sortOrder)" />
|
||||
|
||||
<add initialize="true" sortOrder="2" alias="macros" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MacroTreeController, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="developer" alias="relationTypes" title="Relation Types" type="umbraco.loadRelationTypes, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
|
||||
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias,type)"
|
||||
xdt:Transform="Remove()" />
|
||||
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTreeController, Umbraco.Web" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
|
||||
<add application="developer" alias="python"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="Remove" />
|
||||
|
||||
<!--Users-->
|
||||
<add initialize="true" sortOrder="0" alias="users" application="users" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.UserTreeController, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
|
||||
<add application="users" alias="userTypes"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="Remove" />
|
||||
<add application="users" alias="userPermissions"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="Remove" />
|
||||
|
||||
<!--Members-->
|
||||
<add initialize="true" sortOrder="0" alias="member" application="member" title="Members" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MemberTreeController, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add application="member" alias="memberGroup"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="Remove" />
|
||||
<add application="member" sortOrder="2" alias="memberGroups" title="Member Groups" type="umbraco.loadMemberGroups, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
<add application="member" alias="memberType"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="Remove" />
|
||||
<add application="member" sortOrder="1" alias="memberTypes" initialize="true" title="Member Types" type="Umbraco.Web.Trees.MemberTypeTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
|
||||
<!--Translation-->
|
||||
<add silent="false" initialize="true" sortOrder="1" alias="openTasks" application="translation" title="Tasks assigned to you" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.loadOpenTasks, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add alias="openTasks" application="translation"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="RemoveAttributes(action)" />
|
||||
<add silent="false" initialize="true" sortOrder="2" alias="yourTasks" application="translation" title="Tasks created by you" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.loadYourTasks, Umbraco.Web"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="SetAttributes()" />
|
||||
<add alias="yourTasks" application="translation"
|
||||
xdt:Locator="Match(application,alias)"
|
||||
xdt:Transform="RemoveAttributes(action)" />
|
||||
</trees>
|
||||
+2
-5
@@ -120,7 +120,7 @@
|
||||
} finally {
|
||||
Pop-Location
|
||||
|
||||
# fixme - should we filter the log to find errors?
|
||||
# FIXME: should we filter the log to find errors?
|
||||
#get-content .\build.tmp\belle.log | %{ if ($_ -match "build") { write $_}}
|
||||
|
||||
# restore
|
||||
@@ -177,7 +177,7 @@
|
||||
{
|
||||
Write-Host "Prepare Tests"
|
||||
|
||||
# fixme - idea is to avoid rebuilding everything for tests
|
||||
# FIXME: - idea is to avoid rebuilding everything for tests
|
||||
# but because of our weird assembly versioning (with .* stuff)
|
||||
# everything gets rebuilt all the time...
|
||||
#Copy-Files "$tmp\bin" "." "$tmp\tests"
|
||||
@@ -330,9 +330,6 @@
|
||||
|
||||
$ubuild.DefineMethod("PrepareBuild",
|
||||
{
|
||||
Write-Host "Clear folders and files"
|
||||
$this.RemoveDirectory("$($this.SolutionRoot)\src\Umbraco.Web.UI.Client\bower_components")
|
||||
|
||||
$this.TempStoreFile("$($this.SolutionRoot)\src\Umbraco.Web.UI\web.config")
|
||||
Write-Host "Create clean web.config"
|
||||
$this.CopyFile("$($this.SolutionRoot)\src\Umbraco.Web.UI\web.Template.config", "$($this.SolutionRoot)\src\Umbraco.Web.UI\web.config")
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyCompany("Umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2018")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Umbraco.Core
|
||||
public static class Attempt
|
||||
{
|
||||
// note:
|
||||
// cannot rely on overloads only to differenciate between with/without status
|
||||
// cannot rely on overloads only to differentiate between with/without status
|
||||
// in some cases it will always be ambiguous, so be explicit w/ 'WithStatus' methods
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
|
||||
/// Implicitly operator to check if the attempt was successful without having to access the 'success' property
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
|
||||
/// Implicitly operator to check if the attempt was successful without having to access the 'success' property
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
+13
-13
@@ -8,9 +8,9 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Extensions for strongly typed access
|
||||
/// </summary>
|
||||
public static class CacheProviderExtensions
|
||||
public static class AppCacheExtensions
|
||||
{
|
||||
public static T GetCacheItem<T>(this IRuntimeCacheProvider provider,
|
||||
public static T GetCacheItem<T>(this IAppPolicyCache provider,
|
||||
string cacheKey,
|
||||
Func<T> getCacheItem,
|
||||
TimeSpan? timeout,
|
||||
@@ -19,11 +19,11 @@ namespace Umbraco.Core.Cache
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null)
|
||||
{
|
||||
var result = provider.GetCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
var result = provider.Get(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
return result == null ? default(T) : result.TryConvertTo<T>().Result;
|
||||
}
|
||||
|
||||
public static void InsertCacheItem<T>(this IRuntimeCacheProvider provider,
|
||||
public static void InsertCacheItem<T>(this IAppPolicyCache provider,
|
||||
string cacheKey,
|
||||
Func<T> getCacheItem,
|
||||
TimeSpan? timeout = null,
|
||||
@@ -32,24 +32,24 @@ namespace Umbraco.Core.Cache
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null)
|
||||
{
|
||||
provider.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
provider.Insert(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this ICacheProvider provider, string keyStartsWith)
|
||||
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this IAppCache provider, string keyStartsWith)
|
||||
{
|
||||
var result = provider.GetCacheItemsByKeySearch(keyStartsWith);
|
||||
var result = provider.SearchByKey(keyStartsWith);
|
||||
return result.Select(x => x.TryConvertTo<T>().Result);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this ICacheProvider provider, string regexString)
|
||||
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this IAppCache provider, string regexString)
|
||||
{
|
||||
var result = provider.GetCacheItemsByKeyExpression(regexString);
|
||||
var result = provider.SearchByRegex(regexString);
|
||||
return result.Select(x => x.TryConvertTo<T>().Result);
|
||||
}
|
||||
|
||||
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey)
|
||||
public static T GetCacheItem<T>(this IAppCache provider, string cacheKey)
|
||||
{
|
||||
var result = provider.GetCacheItem(cacheKey);
|
||||
var result = provider.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
@@ -57,9 +57,9 @@ namespace Umbraco.Core.Cache
|
||||
return result.TryConvertTo<T>().Result;
|
||||
}
|
||||
|
||||
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey, Func<T> getCacheItem)
|
||||
public static T GetCacheItem<T>(this IAppCache provider, string cacheKey, Func<T> getCacheItem)
|
||||
{
|
||||
var result = provider.GetCacheItem(cacheKey, () => getCacheItem());
|
||||
var result = provider.Get(cacheKey, () => getCacheItem());
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the application caches.
|
||||
/// </summary>
|
||||
public class AppCaches
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AppCaches"/> for use in a web application.
|
||||
/// </summary>
|
||||
public AppCaches()
|
||||
: this(HttpRuntime.Cache)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AppCaches"/> for use in a web application.
|
||||
/// </summary>
|
||||
public AppCaches(System.Web.Caching.Cache cache)
|
||||
: this(
|
||||
new WebCachingAppCache(cache),
|
||||
new HttpRequestAppCache(),
|
||||
new IsolatedCaches(t => new ObjectCacheAppCache()))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AppCaches"/> with cache providers.
|
||||
/// </summary>
|
||||
public AppCaches(
|
||||
IAppPolicyCache runtimeCache,
|
||||
IAppCache requestCache,
|
||||
IsolatedCaches isolatedCaches)
|
||||
{
|
||||
RuntimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache));
|
||||
RequestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache));
|
||||
IsolatedCaches = isolatedCaches ?? throw new ArgumentNullException(nameof(isolatedCaches));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the special disabled instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When used by repositories, all cache policies apply, but the underlying caches do not cache anything.</para>
|
||||
/// <para>Used by tests.</para>
|
||||
/// </remarks>
|
||||
public static AppCaches Disabled { get; } = new AppCaches(NoAppCache.Instance, NoAppCache.Instance, new IsolatedCaches(_ => NoAppCache.Instance));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the special no-cache instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When used by repositories, all cache policies are bypassed.</para>
|
||||
/// <para>Used by repositories that do no cache.</para>
|
||||
/// </remarks>
|
||||
public static AppCaches NoCache { get; } = new AppCaches(NoAppCache.Instance, NoAppCache.Instance, new IsolatedCaches(_ => NoAppCache.Instance));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the per-request cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The per-request caches works on top of the current HttpContext items.</para>
|
||||
/// <para>Outside a web environment, the behavior of that cache is unspecified.</para>
|
||||
/// </remarks>
|
||||
public IAppCache RequestCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the runtime cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The runtime cache is the main application cache.</para>
|
||||
/// </remarks>
|
||||
public IAppPolicyCache RuntimeCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the isolated caches.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Isolated caches are used by e.g. repositories, to ensure that each cached entity
|
||||
/// type has its own cache, so that lookups are fast and the repository does not need to
|
||||
/// search through all keys on a global scale.</para>
|
||||
/// </remarks>
|
||||
public IsolatedCaches IsolatedCaches { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for implementing a dictionary of <see cref="IAppPolicyCache"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the dictionary key.</typeparam>
|
||||
public abstract class AppPolicedCacheDictionary<TKey>
|
||||
{
|
||||
private readonly ConcurrentDictionary<TKey, IAppPolicyCache> _caches = new ConcurrentDictionary<TKey, IAppPolicyCache>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AppPolicedCacheDictionary{TKey}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="cacheFactory"></param>
|
||||
protected AppPolicedCacheDictionary(Func<TKey, IAppPolicyCache> cacheFactory)
|
||||
{
|
||||
CacheFactory = cacheFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal cache factory, for tests only!
|
||||
/// </summary>
|
||||
internal readonly Func<TKey, IAppPolicyCache> CacheFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates a cache.
|
||||
/// </summary>
|
||||
public IAppPolicyCache GetOrCreate(TKey key)
|
||||
=> _caches.GetOrAdd(key, k => CacheFactory(k));
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a cache.
|
||||
/// </summary>
|
||||
public Attempt<IAppPolicyCache> Get(TKey key)
|
||||
=> _caches.TryGetValue(key, out var cache) ? Attempt.Succeed(cache) : Attempt.Fail<IAppPolicyCache>();
|
||||
|
||||
/// <summary>
|
||||
/// Removes a cache.
|
||||
/// </summary>
|
||||
public void Remove(TKey key)
|
||||
{
|
||||
_caches.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all caches.
|
||||
/// </summary>
|
||||
public void RemoveAll()
|
||||
{
|
||||
_caches.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears a cache.
|
||||
/// </summary>
|
||||
public void ClearCache(TKey key)
|
||||
{
|
||||
if (_caches.TryGetValue(key, out var cache))
|
||||
cache.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all caches.
|
||||
/// </summary>
|
||||
public void ClearAllCaches()
|
||||
{
|
||||
foreach (var cache in _caches.Values)
|
||||
cache.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the application-wide caches.
|
||||
/// </summary>
|
||||
public class CacheHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance for use in the web
|
||||
/// </summary>
|
||||
public CacheHelper()
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider(),
|
||||
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance for use in the web
|
||||
/// </summary>
|
||||
public CacheHelper(System.Web.Caching.Cache cache)
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider(),
|
||||
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance based on the provided providers
|
||||
/// </summary>
|
||||
public CacheHelper(
|
||||
IRuntimeCacheProvider httpCacheProvider,
|
||||
ICacheProvider staticCacheProvider,
|
||||
ICacheProvider requestCacheProvider,
|
||||
IsolatedRuntimeCache isolatedCacheManager)
|
||||
{
|
||||
RuntimeCache = httpCacheProvider ?? throw new ArgumentNullException(nameof(httpCacheProvider));
|
||||
StaticCache = staticCacheProvider ?? throw new ArgumentNullException(nameof(staticCacheProvider));
|
||||
RequestCache = requestCacheProvider ?? throw new ArgumentNullException(nameof(requestCacheProvider));
|
||||
IsolatedRuntimeCache = isolatedCacheManager ?? throw new ArgumentNullException(nameof(isolatedCacheManager));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the special disabled instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When used by repositories, all cache policies apply, but the underlying caches do not cache anything.</para>
|
||||
/// <para>Used by tests.</para>
|
||||
/// </remarks>
|
||||
public static CacheHelper Disabled { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the special no-cache instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When used by repositories, all cache policies are bypassed.</para>
|
||||
/// <para>Used by repositories that do no cache.</para>
|
||||
/// </remarks>
|
||||
public static CacheHelper NoCache { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Request cache
|
||||
/// </summary>
|
||||
public ICacheProvider RequestCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public ICacheProvider StaticCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public IRuntimeCacheProvider RuntimeCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Isolated Runtime cache manager
|
||||
/// </summary>
|
||||
public IsolatedRuntimeCache IsolatedRuntimeCache { get; internal set; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,10 +5,10 @@
|
||||
/// </summary>
|
||||
public static class CacheKeys
|
||||
{
|
||||
public const string ApplicationTreeCacheKey = "ApplicationTreeCache"; // used by ApplicationTreeService
|
||||
public const string ApplicationsCacheKey = "ApplicationCache"; // used by SectionService
|
||||
|
||||
public const string TemplateFrontEndCacheKey = "template"; // fixme usage?
|
||||
// TODO: this one can probably be removed
|
||||
public const string TemplateFrontEndCacheKey = "template";
|
||||
|
||||
public const string MacroContentCacheKey = "macroContent_"; // used in MacroRenderers
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CacheRefresherBase{TInstanceType}"/>.
|
||||
/// </summary>
|
||||
/// <param name="cacheHelper">A cache helper.</param>
|
||||
protected CacheRefresherBase(CacheHelper cacheHelper)
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
protected CacheRefresherBase(AppCaches appCaches)
|
||||
{
|
||||
CacheHelper = cacheHelper;
|
||||
AppCaches = appCaches;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -93,7 +93,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Gets the cache helper.
|
||||
/// </summary>
|
||||
protected CacheHelper CacheHelper { get; }
|
||||
protected AppCaches AppCaches { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cache for all repository entities of a specified type.
|
||||
@@ -102,7 +102,7 @@ namespace Umbraco.Core.Cache
|
||||
protected void ClearAllIsolatedCacheByEntityType<TEntity>()
|
||||
where TEntity : class, IEntity
|
||||
{
|
||||
CacheHelper.IsolatedRuntimeCache.ClearCache<TEntity>();
|
||||
AppCaches.IsolatedCaches.ClearCache<TEntity>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IAppPolicyCache"/> by wrapping an inner other <see cref="IAppPolicyCache"/>
|
||||
/// instance, and ensuring that all inserts and returns are deep cloned copies of the cache item,
|
||||
/// when the item is deep-cloneable.
|
||||
/// </summary>
|
||||
internal class DeepCloneAppCache : IAppPolicyCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeepCloneAppCache"/> class.
|
||||
/// </summary>
|
||||
public DeepCloneAppCache(IAppPolicyCache innerCache)
|
||||
{
|
||||
var type = typeof (DeepCloneAppCache);
|
||||
|
||||
if (innerCache.GetType() == type)
|
||||
throw new InvalidOperationException($"A {type} cannot wrap another instance of itself.");
|
||||
|
||||
InnerCache = innerCache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the inner cache.
|
||||
/// </summary>
|
||||
public IAppPolicyCache InnerCache { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key)
|
||||
{
|
||||
var item = InnerCache.Get(key);
|
||||
return CheckCloneableAndTracksChanges(item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory)
|
||||
{
|
||||
var cached = InnerCache.Get(key, () =>
|
||||
{
|
||||
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
// do not store null values (backward compat), clone / reset to go into the cache
|
||||
return value == null ? null : CheckCloneableAndTracksChanges(value);
|
||||
});
|
||||
return CheckCloneableAndTracksChanges(cached);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
return InnerCache.SearchByKey(keyStartsWith)
|
||||
.Select(CheckCloneableAndTracksChanges);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
return InnerCache.SearchByRegex(regex)
|
||||
.Select(CheckCloneableAndTracksChanges);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
var cached = InnerCache.Get(key, () =>
|
||||
{
|
||||
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
// do not store null values (backward compat), clone / reset to go into the cache
|
||||
return value == null ? null : CheckCloneableAndTracksChanges(value);
|
||||
|
||||
// clone / reset to go into the cache
|
||||
}, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
|
||||
// clone / reset to go into the cache
|
||||
return CheckCloneableAndTracksChanges(cached);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
InnerCache.Insert(key, () =>
|
||||
{
|
||||
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
// do not store null values (backward compat), clone / reset to go into the cache
|
||||
return value == null ? null : CheckCloneableAndTracksChanges(value);
|
||||
}, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear()
|
||||
{
|
||||
InnerCache.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear(string key)
|
||||
{
|
||||
InnerCache.Clear(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType(string typeName)
|
||||
{
|
||||
InnerCache.ClearOfType(typeName);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType<T>()
|
||||
{
|
||||
InnerCache.ClearOfType<T>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
InnerCache.ClearOfType<T>(predicate);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearByKey(string keyStartsWith)
|
||||
{
|
||||
InnerCache.ClearByKey(keyStartsWith);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearByRegex(string regex)
|
||||
{
|
||||
InnerCache.ClearByRegex(regex);
|
||||
}
|
||||
|
||||
private static object CheckCloneableAndTracksChanges(object input)
|
||||
{
|
||||
if (input is IDeepCloneable cloneable)
|
||||
{
|
||||
input = cloneable.DeepClone();
|
||||
}
|
||||
|
||||
// reset dirty initial properties
|
||||
if (input is IRememberBeingDirty tracksChanges)
|
||||
{
|
||||
tracksChanges.ResetDirtyProperties(false);
|
||||
input = tracksChanges;
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface describing this cache provider as a wrapper for another
|
||||
/// </summary>
|
||||
internal interface IRuntimeCacheProviderWrapper
|
||||
{
|
||||
IRuntimeCacheProvider InnerProvider { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper for any IRuntimeCacheProvider that ensures that all inserts and returns
|
||||
/// are a deep cloned copy of the item when the item is IDeepCloneable and that tracks changes are
|
||||
/// reset if the object is TracksChangesEntityBase
|
||||
/// </summary>
|
||||
internal class DeepCloneRuntimeCacheProvider : IRuntimeCacheProvider, IRuntimeCacheProviderWrapper
|
||||
{
|
||||
public IRuntimeCacheProvider InnerProvider { get; }
|
||||
|
||||
public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider)
|
||||
{
|
||||
var type = typeof (DeepCloneRuntimeCacheProvider);
|
||||
|
||||
if (innerProvider.GetType() == type)
|
||||
throw new InvalidOperationException($"A {type} cannot wrap another instance of {type}.");
|
||||
|
||||
InnerProvider = innerProvider;
|
||||
}
|
||||
|
||||
#region Clear - doesn't require any changes
|
||||
|
||||
public void ClearAllCache()
|
||||
{
|
||||
InnerProvider.ClearAllCache();
|
||||
}
|
||||
|
||||
public void ClearCacheItem(string key)
|
||||
{
|
||||
InnerProvider.ClearCacheItem(key);
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
InnerProvider.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
InnerProvider.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
InnerProvider.ClearCacheObjectTypes<T>(predicate);
|
||||
}
|
||||
|
||||
public void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
InnerProvider.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
|
||||
public void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
InnerProvider.ClearCacheByKeyExpression(regexString);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return InnerProvider.GetCacheItemsByKeySearch(keyStartsWith)
|
||||
.Select(CheckCloneableAndTracksChanges);
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return InnerProvider.GetCacheItemsByKeyExpression(regexString)
|
||||
.Select(CheckCloneableAndTracksChanges);
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var item = InnerProvider.GetCacheItem(cacheKey);
|
||||
return CheckCloneableAndTracksChanges(item);
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
var cached = InnerProvider.GetCacheItem(cacheKey, () =>
|
||||
{
|
||||
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return null; // do not store null values (backward compat)
|
||||
|
||||
return CheckCloneableAndTracksChanges(value);
|
||||
});
|
||||
return CheckCloneableAndTracksChanges(cached);
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
var cached = InnerProvider.GetCacheItem(cacheKey, () =>
|
||||
{
|
||||
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return null; // do not store null values (backward compat)
|
||||
|
||||
// clone / reset to go into the cache
|
||||
return CheckCloneableAndTracksChanges(value);
|
||||
}, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
|
||||
// clone / reset to go into the cache
|
||||
return CheckCloneableAndTracksChanges(cached);
|
||||
}
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
InnerProvider.InsertCacheItem(cacheKey, () =>
|
||||
{
|
||||
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return null; // do not store null values (backward compat)
|
||||
|
||||
// clone / reset to go into the cache
|
||||
return CheckCloneableAndTracksChanges(value);
|
||||
}, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
}
|
||||
|
||||
private static object CheckCloneableAndTracksChanges(object input)
|
||||
{
|
||||
var cloneable = input as IDeepCloneable;
|
||||
if (cloneable != null)
|
||||
{
|
||||
input = cloneable.DeepClone();
|
||||
}
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
var tracksChanges = input as IRememberBeingDirty;
|
||||
if (tracksChanges != null)
|
||||
{
|
||||
tracksChanges.ResetDirtyProperties(false);
|
||||
input = tracksChanges;
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Cache
|
||||
private static readonly TEntity[] EmptyEntities = new TEntity[0]; // const
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
public DefaultRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
: base(cache, scopeAccessor)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
protected virtual void InsertEntity(string cacheKey, TEntity entity)
|
||||
{
|
||||
Cache.InsertCacheItem(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
|
||||
Cache.Insert(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
protected virtual void InsertEntities(TId[] ids, TEntity[] entities)
|
||||
@@ -52,7 +52,7 @@ namespace Umbraco.Core.Cache
|
||||
// getting all of them, and finding nothing.
|
||||
// if we can cache a zero count, cache an empty array,
|
||||
// for as long as the cache is not cleared (no expiration)
|
||||
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => EmptyEntities);
|
||||
Cache.Insert(GetEntityTypeCacheKey(), () => EmptyEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -60,7 +60,7 @@ namespace Umbraco.Core.Cache
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var capture = entity;
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
|
||||
Cache.Insert(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,21 +77,21 @@ namespace Umbraco.Core.Cache
|
||||
// just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
Cache.Insert(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if an exception is thrown we need to remove the entry from cache,
|
||||
// this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
|
||||
Cache.Clear(GetEntityCacheKey(entity.Id));
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
|
||||
throw;
|
||||
}
|
||||
@@ -109,21 +109,21 @@ namespace Umbraco.Core.Cache
|
||||
// just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
Cache.Insert(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if an exception is thrown we need to remove the entry from cache,
|
||||
// this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
|
||||
Cache.Clear(GetEntityCacheKey(entity.Id));
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
|
||||
throw;
|
||||
}
|
||||
@@ -142,9 +142,9 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
// whatever happens, clear the cache
|
||||
var cacheKey = GetEntityCacheKey(entity.Id);
|
||||
Cache.ClearCacheItem(cacheKey);
|
||||
Cache.Clear(cacheKey);
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
{
|
||||
Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey());
|
||||
Cache.ClearByKey(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IAppCache"/> on top of a concurrent dictionary.
|
||||
/// </summary>
|
||||
public class DictionaryAppCache : IAppCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the internal items dictionary, for tests only!
|
||||
/// </summary>
|
||||
internal readonly ConcurrentDictionary<string, object> Items = new ConcurrentDictionary<string, object>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual object Get(string key)
|
||||
{
|
||||
return Items.TryGetValue(key, out var value) ? value : null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual object Get(string key, Func<object> factory)
|
||||
{
|
||||
return Items.GetOrAdd(key, _ => factory());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
var items = new List<object>();
|
||||
foreach (var (key, value) in Items)
|
||||
if (key.InvariantStartsWith(keyStartsWith))
|
||||
items.Add(value);
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
var items = new List<object>();
|
||||
foreach (var (key, value) in Items)
|
||||
if (compiled.IsMatch(key))
|
||||
items.Add(value);
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear()
|
||||
{
|
||||
Items.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear(string key)
|
||||
{
|
||||
Items.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType(string typeName)
|
||||
{
|
||||
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByKey(string keyStartsWith)
|
||||
{
|
||||
Items.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
Items.RemoveAll(kvp => compiled.IsMatch(kvp.Key));
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
-68
@@ -7,79 +7,130 @@ using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class DictionaryCacheProvider : ICacheProvider
|
||||
/// <summary>
|
||||
/// Implements a fast <see cref="IAppCache"/> on top of a concurrent dictionary.
|
||||
/// </summary>
|
||||
internal class FastDictionaryAppCache : IAppCache
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, Lazy<object>> _items
|
||||
= new ConcurrentDictionary<string, Lazy<object>>();
|
||||
/// <summary>
|
||||
/// Gets the internal items dictionary, for tests only!
|
||||
/// </summary>
|
||||
internal readonly ConcurrentDictionary<string, Lazy<object>> Items = new ConcurrentDictionary<string, Lazy<object>>();
|
||||
|
||||
// for tests
|
||||
internal ConcurrentDictionary<string, Lazy<object>> Items => _items;
|
||||
|
||||
public void ClearAllCache()
|
||||
/// <inheritdoc />
|
||||
public object Get(string cacheKey)
|
||||
{
|
||||
_items.Clear();
|
||||
Items.TryGetValue(cacheKey, out var result); // else null
|
||||
return result == null ? null : FastDictionaryAppCacheBase.GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public void ClearCacheItem(string key)
|
||||
/// <inheritdoc />
|
||||
public object Get(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
_items.TryRemove(key, out _);
|
||||
var result = Items.GetOrAdd(cacheKey, k => FastDictionaryAppCacheBase.GetSafeLazy(getCacheItem));
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (!(value is FastDictionaryAppCacheBase.ExceptionHolder eh))
|
||||
return value;
|
||||
|
||||
// and... it's in the cache anyway - so contrary to other cache providers,
|
||||
// which would trick with GetSafeLazyValue, we need to remove by ourselves,
|
||||
// in order NOT to cache exceptions
|
||||
|
||||
Items.TryRemove(cacheKey, out result);
|
||||
eh.Exception.Throw(); // throw once!
|
||||
return null; // never reached
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes(string typeName)
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
return Items
|
||||
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith))
|
||||
.Select(kvp => FastDictionaryAppCacheBase.GetSafeLazyValue(kvp.Value))
|
||||
.Where(x => x != null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
return Items
|
||||
.Where(kvp => compiled.IsMatch(kvp.Key))
|
||||
.Select(kvp => FastDictionaryAppCacheBase.GetSafeLazyValue(kvp.Value))
|
||||
.Where(x => x != null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear()
|
||||
{
|
||||
Items.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear(string key)
|
||||
{
|
||||
Items.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType(string typeName)
|
||||
{
|
||||
var type = TypeFinder.GetTypeByName(typeName);
|
||||
if (type == null) return;
|
||||
var isInterface = type.IsInterface;
|
||||
|
||||
foreach (var kvp in _items
|
||||
foreach (var kvp in Items
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
// otherwise remove exact types (not inherited types)
|
||||
return value == null || (isInterface ? (type.IsInstanceOfType(value)) : (value.GetType() == type));
|
||||
}))
|
||||
_items.TryRemove(kvp.Key, out _);
|
||||
Items.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes<T>()
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var isInterface = typeOfT.IsInterface;
|
||||
|
||||
foreach (var kvp in _items
|
||||
foreach (var kvp in Items
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// compare on exact type, don't use "is"
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
// otherwise remove exact types (not inherited types)
|
||||
return value == null || (isInterface ? (value is T) : (value.GetType() == typeOfT));
|
||||
}))
|
||||
_items.TryRemove(kvp.Key, out _);
|
||||
Items.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var isInterface = typeOfT.IsInterface;
|
||||
|
||||
foreach (var kvp in _items
|
||||
foreach (var kvp in Items
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// compare on exact type, don't use "is"
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
|
||||
if (value == null) return true;
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
@@ -88,60 +139,24 @@ namespace Umbraco.Core.Cache
|
||||
// run predicate on the 'public key' part only, ie without prefix
|
||||
&& predicate(x.Key, (T)value);
|
||||
}))
|
||||
_items.TryRemove(kvp.Key, out _);
|
||||
Items.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
public void ClearCacheByKeySearch(string keyStartsWith)
|
||||
/// <inheritdoc />
|
||||
public void ClearByKey(string keyStartsWith)
|
||||
{
|
||||
foreach (var ikvp in _items
|
||||
foreach (var ikvp in Items
|
||||
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)))
|
||||
_items.TryRemove(ikvp.Key, out _);
|
||||
Items.TryRemove(ikvp.Key, out _);
|
||||
}
|
||||
|
||||
public void ClearCacheByKeyExpression(string regexString)
|
||||
/// <inheritdoc />
|
||||
public void ClearByRegex(string regex)
|
||||
{
|
||||
foreach (var ikvp in _items
|
||||
.Where(kvp => Regex.IsMatch(kvp.Key, regexString)))
|
||||
_items.TryRemove(ikvp.Key, out _);
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return _items
|
||||
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith))
|
||||
.Select(kvp => DictionaryCacheProviderBase.GetSafeLazyValue(kvp.Value))
|
||||
.Where(x => x != null);
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return _items
|
||||
.Where(kvp => Regex.IsMatch(kvp.Key, regexString))
|
||||
.Select(kvp => DictionaryCacheProviderBase.GetSafeLazyValue(kvp.Value))
|
||||
.Where(x => x != null);
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey)
|
||||
{
|
||||
_items.TryGetValue(cacheKey, out var result); // else null
|
||||
return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
var result = _items.GetOrAdd(cacheKey, k => DictionaryCacheProviderBase.GetSafeLazy(getCacheItem));
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (!(value is DictionaryCacheProviderBase.ExceptionHolder eh))
|
||||
return value;
|
||||
|
||||
// and... it's in the cache anyway - so contrary to other cache providers,
|
||||
// which would trick with GetSafeLazyValue, we need to remove by ourselves,
|
||||
// in order NOT to cache exceptions
|
||||
|
||||
_items.TryRemove(cacheKey, out result);
|
||||
eh.Exception.Throw(); // throw once!
|
||||
return null; // never reached
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
foreach (var ikvp in Items
|
||||
.Where(kvp => compiled.IsMatch(kvp.Key)))
|
||||
Items.TryRemove(ikvp.Key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
+129
-113
@@ -8,7 +8,10 @@ using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal abstract class DictionaryCacheProviderBase : ICacheProvider
|
||||
/// <summary>
|
||||
/// Provides a base class to fast, dictionary-based <see cref="IAppCache"/> implementations.
|
||||
/// </summary>
|
||||
internal abstract class FastDictionaryAppCacheBase : IAppCache
|
||||
{
|
||||
// prefix cache keys so we know which one are ours
|
||||
protected const string CacheItemPrefix = "umbrtmche";
|
||||
@@ -16,82 +19,75 @@ namespace Umbraco.Core.Cache
|
||||
// an object that represent a value that has not been created yet
|
||||
protected internal static readonly object ValueNotCreated = new object();
|
||||
|
||||
// manupulate the underlying cache entries
|
||||
// these *must* be called from within the appropriate locks
|
||||
// and use the full prefixed cache keys
|
||||
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
|
||||
protected abstract void RemoveEntry(string key);
|
||||
protected abstract object GetEntry(string key);
|
||||
#region IAppCache
|
||||
|
||||
// read-write lock the underlying cache
|
||||
//protected abstract IDisposable ReadLock { get; }
|
||||
//protected abstract IDisposable WriteLock { get; }
|
||||
|
||||
protected abstract void EnterReadLock();
|
||||
protected abstract void ExitReadLock();
|
||||
protected abstract void EnterWriteLock();
|
||||
protected abstract void ExitWriteLock();
|
||||
|
||||
protected string GetCacheKey(string key)
|
||||
/// <inheritdoc />
|
||||
public virtual object Get(string key)
|
||||
{
|
||||
return string.Format("{0}-{1}", CacheItemPrefix, key);
|
||||
}
|
||||
|
||||
protected internal static Lazy<object> GetSafeLazy(Func<object> getCacheItem)
|
||||
{
|
||||
// try to generate the value and if it fails,
|
||||
// wrap in an ExceptionHolder - would be much simpler
|
||||
// to just use lazy.IsValueFaulted alas that field is
|
||||
// internal
|
||||
return new Lazy<object>(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new ExceptionHolder(ExceptionDispatchInfo.Capture(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected internal static object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
|
||||
{
|
||||
// if onlyIfValueIsCreated, do not trigger value creation
|
||||
// must return something, though, to differenciate from null values
|
||||
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
|
||||
|
||||
// if execution has thrown then lazy.IsValueCreated is false
|
||||
// and lazy.IsValueFaulted is true (but internal) so we use our
|
||||
// own exception holder (see Lazy<T> source code) to return null
|
||||
if (lazy.Value is ExceptionHolder) return null;
|
||||
|
||||
// we have a value and execution has not thrown so returning
|
||||
// here does not throw - unless we're re-entering, take care of it
|
||||
key = GetCacheKey(key);
|
||||
Lazy<object> result;
|
||||
try
|
||||
{
|
||||
return lazy.Value;
|
||||
EnterReadLock();
|
||||
result = GetEntry(key) as Lazy<object>; // null if key not found
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
finally
|
||||
{
|
||||
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
|
||||
ExitReadLock();
|
||||
}
|
||||
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
internal class ExceptionHolder
|
||||
/// <inheritdoc />
|
||||
public abstract object Get(string key, Func<object> factory);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
public ExceptionHolder(ExceptionDispatchInfo e)
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
try
|
||||
{
|
||||
Exception = e;
|
||||
EnterReadLock();
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitReadLock();
|
||||
}
|
||||
|
||||
public ExceptionDispatchInfo Exception { get; }
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
}
|
||||
|
||||
#region Clear
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
var plen = prefix.Length;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
try
|
||||
{
|
||||
EnterReadLock();
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => compiled.IsMatch(((string)x.Key).Substring(plen)))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compatible, don't store null values in the cache
|
||||
}
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -106,7 +102,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear(string key)
|
||||
{
|
||||
var cacheKey = GetCacheKey(key);
|
||||
try
|
||||
@@ -120,7 +117,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType(string typeName)
|
||||
{
|
||||
var type = TypeFinder.GetTypeByName(typeName);
|
||||
if (type == null) return;
|
||||
@@ -149,7 +147,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var isInterface = typeOfT.IsInterface;
|
||||
@@ -178,7 +177,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var isInterface = typeOfT.IsInterface;
|
||||
@@ -210,7 +210,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByKey(string keyStartsWith)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
try
|
||||
@@ -227,14 +228,16 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
try
|
||||
{
|
||||
EnterWriteLock();
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.Where(x => compiled.IsMatch(((string)x.Key).Substring(plen)))
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
@@ -246,67 +249,80 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
#region Dictionary
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
// manipulate the underlying cache entries
|
||||
// these *must* be called from within the appropriate locks
|
||||
// and use the full prefixed cache keys
|
||||
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
|
||||
protected abstract void RemoveEntry(string key);
|
||||
protected abstract object GetEntry(string key);
|
||||
|
||||
// read-write lock the underlying cache
|
||||
//protected abstract IDisposable ReadLock { get; }
|
||||
//protected abstract IDisposable WriteLock { get; }
|
||||
|
||||
protected abstract void EnterReadLock();
|
||||
protected abstract void ExitReadLock();
|
||||
protected abstract void EnterWriteLock();
|
||||
protected abstract void ExitWriteLock();
|
||||
|
||||
protected string GetCacheKey(string key)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
try
|
||||
{
|
||||
EnterReadLock();
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitReadLock();
|
||||
}
|
||||
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
return $"{CacheItemPrefix}-{key}";
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
protected internal static Lazy<object> GetSafeLazy(Func<object> getCacheItem)
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
var plen = prefix.Length;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
try
|
||||
// try to generate the value and if it fails,
|
||||
// wrap in an ExceptionHolder - would be much simpler
|
||||
// to just use lazy.IsValueFaulted alas that field is
|
||||
// internal
|
||||
return new Lazy<object>(() =>
|
||||
{
|
||||
EnterReadLock();
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
try
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new ExceptionHolder(ExceptionDispatchInfo.Capture(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
protected internal static object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
Lazy<object> result;
|
||||
// if onlyIfValueIsCreated, do not trigger value creation
|
||||
// must return something, though, to differentiate from null values
|
||||
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
|
||||
|
||||
// if execution has thrown then lazy.IsValueCreated is false
|
||||
// and lazy.IsValueFaulted is true (but internal) so we use our
|
||||
// own exception holder (see Lazy<T> source code) to return null
|
||||
if (lazy.Value is ExceptionHolder) return null;
|
||||
|
||||
// we have a value and execution has not thrown so returning
|
||||
// here does not throw - unless we're re-entering, take care of it
|
||||
try
|
||||
{
|
||||
EnterReadLock();
|
||||
result = GetEntry(cacheKey) as Lazy<object>; // null if key not found
|
||||
return lazy.Value;
|
||||
}
|
||||
finally
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
ExitReadLock();
|
||||
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
|
||||
}
|
||||
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
internal class ExceptionHolder
|
||||
{
|
||||
public ExceptionHolder(ExceptionDispatchInfo e)
|
||||
{
|
||||
Exception = e;
|
||||
}
|
||||
|
||||
public ExceptionDispatchInfo Exception { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Cache
|
||||
private readonly Func<TEntity, TId> _entityGetId;
|
||||
private readonly bool _expires;
|
||||
|
||||
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, Func<TEntity, TId> entityGetId, bool expires)
|
||||
public FullDataSetRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, Func<TEntity, TId> entityGetId, bool expires)
|
||||
: base(cache, scopeAccessor)
|
||||
{
|
||||
_entityGetId = entityGetId;
|
||||
@@ -55,11 +55,11 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
if (_expires)
|
||||
{
|
||||
Cache.InsertCacheItem(key, () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
|
||||
Cache.Insert(key, () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cache.InsertCacheItem(key, () => new DeepCloneableList<TEntity>(entities));
|
||||
Cache.Insert(key, () => new DeepCloneableList<TEntity>(entities));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
{
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+67
-90
@@ -7,54 +7,83 @@ using System.Web;
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache provider that caches items in the HttpContext.Items
|
||||
/// Implements a fast <see cref="IAppCache"/> on top of HttpContext.Items.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the Items collection is null, then this provider has no effect
|
||||
/// <para>If no current HttpContext items can be found (no current HttpContext,
|
||||
/// or no Items...) then this cache acts as a pass-through and does not cache
|
||||
/// anything.</para>
|
||||
/// </remarks>
|
||||
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
|
||||
internal class HttpRequestAppCache : FastDictionaryAppCacheBase
|
||||
{
|
||||
// context provider
|
||||
// the idea is that there is only one, application-wide HttpRequestCacheProvider instance,
|
||||
// that is initialized with a method that returns the "current" context.
|
||||
// NOTE
|
||||
// but then it is initialized with () => new HttpContextWrapper(HttpContent.Current)
|
||||
// which is higly inefficient because it creates a new wrapper each time we refer to _context()
|
||||
// so replace it with _context1 and _context2 below + a way to get context.Items.
|
||||
//private readonly Func<HttpContextBase> _context;
|
||||
|
||||
// NOTE
|
||||
// and then in almost 100% cases _context2 will be () => HttpContext.Current
|
||||
// so why not bring that logic in here and fallback on to HttpContext.Current when
|
||||
// _context1 is null?
|
||||
//private readonly HttpContextBase _context1;
|
||||
//private readonly Func<HttpContext> _context2;
|
||||
private readonly HttpContextBase _context;
|
||||
|
||||
private IDictionary ContextItems
|
||||
{
|
||||
//get { return _context1 != null ? _context1.Items : _context2().Items; }
|
||||
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
|
||||
}
|
||||
|
||||
private bool HasContextItems
|
||||
{
|
||||
get { return (_context != null && _context.Items != null) || HttpContext.Current != null; }
|
||||
}
|
||||
|
||||
// for unit tests
|
||||
public HttpRequestCacheProvider(HttpContextBase context)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class with a context, for unit tests!
|
||||
/// </summary>
|
||||
public HttpRequestAppCache(HttpContextBase context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// main constructor
|
||||
// will use HttpContext.Current
|
||||
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Will use HttpContext.Current.</para>
|
||||
/// TODO: https://github.com/umbraco/Umbraco-CMS/issues/4239 - use IHttpContextAccessor NOT HttpContext.Current
|
||||
/// </remarks>
|
||||
public HttpRequestAppCache()
|
||||
{ }
|
||||
|
||||
private IDictionary ContextItems => _context?.Items ?? HttpContext.Current?.Items;
|
||||
|
||||
private bool HasContextItems => _context?.Items != null || HttpContext.Current != null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Get(string key, Func<object> factory)
|
||||
{
|
||||
//_context2 = context;
|
||||
//no place to cache so just return the callback result
|
||||
if (HasContextItems == false) return factory();
|
||||
|
||||
key = GetCacheKey(key);
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
try
|
||||
{
|
||||
EnterWriteLock();
|
||||
result = ContextItems[key] as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
// more than one thread to reach the return statement below and throw - accepted.
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = GetSafeLazy(factory);
|
||||
ContextItems[key] = result;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitWriteLock();
|
||||
}
|
||||
|
||||
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
|
||||
// exceptions (but try again and again) and silently eat them - however at
|
||||
// some point we have to report them - so need to re-throw here
|
||||
|
||||
// this does not throw anymore
|
||||
//return result.Value;
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (value is ExceptionHolder eh) eh.Exception.Throw(); // throw once!
|
||||
return value;
|
||||
}
|
||||
|
||||
#region Entries
|
||||
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
@@ -62,7 +91,7 @@ namespace Umbraco.Core.Cache
|
||||
if (HasContextItems == false) return Enumerable.Empty<DictionaryEntry>();
|
||||
|
||||
return ContextItems.Cast<DictionaryEntry>()
|
||||
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
|
||||
.Where(x => x.Key is string s && s.StartsWith(prefix));
|
||||
}
|
||||
|
||||
protected override void RemoveEntry(string key)
|
||||
@@ -77,6 +106,8 @@ namespace Umbraco.Core.Cache
|
||||
return HasContextItems ? ContextItems[key] : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lock
|
||||
|
||||
private bool _entered;
|
||||
@@ -103,59 +134,5 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
//no place to cache so just return the callback result
|
||||
if (HasContextItems == false) return getCacheItem();
|
||||
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
try
|
||||
{
|
||||
EnterWriteLock();
|
||||
result = ContextItems[cacheKey] as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
// more than one thread to reach the return statement below and throw - accepted.
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = GetSafeLazy(getCacheItem);
|
||||
ContextItems[cacheKey] = result;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitWriteLock();
|
||||
}
|
||||
|
||||
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
|
||||
// exceptions (but try again and again) and silently eat them - however at
|
||||
// some point we have to report them - so need to re-throw here
|
||||
|
||||
// this does not throw anymore
|
||||
//return result.Value;
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (value is ExceptionHolder eh) eh.Exception.Throw(); // throw once!
|
||||
return value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
#endregion
|
||||
|
||||
private class NoopLocker : DisposableObjectSlim
|
||||
{
|
||||
protected override void DisposeResources()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an application cache.
|
||||
/// </summary>
|
||||
public interface IAppCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an item identified by its key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
/// <returns>The item, or null if the item was not found.</returns>
|
||||
object Get(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates an item identified by its key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
/// <param name="factory">A factory function that can create the item.</param>
|
||||
/// <returns>The item.</returns>
|
||||
object Get(string key, Func<object> factory);
|
||||
|
||||
/// <summary>
|
||||
/// Gets items with a key starting with the specified value.
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The StartsWith value to use in the search.</param>
|
||||
/// <returns>Items matching the search.</returns>
|
||||
IEnumerable<object> SearchByKey(string keyStartsWith);
|
||||
|
||||
/// <summary>
|
||||
/// Gets items with a key matching a regular expression.
|
||||
/// </summary>
|
||||
/// <param name="regex">The regular expression.</param>
|
||||
/// <returns>Items matching the search.</returns>
|
||||
IEnumerable<object> SearchByRegex(string regex);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all items from the cache.
|
||||
/// </summary>
|
||||
void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item identified by its key from the cache.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
void Clear(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Removes items of a specified type from the cache.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The name of the type to remove.</param>
|
||||
/// <remarks>
|
||||
/// <para>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</para>
|
||||
/// <para>Performs a case-sensitive search.</para>
|
||||
/// </remarks>
|
||||
void ClearOfType(string typeName);
|
||||
|
||||
/// <summary>
|
||||
/// Removes items of a specified type from the cache.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the items to remove.</typeparam>
|
||||
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</remarks>
|
||||
void ClearOfType<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Removes items of a specified type from the cache.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the items to remove.</typeparam>
|
||||
/// <param name="predicate">The predicate to satisfy.</param>
|
||||
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</remarks>
|
||||
void ClearOfType<T>(Func<string, T, bool> predicate);
|
||||
|
||||
/// <summary>
|
||||
/// Clears items with a key starting with the specified value.
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The StartsWith value to use in the search.</param>
|
||||
void ClearByKey(string keyStartsWith);
|
||||
|
||||
/// <summary>
|
||||
/// Clears items with a key matching a regular expression.
|
||||
/// </summary>
|
||||
/// <param name="regex">The regular expression.</param>
|
||||
void ClearByRegex(string regex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Web.Caching;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an application cache that support cache policies.
|
||||
/// </summary>
|
||||
/// <remarks>A cache policy can be used to cache with timeouts,
|
||||
/// or depending on files, and with a remove callback, etc.</remarks>
|
||||
public interface IAppPolicyCache : IAppCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an item identified by its key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
/// <param name="factory">A factory function that can create the item.</param>
|
||||
/// <param name="timeout">An optional cache timeout.</param>
|
||||
/// <param name="isSliding">An optional value indicating whether the cache timeout is sliding (default is false).</param>
|
||||
/// <param name="priority">An optional cache priority (default is Normal).</param>
|
||||
/// <param name="removedCallback">An optional callback to handle removals.</param>
|
||||
/// <param name="dependentFiles">Files the cache entry depends on.</param>
|
||||
/// <returns>The item.</returns>
|
||||
object Get(
|
||||
string key,
|
||||
Func<object> factory,
|
||||
TimeSpan? timeout,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an item.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
/// <param name="factory">A factory function that can create the item.</param>
|
||||
/// <param name="timeout">An optional cache timeout.</param>
|
||||
/// <param name="isSliding">An optional value indicating whether the cache timeout is sliding (default is false).</param>
|
||||
/// <param name="priority">An optional cache priority (default is Normal).</param>
|
||||
/// <param name="removedCallback">An optional callback to handle removals.</param>
|
||||
/// <param name="dependentFiles">Files the cache entry depends on.</param>
|
||||
void Insert(
|
||||
string key,
|
||||
Func<object> factory,
|
||||
TimeSpan? timeout = null,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface for implementing a basic cache provider
|
||||
/// </summary>
|
||||
public interface ICacheProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes all items from the cache.
|
||||
/// </summary>
|
||||
void ClearAllCache();
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from the cache, identified by its key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
void ClearCacheItem(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Removes items from the cache, of a specified type.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The name of the type to remove.</param>
|
||||
/// <remarks>
|
||||
/// <para>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</para>
|
||||
/// <para>Performs a case-sensitive search.</para>
|
||||
/// </remarks>
|
||||
void ClearCacheObjectTypes(string typeName);
|
||||
|
||||
/// <summary>
|
||||
/// Removes items from the cache, of a specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the items to remove.</typeparam>
|
||||
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</remarks>
|
||||
void ClearCacheObjectTypes<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Removes items from the cache, of a specified type, satisfying a predicate.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the items to remove.</typeparam>
|
||||
/// <param name="predicate">The predicate to satisfy.</param>
|
||||
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</remarks>
|
||||
void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate);
|
||||
|
||||
void ClearCacheByKeySearch(string keyStartsWith);
|
||||
void ClearCacheByKeyExpression(string regexString);
|
||||
|
||||
IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith);
|
||||
IEnumerable<object> GetCacheItemsByKeyExpression(string regexString);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an item with a given key
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
object GetCacheItem(string cacheKey);
|
||||
|
||||
object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Umbraco.Core.Composing;
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// The IcacheRefresher Interface is used for loadbalancing.
|
||||
/// The IcacheRefresher Interface is used for load balancing.
|
||||
///
|
||||
/// </summary>
|
||||
public interface ICacheRefresher : IDiscoverable
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Umbraco.Core.Cache
|
||||
/// Updates an entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
/// <param name="persistUpdated">The reopsitory PersistUpdatedItem method.</param>
|
||||
/// <param name="persistUpdated">The repository PersistUpdatedItem method.</param>
|
||||
/// <remarks>Updates the entity in the repository, and updates the cache accordingly.</remarks>
|
||||
void Update(TEntity entity, Action<TEntity> persistUpdated);
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Caching;
|
||||
using System.Text;
|
||||
using System.Web.Caching;
|
||||
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract class for implementing a runtime cache provider
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public interface IRuntimeCacheProvider : ICacheProvider
|
||||
{
|
||||
object GetCacheItem(
|
||||
string cacheKey,
|
||||
Func<object> getCacheItem,
|
||||
TimeSpan? timeout,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null);
|
||||
|
||||
void InsertCacheItem(
|
||||
string cacheKey,
|
||||
Func<object> getCacheItem,
|
||||
TimeSpan? timeout = null,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a dictionary of <see cref="IAppPolicyCache"/> for types.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Isolated caches are used by e.g. repositories, to ensure that each cached entity
|
||||
/// type has its own cache, so that lookups are fast and the repository does not need to
|
||||
/// search through all keys on a global scale.</para>
|
||||
/// </remarks>
|
||||
public class IsolatedCaches : AppPolicedCacheDictionary<Type>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IsolatedCaches"/> class.
|
||||
/// </summary>
|
||||
/// <param name="cacheFactory"></param>
|
||||
public IsolatedCaches(Func<Type, IAppPolicyCache> cacheFactory)
|
||||
: base(cacheFactory)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a cache.
|
||||
/// </summary>
|
||||
public IAppPolicyCache GetOrCreate<T>()
|
||||
=> GetOrCreate(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a cache.
|
||||
/// </summary>
|
||||
public Attempt<IAppPolicyCache> Get<T>()
|
||||
=> Get(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Clears a cache.
|
||||
/// </summary>
|
||||
public void ClearCache<T>()
|
||||
=> ClearCache(typeof(T));
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to get/create/manipulate isolated runtime cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful for repository level caches to ensure that cache lookups by key are fast so
|
||||
/// that the repository doesn't need to search through all keys on a global scale.
|
||||
/// </remarks>
|
||||
public class IsolatedRuntimeCache
|
||||
{
|
||||
internal Func<Type, IRuntimeCacheProvider> CacheFactory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that allows specifying a factory for the type of runtime isolated cache to create
|
||||
/// </summary>
|
||||
/// <param name="cacheFactory"></param>
|
||||
public IsolatedRuntimeCache(Func<Type, IRuntimeCacheProvider> cacheFactory)
|
||||
{
|
||||
CacheFactory = cacheFactory;
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<Type, IRuntimeCacheProvider> _isolatedCache = new ConcurrentDictionary<Type, IRuntimeCacheProvider>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns an isolated runtime cache for a given type
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public IRuntimeCacheProvider GetOrCreateCache<T>()
|
||||
{
|
||||
return _isolatedCache.GetOrAdd(typeof(T), type => CacheFactory(type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an isolated runtime cache for a given type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IRuntimeCacheProvider GetOrCreateCache(Type type)
|
||||
{
|
||||
return _isolatedCache.GetOrAdd(type, t => CacheFactory(t));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a cache by the type specified
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public Attempt<IRuntimeCacheProvider> GetCache<T>()
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryGetValue(typeof(T), out cache))
|
||||
{
|
||||
return Attempt.Succeed(cache);
|
||||
}
|
||||
return Attempt<IRuntimeCacheProvider>.Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all values inside this isolated runtime cache
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public void ClearCache<T>()
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryGetValue(typeof(T), out cache))
|
||||
{
|
||||
cache.ClearAllCache();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all of the isolated caches
|
||||
/// </summary>
|
||||
public void ClearAllCaches()
|
||||
{
|
||||
foreach (var key in _isolatedCache.Keys)
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryRemove(key, out cache))
|
||||
{
|
||||
cache.ClearAllCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonCacheRefresherBase{TInstanceType}"/>.
|
||||
/// </summary>
|
||||
/// <param name="cacheHelper">A cache helper.</param>
|
||||
protected JsonCacheRefresherBase(CacheHelper cacheHelper) : base(cacheHelper)
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
protected JsonCacheRefresherBase(AppCaches appCaches) : base(appCaches)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IAppPolicyCache"/> and do not cache.
|
||||
/// </summary>
|
||||
public class NoAppCache : IAppPolicyCache
|
||||
{
|
||||
private NoAppCache() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the singleton instance.
|
||||
/// </summary>
|
||||
public static NoAppCache Instance { get; } = new NoAppCache();
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual object Get(string cacheKey)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual object Get(string cacheKey, Func<object> factory)
|
||||
{
|
||||
return factory();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
return factory();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear(string key)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType(string typeName)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByKey(string keyStartsWith)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByRegex(string regex)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
public static NoCacheRepositoryCachePolicy<TEntity, TId> Instance { get; } = new NoCacheRepositoryCachePolicy<TEntity, TId>();
|
||||
|
||||
public IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
|
||||
public IRepositoryCachePolicy<TEntity, TId> Scoped(IAppPolicyCache runtimeCache, IScope scope)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cache provider that does not cache anything.
|
||||
/// </summary>
|
||||
public class NullCacheProvider : IRuntimeCacheProvider
|
||||
{
|
||||
private NullCacheProvider() { }
|
||||
|
||||
public static NullCacheProvider Instance { get; } = new NullCacheProvider();
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{ }
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
return default(object);
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
+144
-139
@@ -11,31 +11,142 @@ using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cache provider that caches item in a <see cref="MemoryCache"/>.
|
||||
/// A cache provider that wraps the logic of a System.Runtime.Caching.ObjectCache
|
||||
/// Implements <see cref="IAppPolicyCache"/> on top of a <see cref="ObjectCache"/>.
|
||||
/// </summary>
|
||||
/// <remarks>The <see cref="MemoryCache"/> is created with name "in-memory". That name is
|
||||
/// used to retrieve configuration options. It does not identify the memory cache, i.e.
|
||||
/// each instance of this class has its own, independent, memory cache.</remarks>
|
||||
public class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
|
||||
public class ObjectCacheAppCache : IAppPolicyCache
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
internal ObjectCache MemoryCache;
|
||||
|
||||
/// <summary>
|
||||
/// Used for debugging
|
||||
/// Initializes a new instance of the <see cref="ObjectCacheAppCache"/>.
|
||||
/// </summary>
|
||||
internal Guid InstanceId { get; private set; }
|
||||
|
||||
public ObjectCacheRuntimeCacheProvider()
|
||||
public ObjectCacheAppCache()
|
||||
{
|
||||
// the MemoryCache is created with name "in-memory". That name is
|
||||
// used to retrieve configuration options. It does not identify the memory cache, i.e.
|
||||
// each instance of this class has its own, independent, memory cache.
|
||||
MemoryCache = new MemoryCache("in-memory");
|
||||
InstanceId = Guid.NewGuid();
|
||||
}
|
||||
|
||||
#region Clear
|
||||
/// <summary>
|
||||
/// Gets the internal memory cache, for tests only!
|
||||
/// </summary>
|
||||
internal ObjectCache MemoryCache { get; private set; }
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
/// <inheritdoc />
|
||||
public object Get(string key)
|
||||
{
|
||||
Lazy<object> result;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
result = MemoryCache.Get(key) as Lazy<object>; // null if key not found
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return result == null ? null : FastDictionaryAppCacheBase.GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory)
|
||||
{
|
||||
return Get(key, factory, null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
KeyValuePair<string, object>[] entries;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
entries = MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
|
||||
KeyValuePair<string, object>[] entries;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
entries = MemoryCache
|
||||
.Where(x => compiled.IsMatch(x.Key))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
// see notes in HttpRuntimeAppCache
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = MemoryCache.Get(key) as Lazy<object>;
|
||||
if (result == null || FastDictionaryAppCacheBase.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
//NOTE: This does an add or update
|
||||
MemoryCache.Set(key, result, policy);
|
||||
}
|
||||
}
|
||||
|
||||
//return result.Value;
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (value is FastDictionaryAppCacheBase.ExceptionHolder eh) eh.Exception.Throw(); // throw once!
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
|
||||
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
||||
var value = result.Value; // force evaluation now
|
||||
if (value == null) return; // do not store null values (backward compat)
|
||||
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
//NOTE: This does an add or update
|
||||
MemoryCache.Set(key, result, policy);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -50,7 +161,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -65,7 +177,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType(string typeName)
|
||||
{
|
||||
var type = TypeFinder.GetTypeByName(typeName);
|
||||
if (type == null) return;
|
||||
@@ -79,7 +192,7 @@ namespace Umbraco.Core.Cache
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
// otherwise remove exact types (not inherited types)
|
||||
@@ -96,12 +209,13 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>()
|
||||
{
|
||||
try
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
var typeOfT = typeof (T);
|
||||
var typeOfT = typeof(T);
|
||||
var isInterface = typeOfT.IsInterface;
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
@@ -109,7 +223,7 @@ namespace Umbraco.Core.Cache
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
// otherwise remove exact types (not inherited types)
|
||||
@@ -127,7 +241,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -140,7 +255,7 @@ namespace Umbraco.Core.Cache
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
if (value == null) return true;
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
@@ -159,7 +274,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByKey(string keyStartsWith)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -177,13 +293,16 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
|
||||
try
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.Where(x => compiled.IsMatch(x.Key))
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
@@ -195,120 +314,6 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
KeyValuePair<string, object>[] entries;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
entries = MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
KeyValuePair<string, object>[] entries;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
entries = MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey)
|
||||
{
|
||||
Lazy<object> result;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
result = MemoryCache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return GetCacheItem(cacheKey, getCacheItem, null);
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
// see notes in HttpRuntimeCacheProvider
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = MemoryCache.Get(cacheKey) as Lazy<object>;
|
||||
if (result == null || DictionaryCacheProviderBase.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
//NOTE: This does an add or update
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
}
|
||||
|
||||
//return result.Value;
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (value is DictionaryCacheProviderBase.ExceptionHolder eh) eh.Exception.Throw(); // throw once!
|
||||
return value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
|
||||
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
|
||||
var value = result.Value; // force evaluation now
|
||||
if (value == null) return; // do not store null values (backward compat)
|
||||
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
//NOTE: This does an add or update
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
var absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : (timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
@@ -15,8 +15,8 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PayloadCacheRefresherBase{TInstanceType, TPayload}"/>.
|
||||
/// </summary>
|
||||
/// <param name="cacheHelper">A cache helper.</param>
|
||||
protected PayloadCacheRefresherBase(CacheHelper cacheHelper) : base(cacheHelper)
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
protected PayloadCacheRefresherBase(AppCaches appCaches) : base(appCaches)
|
||||
{ }
|
||||
|
||||
#region Json
|
||||
|
||||
@@ -13,16 +13,16 @@ namespace Umbraco.Core.Cache
|
||||
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IEntity
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _globalCache;
|
||||
private readonly IAppPolicyCache _globalCache;
|
||||
private readonly IScopeAccessor _scopeAccessor;
|
||||
|
||||
protected RepositoryCachePolicyBase(IRuntimeCacheProvider globalCache, IScopeAccessor scopeAccessor)
|
||||
protected RepositoryCachePolicyBase(IAppPolicyCache globalCache, IScopeAccessor scopeAccessor)
|
||||
{
|
||||
_globalCache = globalCache ?? throw new ArgumentNullException(nameof(globalCache));
|
||||
_scopeAccessor = scopeAccessor ?? throw new ArgumentNullException(nameof(scopeAccessor));
|
||||
}
|
||||
|
||||
protected IRuntimeCacheProvider Cache
|
||||
protected IAppPolicyCache Cache
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -32,9 +32,9 @@ namespace Umbraco.Core.Cache
|
||||
case RepositoryCacheMode.Default:
|
||||
return _globalCache;
|
||||
case RepositoryCacheMode.Scoped:
|
||||
return ambientScope.IsolatedRuntimeCache.GetOrCreateCache<TEntity>();
|
||||
return ambientScope.IsolatedCaches.GetOrCreate<TEntity>();
|
||||
case RepositoryCacheMode.None:
|
||||
return NullCacheProvider.Instance;
|
||||
return NoAppCache.Instance;
|
||||
default:
|
||||
throw new NotSupportedException($"Repository cache mode {ambientScope.RepositoryCacheMode} is not supported.");
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Cache
|
||||
internal class SingleItemsOnlyRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IEntity
|
||||
{
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
: base(cache, scopeAccessor, options)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cache provider that statically caches item in a concurrent dictionary.
|
||||
/// </summary>
|
||||
public class StaticCacheProvider : ICacheProvider
|
||||
{
|
||||
internal readonly ConcurrentDictionary<string, object> StaticCache = new ConcurrentDictionary<string, object>();
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
StaticCache.Clear();
|
||||
}
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
object val;
|
||||
StaticCache.TryRemove(key, out val);
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName));
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT);
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value));
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
StaticCache.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith));
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
StaticCache.RemoveAll(kvp => Regex.IsMatch(kvp.Key, regexString));
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return (from KeyValuePair<string, object> c in StaticCache
|
||||
where c.Key.InvariantStartsWith(keyStartsWith)
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return (from KeyValuePair<string, object> c in StaticCache
|
||||
where Regex.IsMatch(c.Key, regexString)
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = StaticCache[cacheKey];
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return StaticCache.GetOrAdd(cacheKey, key => getCacheItem());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,9 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypedCacheRefresherBase{TInstanceType, TEntityType}"/>.
|
||||
/// </summary>
|
||||
/// <param name="cacheHelper">A cache helper.</param>
|
||||
protected TypedCacheRefresherBase(CacheHelper cacheHelper)
|
||||
: base(cacheHelper)
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
protected TypedCacheRefresherBase(AppCaches appCaches)
|
||||
: base(appCaches)
|
||||
{ }
|
||||
|
||||
#region Refresher
|
||||
|
||||
+44
-75
@@ -4,14 +4,14 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web.Caching;
|
||||
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A CacheProvider that wraps the logic of the HttpRuntime.Cache
|
||||
/// Implements <see cref="IAppPolicyCache"/> on top of a <see cref="System.Web.Caching.Cache"/>.
|
||||
/// </summary>
|
||||
internal class HttpRuntimeCacheProvider : DictionaryCacheProviderBase, IRuntimeCacheProvider
|
||||
/// <remarks>The underlying cache is expected to be HttpRuntime.Cache.</remarks>
|
||||
internal class WebCachingAppCache : FastDictionaryAppCacheBase, IAppPolicyCache
|
||||
{
|
||||
// locker object that supports upgradeable read locking
|
||||
// does not need to support recursion if we implement the cache correctly and ensure
|
||||
@@ -21,16 +21,43 @@ namespace Umbraco.Core.Cache
|
||||
private readonly System.Web.Caching.Cache _cache;
|
||||
|
||||
/// <summary>
|
||||
/// Used for debugging
|
||||
/// Initializes a new instance of the <see cref="WebCachingAppCache"/> class.
|
||||
/// </summary>
|
||||
internal Guid InstanceId { get; private set; }
|
||||
|
||||
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
|
||||
public WebCachingAppCache(System.Web.Caching.Cache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
InstanceId = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Get(string key, Func<object> factory)
|
||||
{
|
||||
return Get(key, factory, null, dependentFiles: null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
CacheDependency dependency = null;
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
dependency = new CacheDependency(dependentFiles);
|
||||
}
|
||||
return Get(key, factory, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
CacheDependency dependency = null;
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
dependency = new CacheDependency(dependentFiles);
|
||||
}
|
||||
Insert(key, factory, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#region Dictionary
|
||||
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
@@ -48,6 +75,8 @@ namespace Umbraco.Core.Cache
|
||||
return _cache.Get(key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lock
|
||||
|
||||
protected override void EnterReadLock()
|
||||
@@ -74,33 +103,9 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
private object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
{
|
||||
return GetCacheItem(cacheKey, getCacheItem, null, dependentFiles: null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This overload is here for legacy purposes
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <param name="isSliding"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="removedCallback"></param>
|
||||
/// <param name="dependency"></param>
|
||||
/// <returns></returns>
|
||||
internal object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
key = GetCacheKey(key);
|
||||
|
||||
// NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang,
|
||||
// getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and
|
||||
@@ -133,7 +138,7 @@ namespace Umbraco.Core.Cache
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
result = _cache.Get(key) as Lazy<object>; // null if key not found
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -145,7 +150,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
result = _cache.Get(key) as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
@@ -153,13 +158,13 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = GetSafeLazy(getCacheItem);
|
||||
result = GetSafeLazy(factory);
|
||||
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
_cache.Insert(key, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,31 +180,7 @@ namespace Umbraco.Core.Cache
|
||||
return value;
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
CacheDependency dependency = null;
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
dependency = new CacheDependency(dependentFiles);
|
||||
}
|
||||
return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
|
||||
/// <summary>
|
||||
/// This overload is here for legacy purposes
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <param name="isSliding"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="removedCallback"></param>
|
||||
/// <param name="dependency"></param>
|
||||
internal void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
private void Insert(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
{
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
@@ -225,17 +206,5 @@ namespace Umbraco.Core.Cache
|
||||
_locker.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
CacheDependency dependency = null;
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
dependency = new CacheDependency(dependentFiles);
|
||||
}
|
||||
InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -125,6 +125,18 @@ namespace Umbraco.Core.Collections
|
||||
|
||||
}
|
||||
|
||||
public void ReplaceAll(IEnumerable<TValue> values)
|
||||
{
|
||||
if (values == null) throw new ArgumentNullException(nameof(values));
|
||||
|
||||
Clear();
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
if (!Indecies.ContainsKey(key)) return false;
|
||||
|
||||
@@ -76,11 +76,13 @@ namespace Umbraco.Core.Components
|
||||
var composerTypeList = _composerTypes
|
||||
.Where(x =>
|
||||
{
|
||||
// use the min level specified by the attribute if any
|
||||
// otherwise, user composers have Run min level, anything else is Unknown (always run)
|
||||
// use the min/max levels specified by the attribute if any
|
||||
// otherwise, min: user composers are Run, anything else is Unknown (always run)
|
||||
// max: everything is Run (always run)
|
||||
var attr = x.GetCustomAttribute<RuntimeLevelAttribute>();
|
||||
var minLevel = attr?.MinLevel ?? (x.Implements<IUserComposer>() ? RuntimeLevel.Run : RuntimeLevel.Unknown);
|
||||
return _composition.RuntimeState.Level >= minLevel;
|
||||
var maxLevel = attr?.MaxLevel ?? RuntimeLevel.Run;
|
||||
return _composition.RuntimeState.Level >= minLevel && _composition.RuntimeState.Level <= maxLevel;
|
||||
})
|
||||
.ToList();
|
||||
|
||||
@@ -171,27 +173,46 @@ namespace Umbraco.Core.Components
|
||||
// what happens in case of conflicting remote declarations is unspecified. more
|
||||
// precisely, the last declaration to be processed wins, but the order of the
|
||||
// declarations depends on the type finder and is unspecified.
|
||||
|
||||
void UpdateEnableInfo(Type composerType, int weight2, Dictionary<Type, EnableInfo> enabled2, bool value)
|
||||
{
|
||||
if (enabled.TryGetValue(composerType, out var enableInfo) == false) enableInfo = enabled2[composerType] = new EnableInfo();
|
||||
if (enableInfo.Weight > weight2) return;
|
||||
|
||||
enableInfo.Enabled = value;
|
||||
enableInfo.Weight = weight2;
|
||||
}
|
||||
|
||||
var assemblies = types.Select(x => x.Assembly).Distinct();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
foreach (var attr in assembly.GetCustomAttributes<EnableComposerAttribute>())
|
||||
{
|
||||
var type = attr.EnabledType;
|
||||
UpdateEnableInfo(type, 2, enabled, true);
|
||||
}
|
||||
|
||||
foreach (var attr in assembly.GetCustomAttributes<DisableComposerAttribute>())
|
||||
{
|
||||
var type = attr.DisabledType;
|
||||
UpdateEnableInfo(type, 2, enabled, false);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var composerType in types)
|
||||
{
|
||||
foreach (var attr in composerType.GetCustomAttributes<EnableAttribute>())
|
||||
{
|
||||
var type = attr.EnabledType ?? composerType;
|
||||
if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo();
|
||||
var weight = type == composerType ? 1 : 2;
|
||||
if (enableInfo.Weight > weight) continue;
|
||||
|
||||
enableInfo.Enabled = true;
|
||||
enableInfo.Weight = weight;
|
||||
var weight = type == composerType ? 1 : 3;
|
||||
UpdateEnableInfo(type, weight, enabled, true);
|
||||
}
|
||||
|
||||
foreach (var attr in composerType.GetCustomAttributes<DisableAttribute>())
|
||||
{
|
||||
var type = attr.DisabledType ?? composerType;
|
||||
if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo();
|
||||
var weight = type == composerType ? 1 : 2;
|
||||
if (enableInfo.Weight > weight) continue;
|
||||
|
||||
enableInfo.Enabled = false;
|
||||
enableInfo.Weight = weight;
|
||||
var weight = type == composerType ? 1 : 3;
|
||||
UpdateEnableInfo(type, weight, enabled, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Components
|
||||
public class Composition : IRegister
|
||||
{
|
||||
private readonly Dictionary<Type, ICollectionBuilder> _builders = new Dictionary<Type, ICollectionBuilder>();
|
||||
private readonly Dictionary<Type, Unique> _uniques = new Dictionary<Type, Unique>();
|
||||
private readonly Dictionary<string, Action<IRegister>> _uniques = new Dictionary<string, Action<IRegister>>();
|
||||
private readonly IRegister _register;
|
||||
|
||||
/// <summary>
|
||||
@@ -83,11 +83,32 @@ namespace Umbraco.Core.Components
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register<TService>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> _register.Register(factory, lifetime);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterInstance(Type serviceType, object instance)
|
||||
=> _register.RegisterInstance(serviceType, instance);
|
||||
public void Register(Type serviceType, object instance)
|
||||
=> _register.Register(serviceType, instance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> _register.RegisterFor<TService, TTarget>(lifetime);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> _register.RegisterFor<TService, TTarget>(implementingType, lifetime);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> _register.RegisterFor<TService, TTarget>(factory, lifetime);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(TService instance)
|
||||
where TService : class
|
||||
=> _register.RegisterFor<TService, TTarget>(instance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterAuto(Type serviceBaseType)
|
||||
@@ -104,10 +125,12 @@ namespace Umbraco.Core.Components
|
||||
onCreating();
|
||||
|
||||
foreach (var unique in _uniques.Values)
|
||||
unique.RegisterWith(_register);
|
||||
unique(_register);
|
||||
_uniques.Clear(); // no point keep them around
|
||||
|
||||
foreach (var builder in _builders.Values)
|
||||
builder.RegisterWith(_register);
|
||||
_builders.Clear(); // no point keep them around
|
||||
|
||||
Configs.RegisterWith(_register);
|
||||
|
||||
@@ -123,74 +146,78 @@ namespace Umbraco.Core.Components
|
||||
|
||||
#region Unique
|
||||
|
||||
private string GetUniqueName<TService>()
|
||||
=> GetUniqueName(typeof(TService));
|
||||
|
||||
private string GetUniqueName(Type serviceType)
|
||||
=> serviceType.FullName;
|
||||
|
||||
private string GetUniqueName<TService, TTarget>()
|
||||
=> GetUniqueName(typeof(TService), typeof(TTarget));
|
||||
|
||||
private string GetUniqueName(Type serviceType, Type targetType)
|
||||
=> serviceType.FullName + "::" + targetType.FullName;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service.
|
||||
/// Registers a unique service as its own implementation.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUnique(Type serviceType)
|
||||
=> _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, Lifetime.Singleton);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service with an implementation type.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUnique(Type serviceType, Type implementingType)
|
||||
=> _uniques[serviceType] = new Unique(serviceType, implementingType);
|
||||
=> _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, implementingType, Lifetime.Singleton);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUnique(Type serviceType, object instance)
|
||||
=> _uniques[serviceType] = new Unique(serviceType, instance);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service.
|
||||
/// Registers a unique service with an implementation factory.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUnique<TService>(Func<IFactory, TService> factory)
|
||||
=> _uniques[typeof(TService)] = new Unique<TService>(factory);
|
||||
where TService : class
|
||||
=> _uniques[GetUniqueName<TService>()] = register => register.Register<TService>(factory, Lifetime.Singleton);
|
||||
|
||||
private class Unique
|
||||
{
|
||||
private readonly Type _serviceType;
|
||||
private readonly Type _implementingType;
|
||||
private readonly object _instance;
|
||||
/// <summary>
|
||||
/// Registers a unique service with an implementing instance.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUnique(Type serviceType, object instance)
|
||||
=> _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, instance);
|
||||
|
||||
protected Unique(Type serviceType)
|
||||
{
|
||||
_serviceType = serviceType;
|
||||
}
|
||||
/// <summary>
|
||||
/// Registers a unique service for a target, as its own implementation.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUniqueFor<TService, TTarget>()
|
||||
where TService : class
|
||||
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(Lifetime.Singleton);
|
||||
|
||||
public Unique(Type serviceType, Type implementingType)
|
||||
: this(serviceType)
|
||||
{
|
||||
_implementingType = implementingType;
|
||||
}
|
||||
/// <summary>
|
||||
/// Registers a unique service for a target, with an implementing type.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUniqueFor<TService, TTarget>(Type implementingType)
|
||||
where TService : class
|
||||
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(implementingType, Lifetime.Singleton);
|
||||
|
||||
public Unique(Type serviceType, object instance)
|
||||
: this(serviceType)
|
||||
{
|
||||
_instance = instance;
|
||||
}
|
||||
/// <summary>
|
||||
/// Registers a unique service for a target, with an implementation factory.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUniqueFor<TService, TTarget>(Func<IFactory, TService> factory)
|
||||
where TService : class
|
||||
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(factory, Lifetime.Singleton);
|
||||
|
||||
public virtual void RegisterWith(IRegister register)
|
||||
{
|
||||
if (_implementingType != null)
|
||||
register.Register(_serviceType, _implementingType, Lifetime.Singleton);
|
||||
else if (_instance != null)
|
||||
register.RegisterInstance(_serviceType, _instance);
|
||||
}
|
||||
}
|
||||
|
||||
private class Unique<TService> : Unique
|
||||
{
|
||||
private readonly Func<IFactory, TService> _factory;
|
||||
|
||||
public Unique(Func<IFactory, TService> factory)
|
||||
: base(typeof(TService))
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
public override void RegisterWith(IRegister register)
|
||||
{
|
||||
register.Register(_factory, Lifetime.Singleton);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Registers a unique service for a target, with an implementing instance.
|
||||
/// </summary>
|
||||
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
|
||||
public void RegisterUniqueFor<TService, TTarget>(TService instance)
|
||||
where TService : class
|
||||
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(instance);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Core._Legacy.PackageActions;
|
||||
using Umbraco.Core.Logging.Viewer;
|
||||
using Umbraco.Core.PackageActions;
|
||||
|
||||
namespace Umbraco.Core.Components
|
||||
{
|
||||
@@ -26,15 +27,16 @@ namespace Umbraco.Core.Components
|
||||
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
|
||||
/// <typeparam name="TImplementing">The implementing type.</typeparam>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <param name="supportingFileSystemFactory">A factory method creating the supporting filesystem.</param>
|
||||
/// <returns>The register.</returns>
|
||||
public static void RegisterFileSystem<TFileSystem, TImplementing>(this Composition composition, Func<IFactory, IFileSystem> supportingFileSystemFactory)
|
||||
public static void RegisterFileSystem<TFileSystem, TImplementing>(this Composition composition)
|
||||
where TImplementing : FileSystemWrapper, TFileSystem
|
||||
where TFileSystem : class
|
||||
{
|
||||
composition.RegisterUnique<TFileSystem>(factory =>
|
||||
{
|
||||
var fileSystems = factory.GetInstance<FileSystems>();
|
||||
return fileSystems.GetFileSystem<TImplementing>(supportingFileSystemFactory(factory));
|
||||
var supporting = factory.GetInstance<SupportingFileSystems>();
|
||||
return fileSystems.GetFileSystem<TImplementing>(supporting.For<TFileSystem>());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,15 +45,15 @@ namespace Umbraco.Core.Components
|
||||
/// </summary>
|
||||
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <param name="supportingFileSystemFactory">A factory method creating the supporting filesystem.</param>
|
||||
/// <returns>The register.</returns>
|
||||
public static void RegisterFileSystem<TFileSystem>(this Composition composition, Func<IFactory, IFileSystem> supportingFileSystemFactory)
|
||||
public static void RegisterFileSystem<TFileSystem>(this Composition composition)
|
||||
where TFileSystem : FileSystemWrapper
|
||||
{
|
||||
composition.RegisterUnique(factory =>
|
||||
{
|
||||
var fileSystems = factory.GetInstance<FileSystems>();
|
||||
return fileSystems.GetFileSystem<TFileSystem>(supportingFileSystemFactory(factory));
|
||||
var supporting = factory.GetInstance<SupportingFileSystems>();
|
||||
return fileSystems.GetFileSystem<TFileSystem>(supporting.For<TFileSystem>());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -202,7 +204,7 @@ namespace Umbraco.Core.Components
|
||||
/// Sets the server registrar.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <param name="factory">A function creating a server registar.</param>
|
||||
/// <param name="factory">A function creating a server registrar.</param>
|
||||
public static void SetServerRegistrar(this Composition composition, Func<IFactory, IServerRegistrar> factory)
|
||||
{
|
||||
composition.RegisterUnique(factory);
|
||||
@@ -280,6 +282,53 @@ namespace Umbraco.Core.Components
|
||||
composition.RegisterUnique(_ => helper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the underlying media filesystem.
|
||||
/// </summary>
|
||||
/// <param name="composition">A composition.</param>
|
||||
/// <param name="filesystemFactory">A filesystem factory.</param>
|
||||
public static void SetMediaFileSystem(this Composition composition, Func<IFactory, IFileSystem> filesystemFactory)
|
||||
=> composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(filesystemFactory);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the underlying media filesystem.
|
||||
/// </summary>
|
||||
/// <param name="composition">A composition.</param>
|
||||
/// <param name="filesystemFactory">A filesystem factory.</param>
|
||||
public static void SetMediaFileSystem(this Composition composition, Func<IFileSystem> filesystemFactory)
|
||||
=> composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(_ => filesystemFactory());
|
||||
|
||||
/// <summary>
|
||||
/// Sets the log viewer.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the log viewer.</typeparam>
|
||||
/// <param name="composition">The composition.</param>
|
||||
public static void SetLogViewer<T>(this Composition composition)
|
||||
where T : ILogViewer
|
||||
{
|
||||
composition.RegisterUnique<ILogViewer, T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the log viewer.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <param name="factory">A function creating a log viewer.</param>
|
||||
public static void SetLogViewer(this Composition composition, Func<IFactory, ILogViewer> factory)
|
||||
{
|
||||
composition.RegisterUnique(factory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the log viewer.
|
||||
/// </summary>
|
||||
/// <param name="composition">A composition.</param>
|
||||
/// <param name="helper">A log viewer.</param>
|
||||
public static void SetLogViewer(this Composition composition, ILogViewer viewer)
|
||||
{
|
||||
composition.RegisterUnique(_ => viewer);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,8 @@ namespace Umbraco.Core.Components
|
||||
/// <para>If a type is specified, disables the composer of that type, else disables the composer marked with the attribute.</para>
|
||||
/// <para>This attribute is *not* inherited.</para>
|
||||
/// <para>This attribute applies to classes only, it is not possible to enable/disable interfaces.</para>
|
||||
/// <para>If a composer ends up being both enabled and disabled: attributes marking the composer itself have lower priority
|
||||
/// than attributes on *other* composers, eg if a composer declares itself as disabled it is possible to enable it from
|
||||
/// another composer. Anything else is unspecified.</para>
|
||||
/// <para>Assembly-level <see cref="DisableComposerAttribute"/> has greater priority than <see cref="DisableAttribute"/>
|
||||
/// attribute when it is marking the composer itself, but lower priority that when it is referencing another composer.</para>
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
||||
public class DisableAttribute : Attribute
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that a composer should be disabled.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Assembly-level <see cref="DisableComposerAttribute"/> has greater priority than <see cref="DisableAttribute"/>
|
||||
/// attribute when it is marking the composer itself, but lower priority that when it is referencing another composer.</para>
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
|
||||
public class DisableComposerAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DisableComposerAttribute"/> class.
|
||||
/// </summary>
|
||||
public DisableComposerAttribute(Type disabledType)
|
||||
{
|
||||
DisabledType = disabledType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the disabled type, or null if it is the composer marked with the attribute.
|
||||
/// </summary>
|
||||
public Type DisabledType { get; }
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,8 @@ namespace Umbraco.Core.Components
|
||||
/// <para>If a type is specified, enables the composer of that type, else enables the composer marked with the attribute.</para>
|
||||
/// <para>This attribute is *not* inherited.</para>
|
||||
/// <para>This attribute applies to classes only, it is not possible to enable/disable interfaces.</para>
|
||||
/// <para>If a composer ends up being both enabled and disabled: attributes marking the composer itself have lower priority
|
||||
/// than attributes on *other* composers, eg if a composer declares itself as disabled it is possible to enable it from
|
||||
/// another composer. Anything else is unspecified.</para>
|
||||
/// <para>Assembly-level <see cref="DisableComposerAttribute"/> has greater priority than <see cref="DisableAttribute"/>
|
||||
/// attribute when it is marking the composer itself, but lower priority that when it is referencing another composer.</para>
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
||||
public class EnableAttribute : Attribute
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that a composer should be enabled.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>If a type is specified, enables the composer of that type, else enables the composer marked with the attribute.</para>
|
||||
/// <para>This attribute is *not* inherited.</para>
|
||||
/// <para>This attribute applies to classes only, it is not possible to enable/disable interfaces.</para>
|
||||
/// <para>Assembly-level <see cref="DisableComposerAttribute"/> has greater priority than <see cref="DisableAttribute"/>
|
||||
/// attribute when it is marking the composer itself, but lower priority that when it is referencing another composer.</para>
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
|
||||
public class EnableComposerAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EnableComposerAttribute"/> class.
|
||||
/// </summary>
|
||||
public EnableComposerAttribute(Type enabledType)
|
||||
{
|
||||
EnabledType = enabledType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the enabled type, or null if it is the composer marked with the attribute.
|
||||
/// </summary>
|
||||
public Type EnabledType { get; }
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using Umbraco.Core.Services.Implement;
|
||||
|
||||
namespace Umbraco.Core.Components
|
||||
{
|
||||
//TODO: This should just exist in the content service/repo!
|
||||
// TODO: This should just exist in the content service/repo!
|
||||
public sealed class RelateOnCopyComponent : IComponent
|
||||
{
|
||||
public void Initialize()
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
using System;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks a composer to indicate a minimum and/or maximum runtime level for which the composer would compose.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class /*, AllowMultiple = false, Inherited = true*/)]
|
||||
public class RuntimeLevelAttribute : Attribute
|
||||
{
|
||||
//public RuntimeLevelAttribute()
|
||||
//{ }
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum runtime level for which the composer would compose.
|
||||
/// </summary>
|
||||
public RuntimeLevel MinLevel { get; set; } = RuntimeLevel.Install;
|
||||
|
||||
public RuntimeLevel MinLevel { get; set; } = RuntimeLevel.Boot;
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum runtime level for which the composer would compose.
|
||||
/// </summary>
|
||||
public RuntimeLevel MaxLevel { get; set; } = RuntimeLevel.Run;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <typeparam name="TItem">The type of the items.</typeparam>
|
||||
public abstract class CollectionBuilderBase<TBuilder, TCollection, TItem> : ICollectionBuilder<TCollection, TItem>
|
||||
where TBuilder: CollectionBuilderBase<TBuilder, TCollection, TItem>
|
||||
where TCollection : IBuilderCollection<TItem>
|
||||
where TCollection : class, IBuilderCollection<TItem>
|
||||
{
|
||||
private readonly List<Type> _types = new List<Type>();
|
||||
private readonly object _locker = new object();
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace Umbraco.Core.Composing.Composers
|
||||
// register others
|
||||
|
||||
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().Content);
|
||||
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().Templates);
|
||||
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().RequestHandler);
|
||||
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().Security);
|
||||
|
||||
|
||||
@@ -12,10 +12,9 @@ namespace Umbraco.Core.Composing.Composers
|
||||
*
|
||||
* Create a component and use it to modify the composition by adding something like:
|
||||
*
|
||||
* composition.Container.RegisterFileSystem<IMediaFileSystem, MediaFileSystem>(
|
||||
* factory => new PhysicalFileSystem("~/somewhere"));
|
||||
* composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(...);
|
||||
*
|
||||
* return whatever supporting filesystem you like.
|
||||
* and register whatever supporting filesystem you like.
|
||||
*
|
||||
*
|
||||
* HOW TO IMPLEMENT MY OWN FILESYSTEM
|
||||
@@ -30,12 +29,15 @@ namespace Umbraco.Core.Composing.Composers
|
||||
* { }
|
||||
* }
|
||||
*
|
||||
* The ctor can have more parameters that will be resolved by the container.
|
||||
* The ctor can have more parameters, that will be resolved by the container.
|
||||
*
|
||||
* Register your filesystem, in a component:
|
||||
*
|
||||
* composition.Container.RegisterFileSystem<MyFileSystem>(
|
||||
* factory => new PhysicalFileSystem("~/my"));
|
||||
* composition.RegisterFileSystem<MyFileSystem>();
|
||||
*
|
||||
* Register the underlying filesystem:
|
||||
*
|
||||
* composition.RegisterUniqueFor<IFileSystem, MyFileSystem>(...);
|
||||
*
|
||||
* And that's it, you can inject MyFileSystem wherever it's needed.
|
||||
*
|
||||
@@ -48,8 +50,8 @@ namespace Umbraco.Core.Composing.Composers
|
||||
* Make the class implement the interface, then
|
||||
* register your filesystem, in a component:
|
||||
*
|
||||
* composition.Container.RegisterFileSystem<IMyFileSystem, MyFileSystem>(
|
||||
* factory => new PhysicalFileSystem("~/my"));
|
||||
* composition.RegisterFileSystem<IMyFileSystem, MyFileSystem>();
|
||||
* composition.RegisterUniqueFor<IFileSystem, IMyFileSystem>(...);
|
||||
*
|
||||
* And that's it, you can inject IMyFileSystem wherever it's needed.
|
||||
*
|
||||
@@ -79,9 +81,16 @@ namespace Umbraco.Core.Composing.Composers
|
||||
// register the scheme for media paths
|
||||
composition.RegisterUnique<IMediaPathScheme, TwoGuidsMediaPathScheme>();
|
||||
|
||||
// register the IMediaFileSystem implementation with a supporting filesystem
|
||||
composition.RegisterFileSystem<IMediaFileSystem, MediaFileSystem>(
|
||||
factory => new PhysicalFileSystem("~/media"));
|
||||
// register the IMediaFileSystem implementation
|
||||
composition.RegisterFileSystem<IMediaFileSystem, MediaFileSystem>();
|
||||
|
||||
// register the supporting filesystems provider
|
||||
composition.Register(factory => new SupportingFileSystems(factory), Lifetime.Singleton);
|
||||
|
||||
// register the IFileSystem supporting the IMediaFileSystem
|
||||
// THIS IS THE ONLY THING THAT NEEDS TO CHANGE, IN ORDER TO REPLACE THE UNDERLYING FILESYSTEM
|
||||
// and, SupportingFileSystem.For<IMediaFileSystem>() returns the underlying filesystem
|
||||
composition.SetMediaFileSystem(() => new PhysicalFileSystem("~/media"));
|
||||
|
||||
return composition;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Core.Components;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Packaging;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services.Implement;
|
||||
|
||||
@@ -36,6 +37,7 @@ namespace Umbraco.Core.Composing.Composers
|
||||
composition.RegisterUnique<IMemberService, MemberService>();
|
||||
composition.RegisterUnique<IMediaService, MediaService>();
|
||||
composition.RegisterUnique<IContentTypeService, ContentTypeService>();
|
||||
composition.RegisterUnique<IContentTypeBaseServiceProvider, ContentTypeBaseServiceProvider>();
|
||||
composition.RegisterUnique<IMediaTypeService, MediaTypeService>();
|
||||
composition.RegisterUnique<IDataTypeService, DataTypeService>();
|
||||
composition.RegisterUnique<IFileService, FileService>();
|
||||
@@ -56,14 +58,36 @@ namespace Umbraco.Core.Composing.Composers
|
||||
factory.GetInstance<Lazy<LocalizedTextServiceFileSources>>(),
|
||||
factory.GetInstance<ILogger>()));
|
||||
|
||||
//TODO: These are replaced in the web project - we need to declare them so that
|
||||
// something is wired up, just not sure this is very nice but will work for now.
|
||||
composition.RegisterUnique<IApplicationTreeService, EmptyApplicationTreeService>();
|
||||
composition.RegisterUnique<ISectionService, EmptySectionService>();
|
||||
composition.RegisterUnique<IEntityXmlSerializer, EntityXmlSerializer>();
|
||||
|
||||
composition.RegisterUnique<IPackageActionRunner, PackageActionRunner>();
|
||||
|
||||
composition.RegisterUnique<ConflictingPackageData>();
|
||||
composition.RegisterUnique<CompiledPackageXmlParser>();
|
||||
composition.RegisterUnique<ICreatedPackagesRepository>(factory => CreatePackageRepository(factory, "createdPackages.config"));
|
||||
composition.RegisterUnique<IInstalledPackagesRepository>(factory => CreatePackageRepository(factory, "installedPackages.config"));
|
||||
composition.RegisterUnique<PackageDataInstallation>();
|
||||
composition.RegisterUnique<PackageFileInstallation>();
|
||||
composition.RegisterUnique<IPackageInstallation>(factory => //factory required because we need to pass in a string path
|
||||
new PackageInstallation(
|
||||
factory.GetInstance<PackageDataInstallation>(), factory.GetInstance<PackageFileInstallation>(),
|
||||
factory.GetInstance<CompiledPackageXmlParser>(), factory.GetInstance<IPackageActionRunner>(),
|
||||
new DirectoryInfo(IOHelper.GetRootDirectorySafe())));
|
||||
|
||||
return composition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of PackagesRepository for either the ICreatedPackagesRepository or the IInstalledPackagesRepository
|
||||
/// </summary>
|
||||
/// <param name="factory"></param>
|
||||
/// <param name="packageRepoFileName"></param>
|
||||
/// <returns></returns>
|
||||
private static PackagesRepository CreatePackageRepository(IFactory factory, string packageRepoFileName)
|
||||
=> new PackagesRepository(
|
||||
factory.GetInstance<IContentService>(), factory.GetInstance<IContentTypeService>(), factory.GetInstance<IDataTypeService>(), factory.GetInstance<IFileService>(), factory.GetInstance<IMacroService>(), factory.GetInstance<ILocalizationService>(), factory.GetInstance<IEntityXmlSerializer>(), factory.GetInstance<ILogger>(),
|
||||
packageRepoFileName);
|
||||
|
||||
private static LocalizedTextServiceFileSources SourcesFactory(IFactory container)
|
||||
{
|
||||
var mainLangFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Umbraco + "/config/lang/"));
|
||||
@@ -88,7 +112,7 @@ namespace Umbraco.Core.Composing.Composers
|
||||
|
||||
return new LocalizedTextServiceFileSources(
|
||||
container.GetInstance<ILogger>(),
|
||||
container.GetInstance<CacheHelper>().RuntimeCache,
|
||||
container.GetInstance<AppCaches>(),
|
||||
mainLangFolder,
|
||||
pluginLangFolders.Concat(userLangFolders));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Umbraco.Core.Composing
|
||||
public static void RegisterEssentials(this Composition composition,
|
||||
ILogger logger, IProfiler profiler, IProfilingLogger profilingLogger,
|
||||
IMainDom mainDom,
|
||||
CacheHelper appCaches,
|
||||
AppCaches appCaches,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
TypeLoader typeLoader,
|
||||
IRuntimeState state)
|
||||
@@ -28,7 +28,6 @@ namespace Umbraco.Core.Composing
|
||||
composition.RegisterUnique(profilingLogger);
|
||||
composition.RegisterUnique(mainDom);
|
||||
composition.RegisterUnique(appCaches);
|
||||
composition.RegisterUnique(factory => factory.GetInstance<CacheHelper>().RuntimeCache);
|
||||
composition.RegisterUnique(databaseFactory);
|
||||
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoDatabaseFactory>().SqlContext);
|
||||
composition.RegisterUnique(typeLoader);
|
||||
@@ -51,6 +50,13 @@ namespace Umbraco.Core.Composing
|
||||
public static void RegisterUnique<TService, TImplementing>(this Composition composition)
|
||||
=> composition.RegisterUnique(typeof(TService), typeof(TImplementing));
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service with an implementation type, for a target.
|
||||
/// </summary>
|
||||
public static void RegisterUniqueFor<TService, TTarget, TImplementing>(this Composition composition)
|
||||
where TService : class
|
||||
=> composition.RegisterUniqueFor<TService, TTarget>(typeof(TImplementing));
|
||||
|
||||
/// <summary>
|
||||
/// Registers a unique service with an implementing instance.
|
||||
/// </summary>
|
||||
|
||||
@@ -5,13 +5,14 @@ using Umbraco.Core.Dictionary;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.PackageActions;
|
||||
using Umbraco.Core.Packaging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Core._Legacy.PackageActions;
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
@@ -28,7 +29,7 @@ namespace Umbraco.Core.Composing
|
||||
{
|
||||
private static IFactory _factory;
|
||||
|
||||
// fixme - refactor
|
||||
// TODO: get rid of these oddities
|
||||
// we don't want Umbraco tests to die because the container has not been properly initialized,
|
||||
// for some too-important things such as IShortStringHelper or loggers, so if it's not
|
||||
// registered we setup a default one. We should really refactor our tests so that it does
|
||||
@@ -161,6 +162,9 @@ namespace Umbraco.Core.Composing
|
||||
internal static PackageActionCollection PackageActions
|
||||
=> Factory.GetInstance<PackageActionCollection>();
|
||||
|
||||
internal static IPackageActionRunner PackageActionRunner
|
||||
=> Factory.GetInstance<IPackageActionRunner>();
|
||||
|
||||
internal static PropertyValueConverterCollection PropertyValueConverters
|
||||
=> Factory.GetInstance<PropertyValueConverterCollection>();
|
||||
|
||||
@@ -176,8 +180,8 @@ namespace Umbraco.Core.Composing
|
||||
public static ICultureDictionaryFactory CultureDictionaryFactory
|
||||
=> Factory.GetInstance<ICultureDictionaryFactory>();
|
||||
|
||||
public static CacheHelper ApplicationCache
|
||||
=> Factory.GetInstance<CacheHelper>();
|
||||
public static AppCaches AppCaches
|
||||
=> Factory.GetInstance<AppCaches>();
|
||||
|
||||
public static ServiceContext Services
|
||||
=> Factory.GetInstance<ServiceContext>();
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <returns>An instance of the specified type.</returns>
|
||||
/// <remarks>Throws an exception if the factory failed to get an instance of the specified type.</remarks>
|
||||
public static T GetInstance<T>(this IFactory factory)
|
||||
where T : class
|
||||
=> (T)factory.GetInstance(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
@@ -28,6 +29,7 @@ namespace Umbraco.Core.Composing
|
||||
/// of the specified type. Throws an exception if the factory does know how
|
||||
/// to get an instance of the specified type, but failed to do so.</remarks>
|
||||
public static T TryGetInstance<T>(this IFactory factory)
|
||||
where T : class
|
||||
=> (T)factory.TryGetInstance(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
@@ -42,6 +44,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <para>The arguments are used as dependencies by the factory.</para>
|
||||
/// </remarks>
|
||||
public static T CreateInstance<T>(this IFactory factory, params object[] args)
|
||||
where T : class
|
||||
=> (T)factory.CreateInstance(typeof(T), args);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,13 +3,6 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
// Implementing:
|
||||
//
|
||||
// The factory
|
||||
// - always picks the constructor with the most parameters
|
||||
// - supports Lazy parameters (and prefers them over non-Lazy) in constructors
|
||||
// - what happens with 'releasing' is unclear
|
||||
|
||||
/// <summary>
|
||||
/// Defines a service factory for Umbraco.
|
||||
/// </summary>
|
||||
@@ -28,6 +21,15 @@ namespace Umbraco.Core.Composing
|
||||
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
|
||||
object GetInstance(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a targeted instance of a service.
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">The type of the service.</typeparam>
|
||||
/// <typeparam name="TTarget">The type of the target.</typeparam>
|
||||
/// <returns>The instance of the specified type for the specified target.</returns>
|
||||
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
|
||||
TService GetInstanceFor<TService, TTarget>();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get an instance of a service.
|
||||
/// </summary>
|
||||
@@ -48,7 +50,8 @@ namespace Umbraco.Core.Composing
|
||||
/// Gets all instances of a service.
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">The type of the service.</typeparam>
|
||||
IEnumerable<TService> GetAllInstances<TService>();
|
||||
IEnumerable<TService> GetAllInstances<TService>()
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Releases an instance.
|
||||
|
||||
@@ -2,17 +2,6 @@
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
// Implementing:
|
||||
//
|
||||
// The register
|
||||
// - supports registering a service, even after some instances of other services have been created
|
||||
// - supports re-registering a service, as long as no instance of that service has been created
|
||||
// - throws when re-registering a service, and an instance of that service has been created
|
||||
//
|
||||
// - registers only one implementation of a nameless service, re-registering replaces the previous
|
||||
// registration - names are required to register multiple implementations - and getting an
|
||||
// IEnumerable of the service, nameless, returns them all
|
||||
|
||||
/// <summary>
|
||||
/// Defines a service register for Umbraco.
|
||||
/// </summary>
|
||||
@@ -36,12 +25,53 @@ namespace Umbraco.Core.Composing
|
||||
/// <summary>
|
||||
/// Registers a service with an implementation factory.
|
||||
/// </summary>
|
||||
void Register<TService>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient);
|
||||
void Register<TService>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service with an implementing instance.
|
||||
/// </summary>
|
||||
void RegisterInstance(Type serviceType, object instance);
|
||||
void Register(Type serviceType, object instance);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service for a target, as its own implementation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There can only be one implementation or instanced registered for a service and target;
|
||||
/// what happens if many are registered is not specified.
|
||||
/// </remarks>
|
||||
void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service for a target, with an implementation type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There can only be one implementation or instanced registered for a service and target;
|
||||
/// what happens if many are registered is not specified.
|
||||
/// </remarks>
|
||||
void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service for a target, with an implementation factory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There can only be one implementation or instanced registered for a service and target;
|
||||
/// what happens if many are registered is not specified.
|
||||
/// </remarks>
|
||||
void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service for a target, with an implementing instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There can only be one implementation or instanced registered for a service and target;
|
||||
/// what happens if many are registered is not specified.
|
||||
/// </remarks>
|
||||
void RegisterFor<TService, TTarget>(TService instance)
|
||||
where TService : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a base type for auto-registration.
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <typeparam name="TItem">The type of the items.</typeparam>
|
||||
public abstract class LazyCollectionBuilderBase<TBuilder, TCollection, TItem> : CollectionBuilderBase<TBuilder, TCollection, TItem>
|
||||
where TBuilder : LazyCollectionBuilderBase<TBuilder, TCollection, TItem>
|
||||
where TCollection : IBuilderCollection<TItem>
|
||||
where TCollection : class, IBuilderCollection<TItem>
|
||||
{
|
||||
private readonly List<Func<IEnumerable<Type>>> _producers = new List<Func<IEnumerable<Type>>>();
|
||||
private readonly List<Type> _excluded = new List<Type>();
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
/// <summary>
|
||||
/// One unique instance per request.
|
||||
/// </summary>
|
||||
// fixme - not what you think!
|
||||
// TODO: review lifetimes for LightInject vs other containers
|
||||
// currently, corresponds to 'Request' in LightInject which is 'Transient + disposed by Scope'
|
||||
// but NOT (in LightInject) a per-web-request lifetime, more a TransientScoped
|
||||
//
|
||||
|
||||
@@ -102,18 +102,25 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
/// <inheritdoc />
|
||||
public IFactory CreateFactory() => this;
|
||||
|
||||
private static string GetTargetedServiceName<TTarget>() => "TARGET:" + typeof(TTarget).FullName;
|
||||
|
||||
#region Factory
|
||||
|
||||
/// <inheritdoc />
|
||||
public object GetInstance(Type type)
|
||||
=> Container.GetInstance(type);
|
||||
|
||||
/// <inheritdoc />
|
||||
public TService GetInstanceFor<TService, TTarget>()
|
||||
=> Container.GetInstance<TService>(GetTargetedServiceName<TTarget>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public object TryGetInstance(Type type)
|
||||
=> Container.TryGetInstance(type);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<T> GetAllInstances<T>()
|
||||
where T : class
|
||||
=> Container.GetAllInstances<T>();
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -138,21 +145,7 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient)
|
||||
{
|
||||
switch (lifetime)
|
||||
{
|
||||
case Lifetime.Transient:
|
||||
Container.Register(serviceType);
|
||||
break;
|
||||
case Lifetime.Request:
|
||||
case Lifetime.Scope:
|
||||
case Lifetime.Singleton:
|
||||
Container.Register(serviceType, GetLifetime(lifetime));
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"Lifetime {lifetime} is not supported.");
|
||||
}
|
||||
}
|
||||
=> Container.Register(serviceType, GetLifetime(lifetime));
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient)
|
||||
@@ -174,22 +167,41 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register<TService>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
{
|
||||
switch (lifetime)
|
||||
{
|
||||
case Lifetime.Transient:
|
||||
Container.Register(f => factory(this));
|
||||
break;
|
||||
case Lifetime.Request:
|
||||
case Lifetime.Scope:
|
||||
case Lifetime.Singleton:
|
||||
Container.Register(f => factory(this), GetLifetime(lifetime));
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"Lifetime {lifetime} is not supported.");
|
||||
}
|
||||
Container.Register(f => factory(this), GetLifetime(lifetime));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register(Type serviceType, object instance)
|
||||
=> Container.RegisterInstance(serviceType, instance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> RegisterFor<TService, TTarget>(typeof(TService), lifetime);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
{
|
||||
// note that there can only be one implementation or instance registered "for" a service
|
||||
Container.Register(typeof(TService), implementingType, GetTargetedServiceName<TTarget>(), GetLifetime(lifetime));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
{
|
||||
// note that there can only be one implementation or instance registered "for" a service
|
||||
Container.Register(f => factory(this), GetTargetedServiceName<TTarget>(), GetLifetime(lifetime));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterFor<TService, TTarget>(TService instance)
|
||||
where TService : class
|
||||
=> Container.RegisterInstance(typeof(TService), instance, GetTargetedServiceName<TTarget>());
|
||||
|
||||
private ILifetime GetLifetime(Lifetime lifetime)
|
||||
{
|
||||
switch (lifetime)
|
||||
@@ -207,10 +219,6 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterInstance(Type serviceType, object instance)
|
||||
=> Container.RegisterInstance(serviceType, instance);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterAuto(Type serviceBaseType)
|
||||
{
|
||||
@@ -223,17 +231,6 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
}, null);
|
||||
}
|
||||
|
||||
// was the Light-Inject specific way of dealing with args, but we've replaced it with our own
|
||||
// beware! does NOT work on singletons, see https://github.com/seesharper/LightInject/issues/294
|
||||
//
|
||||
///// <inheritdoc />
|
||||
//public void RegisterConstructorDependency<TDependency>(Func<IContainer, ParameterInfo, TDependency> factory)
|
||||
// => Container.RegisterConstructorDependency((f, x) => factory(this, x));
|
||||
//
|
||||
///// <inheritdoc />
|
||||
//public void RegisterConstructorDependency<TDependency>(Func<IContainer, ParameterInfo, object[], TDependency> factory)
|
||||
// => Container.RegisterConstructorDependency((f, x, a) => factory(this, x, a));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Control
|
||||
@@ -256,21 +253,14 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
|
||||
private class AssemblyScanner : IAssemblyScanner
|
||||
{
|
||||
//private readonly IAssemblyScanner _scanner;
|
||||
|
||||
//public AssemblyScanner(IAssemblyScanner scanner)
|
||||
//{
|
||||
// _scanner = scanner;
|
||||
//}
|
||||
|
||||
public void Scan(Assembly assembly, IServiceRegistry serviceRegistry, Func<ILifetime> lifetime, Func<Type, Type, bool> shouldRegister, Func<Type, Type, string> serviceNameProvider)
|
||||
{
|
||||
// nothing - we *could* scan non-Umbraco assemblies, though
|
||||
// nothing - we don't want LightInject to scan
|
||||
}
|
||||
|
||||
public void Scan(Assembly assembly, IServiceRegistry serviceRegistry)
|
||||
{
|
||||
// nothing - we *could* scan non-Umbraco assemblies, though
|
||||
// nothing - we don't want LightInject to scan
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
// of PerWebRequestScopeManagerProvider - but all delegates see is the mixed one - and therefore
|
||||
// they can transition without issues.
|
||||
//
|
||||
// The PerWebRequestScopeManager maintains the scope in HttpContext and LightInject registers a
|
||||
// module (PreApplicationStartMethod) which disposes it on EndRequest
|
||||
//
|
||||
// the mixed provider is installed in container.ConfigureUmbracoCore() and then,
|
||||
// when doing eg container.EnableMvc() or anything that does container.EnablePerWebRequestScope()
|
||||
// we need to take great care to preserve the mixed scope manager provider!
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <typeparam name="TItem">The type of the items.</typeparam>
|
||||
public abstract class OrderedCollectionBuilderBase<TBuilder, TCollection, TItem> : CollectionBuilderBase<TBuilder, TCollection, TItem>
|
||||
where TBuilder : OrderedCollectionBuilderBase<TBuilder, TCollection, TItem>
|
||||
where TCollection : IBuilderCollection<TItem>
|
||||
where TCollection : class, IBuilderCollection<TItem>
|
||||
{
|
||||
protected abstract TBuilder This { get; }
|
||||
|
||||
@@ -78,33 +78,6 @@ namespace Umbraco.Core.Composing
|
||||
return This;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a type after another type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAfter">The other type.</typeparam>
|
||||
/// <typeparam name="T">The type to append.</typeparam>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <remarks>Throws if both types are identical, or if the other type does not already belong to the collection.</remarks>
|
||||
public TBuilder AppendAfter<TAfter, T>()
|
||||
where TAfter : TItem
|
||||
where T : TItem
|
||||
{
|
||||
Configure(types =>
|
||||
{
|
||||
var typeAfter = typeof (TAfter);
|
||||
var type = typeof(T);
|
||||
if (typeAfter == type) throw new InvalidOperationException();
|
||||
|
||||
var index = types.IndexOf(typeAfter);
|
||||
if (index < 0) throw new InvalidOperationException();
|
||||
|
||||
if (types.Contains(type)) types.Remove(type);
|
||||
index = types.IndexOf(typeAfter); // in case removing type changed index
|
||||
types.Insert(index + 1, type);
|
||||
});
|
||||
return This;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a type into the collection.
|
||||
/// </summary>
|
||||
@@ -206,6 +179,69 @@ namespace Umbraco.Core.Composing
|
||||
return This;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a type after another type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAfter">The other type.</typeparam>
|
||||
/// <typeparam name="T">The type to append.</typeparam>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <remarks>Throws if both types are identical, or if the other type does not already belong to the collection.</remarks>
|
||||
public TBuilder InsertAfter<TAfter, T>()
|
||||
where TAfter : TItem
|
||||
where T : TItem
|
||||
{
|
||||
Configure(types =>
|
||||
{
|
||||
var typeAfter = typeof(TAfter);
|
||||
var type = typeof(T);
|
||||
if (typeAfter == type) throw new InvalidOperationException();
|
||||
|
||||
var index = types.IndexOf(typeAfter);
|
||||
if (index < 0) throw new InvalidOperationException();
|
||||
|
||||
if (types.Contains(type)) types.Remove(type);
|
||||
index = types.IndexOf(typeAfter); // in case removing type changed index
|
||||
index += 1; // insert here
|
||||
|
||||
if (index == types.Count)
|
||||
types.Add(type);
|
||||
else
|
||||
types.Insert(index, type);
|
||||
});
|
||||
return This;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a type after another type.
|
||||
/// </summary>
|
||||
/// <param name="typeAfter">The other type.</param>
|
||||
/// <param name="type">The type to insert.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <remarks>Throws if both types are identical, or if the other type does not already belong to the collection.</remarks>
|
||||
public TBuilder InsertAfter(Type typeAfter, Type type)
|
||||
{
|
||||
Configure(types =>
|
||||
{
|
||||
EnsureType(typeAfter, "find");
|
||||
EnsureType(type, "register");
|
||||
|
||||
if (typeAfter == type) throw new InvalidOperationException();
|
||||
|
||||
var index = types.IndexOf(typeAfter);
|
||||
if (index < 0) throw new InvalidOperationException();
|
||||
|
||||
if (types.Contains(type)) types.Remove(type);
|
||||
index = types.IndexOf(typeAfter); // in case removing type changed index
|
||||
index += 1; // insert here
|
||||
|
||||
if (index == types.Count)
|
||||
types.Add(type);
|
||||
else
|
||||
types.Insert(index, type);
|
||||
});
|
||||
return This;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a type from the collection.
|
||||
/// </summary>
|
||||
|
||||
@@ -11,22 +11,32 @@
|
||||
public static void Register<TService, TImplementing>(this IRegister register, Lifetime lifetime = Lifetime.Transient)
|
||||
=> register.Register(typeof(TService), typeof(TImplementing), lifetime);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service with an implementation type, for a target.
|
||||
/// </summary>
|
||||
public static void RegisterFor<TService, TImplementing, TTarget>(this IRegister register, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> register.RegisterFor<TService, TTarget>(typeof(TImplementing), lifetime);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service as its own implementation.
|
||||
/// </summary>
|
||||
public static void Register<TService>(this IRegister register, Lifetime lifetime = Lifetime.Transient)
|
||||
where TService : class
|
||||
=> register.Register(typeof(TService), lifetime);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a service with an implementing instance.
|
||||
/// </summary>
|
||||
public static void RegisterInstance<TService>(this IRegister register, TService instance)
|
||||
=> register.RegisterInstance(typeof(TService), instance);
|
||||
public static void Register<TService>(this IRegister register, TService instance)
|
||||
where TService : class
|
||||
=> register.Register(typeof(TService), instance);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a base type for auto-registration.
|
||||
/// </summary>
|
||||
public static void RegisterAuto<TServiceBase>(this IRegister register)
|
||||
where TServiceBase : class
|
||||
=> register.RegisterAuto(typeof(TServiceBase));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ namespace Umbraco.Core.Composing
|
||||
/// Creates a new instance of the configured container.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To override the default LightInjectContainer, add an appSetting named umbracoContainerType with
|
||||
/// To override the default LightInjectContainer, add an appSetting named 'Umbraco.Core.RegisterType' with
|
||||
/// a fully qualified type name to a class with a static method "Create" returning an IRegister.
|
||||
/// </remarks>
|
||||
public static IRegister Create()
|
||||
{
|
||||
Type type;
|
||||
|
||||
var configuredTypeName = ConfigurationManager.AppSettings["umbracoRegisterType"];
|
||||
var configuredTypeName = ConfigurationManager.AppSettings[Constants.AppSettings.RegisterType];
|
||||
if (configuredTypeName.IsNullOrWhiteSpace())
|
||||
{
|
||||
// try to get the web LightInject container type,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for targeted service factories.
|
||||
/// </summary>
|
||||
/// <typeparam name="TService"></typeparam>
|
||||
public abstract class TargetedServiceFactory<TService>
|
||||
{
|
||||
private readonly IFactory _factory;
|
||||
|
||||
protected TargetedServiceFactory(IFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
public TService For<TTarget>() => _factory.GetInstanceFor<TService, TTarget>();
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Composing
|
||||
if (_assembliesAcceptingLoadExceptions != null)
|
||||
return _assembliesAcceptingLoadExceptions;
|
||||
|
||||
var s = ConfigurationManager.AppSettings["Umbraco.AssembliesAcceptingLoadExceptions"];
|
||||
var s = ConfigurationManager.AppSettings[Constants.AppSettings.AssembliesAcceptingLoadExceptions];
|
||||
return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s)
|
||||
? Array.Empty<string>()
|
||||
: s.Split(',').Select(x => x.Trim()).ToArray();
|
||||
@@ -163,8 +163,8 @@ namespace Umbraco.Core.Composing
|
||||
|
||||
/// <summary>
|
||||
/// Return a list of found local Assemblies excluding the known assemblies we don't want to scan
|
||||
/// and exluding the ones passed in and excluding the exclusion list filter, the results of this are
|
||||
/// cached for perforance reasons.
|
||||
/// and excluding the ones passed in and excluding the exclusion list filter, the results of this are
|
||||
/// cached for performance reasons.
|
||||
/// </summary>
|
||||
/// <param name="excludeFromResults"></param>
|
||||
/// <returns></returns>
|
||||
@@ -183,7 +183,7 @@ namespace Umbraco.Core.Composing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a distinct list of found local Assemblies and exluding the ones passed in and excluding the exclusion list filter
|
||||
/// Return a distinct list of found local Assemblies and excluding the ones passed in and excluding the exclusion list filter
|
||||
/// </summary>
|
||||
/// <param name="excludeFromResults"></param>
|
||||
/// <param name="exclusionFilter"></param>
|
||||
@@ -204,7 +204,7 @@ namespace Umbraco.Core.Composing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// this is our assembly filter to filter out known types that def dont contain types we'd like to find or plugins
|
||||
/// this is our assembly filter to filter out known types that def don't contain types we'd like to find or plugins
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// NOTE the comma vs period... comma delimits the name in an Assembly FullName property so if it ends with comma then its an exact name match
|
||||
@@ -614,7 +614,7 @@ namespace Umbraco.Core.Composing
|
||||
var type = BuildManager.GetType(typeName, false);
|
||||
if (type != null) return type;
|
||||
|
||||
//TODO: This isn't very elegant, and will have issues since the AppDomain.CurrentDomain
|
||||
// TODO: This isn't very elegant, and will have issues since the AppDomain.CurrentDomain
|
||||
// doesn't actualy load in all assemblies, only the types that have been referenced so far.
|
||||
// However, in a web context, the BuildManager will have executed which will force all assemblies
|
||||
// to be loaded so it's fine for now.
|
||||
|
||||
@@ -25,9 +25,6 @@ namespace Umbraco.Core.Composing
|
||||
/// Based on a type we'll check if it is IEnumerable{T} (or similar) and if so we'll return a List{T}, this will also deal with array types and return List{T} for those too.
|
||||
/// If it cannot be done, null is returned.
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
// fixme wtf is this and do we need it in v8?
|
||||
internal static IList CreateGenericEnumerableFromObject(object obj)
|
||||
{
|
||||
var type = obj.GetType();
|
||||
@@ -156,7 +153,7 @@ namespace Umbraco.Core.Composing
|
||||
{
|
||||
var others = types.Except(new[] {curr});
|
||||
|
||||
//is the curr type a common denominator for all others ?
|
||||
//is the current type a common denominator for all others ?
|
||||
var isBase = others.All(curr.IsAssignableFrom);
|
||||
|
||||
//if this type is the base for all others
|
||||
@@ -281,7 +278,7 @@ namespace Umbraco.Core.Composing
|
||||
|
||||
#region Match Type
|
||||
|
||||
//TODO: Need to determine if these methods should replace/combine/merge etc with IsTypeAssignableFrom, IsAssignableFromGeneric
|
||||
// TODO: Need to determine if these methods should replace/combine/merge etc with IsTypeAssignableFrom, IsAssignableFromGeneric
|
||||
|
||||
// readings:
|
||||
// http://stackoverflow.com/questions/2033912/c-sharp-variance-problem-assigning-listderived-as-listbase
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Composing
|
||||
{
|
||||
private const string CacheKey = "umbraco-types.list";
|
||||
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly IAppPolicyCache _runtimeCache;
|
||||
private readonly IProfilingLogger _logger;
|
||||
|
||||
private readonly Dictionary<CompositeTypeTypeKey, TypeList> _types = new Dictionary<CompositeTypeTypeKey, TypeList>();
|
||||
@@ -51,7 +51,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <param name="runtimeCache">The application runtime cache.</param>
|
||||
/// <param name="localTempStorage">Files storage mode.</param>
|
||||
/// <param name="logger">A profiling logger.</param>
|
||||
public TypeLoader(IRuntimeCacheProvider runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger)
|
||||
public TypeLoader(IAppPolicyCache runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger)
|
||||
: this(runtimeCache, localTempStorage, logger, true)
|
||||
{ }
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <param name="localTempStorage">Files storage mode.</param>
|
||||
/// <param name="logger">A profiling logger.</param>
|
||||
/// <param name="detectChanges">Whether to detect changes using hashes.</param>
|
||||
internal TypeLoader(IRuntimeCacheProvider runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger, bool detectChanges)
|
||||
internal TypeLoader(IAppPolicyCache runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger, bool detectChanges)
|
||||
{
|
||||
_runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache));
|
||||
_localTempStorage = localTempStorage == LocalTempStorage.Unknown ? LocalTempStorage.Default : localTempStorage;
|
||||
@@ -109,7 +109,7 @@ namespace Umbraco.Core.Composing
|
||||
/// Gets or sets the set of assemblies to scan.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>If not explicitely set, defaults to all assemblies except those that are know to not have any of the
|
||||
/// <para>If not explicitly set, defaults to all assemblies except those that are know to not have any of the
|
||||
/// types we might scan. Because we only scan for application types, this means we can safely exclude GAC assemblies
|
||||
/// for example.</para>
|
||||
/// <para>This is for unit tests.</para>
|
||||
@@ -185,9 +185,7 @@ namespace Umbraco.Core.Composing
|
||||
// the app code folder and everything in it
|
||||
new Tuple<FileSystemInfo, bool>(new DirectoryInfo(IOHelper.MapPath("~/App_Code")), false),
|
||||
// global.asax (the app domain also monitors this, if it changes will do a full restart)
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath("~/global.asax")), false),
|
||||
// trees.config - use the contents to create the hash since this gets resaved on every app startup!
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true)
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath("~/global.asax")), false)
|
||||
}, _logger);
|
||||
|
||||
return _currentAssembliesHash;
|
||||
@@ -213,7 +211,7 @@ namespace Umbraco.Core.Composing
|
||||
/// file properties (false) or the file contents (true).</remarks>
|
||||
private static string GetFileHash(IEnumerable<Tuple<FileSystemInfo, bool>> filesAndFolders, IProfilingLogger logger)
|
||||
{
|
||||
using (logger.TraceDuration<TypeLoader>("Determining hash of code files on disk", "Hash determined"))
|
||||
using (logger.DebugDuration<TypeLoader>("Determining hash of code files on disk", "Hash determined"))
|
||||
{
|
||||
// get the distinct file infos to hash
|
||||
var uniqInfos = new HashSet<string>();
|
||||
@@ -271,7 +269,7 @@ namespace Umbraco.Core.Composing
|
||||
// internal for tests
|
||||
internal static string GetFileHash(IEnumerable<FileSystemInfo> filesAndFolders, IProfilingLogger logger)
|
||||
{
|
||||
using (logger.TraceDuration<TypeLoader>("Determining hash of code files on disk", "Hash determined"))
|
||||
using (logger.DebugDuration<TypeLoader>("Determining hash of code files on disk", "Hash determined"))
|
||||
{
|
||||
using (var generator = new HashGenerator())
|
||||
{
|
||||
@@ -405,7 +403,7 @@ namespace Umbraco.Core.Composing
|
||||
break;
|
||||
case LocalTempStorage.Default:
|
||||
default:
|
||||
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/TypesCache");
|
||||
var tempFolder = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "TypesCache");
|
||||
_fileBasePath = Path.Combine(tempFolder, "umbraco-types." + NetworkHelper.FileSafeMachineName);
|
||||
break;
|
||||
}
|
||||
@@ -478,7 +476,7 @@ namespace Umbraco.Core.Composing
|
||||
var typesHashFilePath = GetTypesHashFilePath();
|
||||
DeleteFile(typesHashFilePath, FileDeleteTimeout);
|
||||
|
||||
_runtimeCache.ClearCacheItem(CacheKey);
|
||||
_runtimeCache.Clear(CacheKey);
|
||||
}
|
||||
|
||||
private Stream GetFileStream(string path, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, int timeoutMilliseconds)
|
||||
@@ -668,7 +666,7 @@ namespace Umbraco.Core.Composing
|
||||
var name = GetName(baseType, attributeType);
|
||||
|
||||
lock (_locko)
|
||||
using (_logger.TraceDuration<TypeLoader>(
|
||||
using (_logger.DebugDuration<TypeLoader>(
|
||||
"Getting " + name,
|
||||
"Got " + name)) // cannot contain typesFound.Count as it's evaluated before the find
|
||||
{
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.PackageActions;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core._Legacy.PackageActions;
|
||||
|
||||
namespace Umbraco.Core.Composing
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <typeparam name="TItem">The type of the items.</typeparam>
|
||||
public abstract class WeightedCollectionBuilderBase<TBuilder, TCollection, TItem> : CollectionBuilderBase<TBuilder, TCollection, TItem>
|
||||
where TBuilder : WeightedCollectionBuilderBase<TBuilder, TCollection, TItem>
|
||||
where TCollection : IBuilderCollection<TItem>
|
||||
where TCollection : class, IBuilderCollection<TItem>
|
||||
{
|
||||
protected abstract TBuilder This { get; }
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace Umbraco.Core.Composing
|
||||
return list;
|
||||
}
|
||||
|
||||
protected virtual int DefaultWeight { get; set; } = 10;
|
||||
public virtual int DefaultWeight { get; set; } = 100;
|
||||
|
||||
protected virtual int GetWeight(Type type)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Dashboard;
|
||||
using Umbraco.Core.Configuration.Grid;
|
||||
using Umbraco.Core.Configuration.HealthChecks;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
@@ -22,9 +21,6 @@ namespace Umbraco.Core
|
||||
public static IUmbracoSettingsSection Settings(this Configs configs)
|
||||
=> configs.GetConfig<IUmbracoSettingsSection>();
|
||||
|
||||
public static IDashboardSection Dashboards(this Configs configs)
|
||||
=> configs.GetConfig<IDashboardSection>();
|
||||
|
||||
public static IHealthChecks HealthChecks(this Configs configs)
|
||||
=> configs.GetConfig<IHealthChecks>();
|
||||
|
||||
@@ -40,13 +36,12 @@ namespace Umbraco.Core
|
||||
|
||||
configs.Add<IGlobalSettings>(() => new GlobalSettings());
|
||||
configs.Add<IUmbracoSettingsSection>("umbracoConfiguration/settings");
|
||||
configs.Add<IDashboardSection>("umbracoConfiguration/dashBoard");
|
||||
configs.Add<IHealthChecks>("umbracoConfiguration/HealthChecks");
|
||||
|
||||
configs.Add(() => new CoreDebug());
|
||||
|
||||
// GridConfig depends on runtime caches, manifest parsers... and cannot be available during composition
|
||||
configs.Add<IGridConfig>(factory => new GridConfig(factory.GetInstance<ILogger>(), factory.GetInstance<IRuntimeCacheProvider>(), configDir, factory.GetInstance<IRuntimeState>().Debug));
|
||||
configs.Add<IGridConfig>(factory => new GridConfig(factory.GetInstance<ILogger>(), factory.GetInstance<AppCaches>(), configDir, factory.GetInstance<IRuntimeState>().Debug));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace Umbraco.Core.Configuration
|
||||
if (_registerings == null)
|
||||
throw new InvalidOperationException("Configurations have already been registered.");
|
||||
|
||||
register.RegisterInstance(this);
|
||||
register.Register(this);
|
||||
|
||||
foreach (var registering in _registerings.Values)
|
||||
registering(register);
|
||||
|
||||
@@ -7,15 +7,15 @@ namespace Umbraco.Core.Configuration
|
||||
public CoreDebug()
|
||||
{
|
||||
var appSettings = System.Configuration.ConfigurationManager.AppSettings;
|
||||
LogUncompletedScopes = string.Equals("true", appSettings["Umbraco.CoreDebug.LogUncompletedScopes"], StringComparison.OrdinalIgnoreCase);
|
||||
DumpOnTimeoutThreadAbort = string.Equals("true", appSettings["Umbraco.CoreDebug.DumpOnTimeoutThreadAbort"], StringComparison.OrdinalIgnoreCase);
|
||||
LogUncompletedScopes = string.Equals("true", appSettings[Constants.AppSettings.Debug.LogUncompletedScopes], StringComparison.OrdinalIgnoreCase);
|
||||
DumpOnTimeoutThreadAbort = string.Equals("true", appSettings[Constants.AppSettings.Debug.DumpOnTimeoutThreadAbort], StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
// when true, Scope logs the stack trace for any scope that gets disposed without being completed.
|
||||
// this helps troubleshooting rogue scopes that we forget to complete
|
||||
public bool LogUncompletedScopes { get; }
|
||||
|
||||
// when true, the Logger creates a minidump of w3wp in ~/App_Data/MiniDump whenever it logs
|
||||
// when true, the Logger creates a mini dump of w3wp in ~/App_Data/MiniDump whenever it logs
|
||||
// an error due to a ThreadAbortException that is due to a timeout.
|
||||
public bool DumpOnTimeoutThreadAbort { get; }
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class AccessElement : RawXmlConfigurationElement, IAccess
|
||||
{
|
||||
public AccessElement()
|
||||
{ }
|
||||
|
||||
public AccessElement(XElement rawXml)
|
||||
: base(rawXml)
|
||||
{ }
|
||||
|
||||
public IEnumerable<IAccessRule> Rules
|
||||
{
|
||||
get
|
||||
{
|
||||
var result = new List<AccessRule>();
|
||||
if (RawXml == null) return result;
|
||||
|
||||
result.AddRange(RawXml.Elements("deny").Select(x => new AccessRule {Type = AccessRuleType.Deny, Value = x.Value }));
|
||||
result.AddRange(RawXml.Elements("grant").Select(x => new AccessRule { Type = AccessRuleType.Grant, Value = x.Value }));
|
||||
result.AddRange(RawXml.Elements("grantBySection").Select(x => new AccessRule { Type = AccessRuleType.GrantBySection, Value = x.Value }));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class AreaCollection : ConfigurationElementCollection, IEnumerable<IArea>
|
||||
{
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
return new AreaElement();
|
||||
}
|
||||
|
||||
protected override object GetElementKey(ConfigurationElement element)
|
||||
{
|
||||
return ((AreaElement) element).Value;
|
||||
}
|
||||
|
||||
IEnumerator<IArea> IEnumerable<IArea>.GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
yield return BaseGet(i) as IArea;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class AreaElement : InnerTextConfigurationElement<string>, IArea
|
||||
{
|
||||
string IArea.AreaName
|
||||
{
|
||||
get { return Value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class AreasElement : ConfigurationElement
|
||||
{
|
||||
[ConfigurationCollection(typeof(SectionCollection), AddItemName = "area")]
|
||||
[ConfigurationProperty("", IsDefaultCollection = true)]
|
||||
public AreaCollection AreaCollection
|
||||
{
|
||||
get { return (AreaCollection)base[""]; }
|
||||
set { base[""] = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class ControlCollection : ConfigurationElementCollection, IEnumerable<IDashboardControl>
|
||||
{
|
||||
internal void Add(ControlElement c)
|
||||
{
|
||||
BaseAdd(c);
|
||||
}
|
||||
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
return new ControlElement();
|
||||
}
|
||||
|
||||
protected override object GetElementKey(ConfigurationElement element)
|
||||
{
|
||||
return ((ControlElement)element).ControlPath;
|
||||
}
|
||||
|
||||
IEnumerator<IDashboardControl> IEnumerable<IDashboardControl>.GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
yield return BaseGet(i) as IDashboardControl;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
|
||||
internal class ControlElement : RawXmlConfigurationElement, IDashboardControl
|
||||
{
|
||||
public string PanelCaption
|
||||
{
|
||||
get
|
||||
{
|
||||
var panelCaption = RawXml.Attribute("panelCaption");
|
||||
return panelCaption == null ? "" : panelCaption.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public AccessElement Access
|
||||
{
|
||||
get
|
||||
{
|
||||
var access = RawXml.Element("access");
|
||||
return access == null ? new AccessElement() : new AccessElement(access);
|
||||
}
|
||||
}
|
||||
|
||||
public string ControlPath
|
||||
{
|
||||
get
|
||||
{
|
||||
//we need to return the first (and only) text element of the children (wtf... who designed this configuration ! :P )
|
||||
var txt = RawXml.Nodes().OfType<XText>().FirstOrDefault();
|
||||
if (txt == null)
|
||||
{
|
||||
throw new ConfigurationErrorsException("The control element must contain a text node indicating the control path");
|
||||
}
|
||||
return txt.Value.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
IAccess IDashboardControl.AccessRights => Access;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
internal class DashboardSection : ConfigurationSection, IDashboardSection
|
||||
{
|
||||
[ConfigurationCollection(typeof(SectionCollection), AddItemName = "section")]
|
||||
[ConfigurationProperty("", IsDefaultCollection = true)]
|
||||
public SectionCollection SectionCollection
|
||||
{
|
||||
get { return (SectionCollection)base[""]; }
|
||||
set { base[""] = value; }
|
||||
}
|
||||
|
||||
IEnumerable<ISection> IDashboardSection.Sections
|
||||
{
|
||||
get { return SectionCollection; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IAccess
|
||||
{
|
||||
IEnumerable<IAccessRule> Rules { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IArea
|
||||
{
|
||||
string AreaName { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IDashboardControl
|
||||
{
|
||||
string PanelCaption { get; }
|
||||
|
||||
string ControlPath { get; }
|
||||
|
||||
IAccess AccessRights { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IDashboardSection
|
||||
{
|
||||
IEnumerable<ISection> Sections { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Dashboard
|
||||
{
|
||||
public interface IDashboardTab
|
||||
{
|
||||
string Caption { get; }
|
||||
|
||||
IEnumerable<IDashboardControl> Controls { get; }
|
||||
|
||||
IAccess AccessRights { get; }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user