{"action":"created","comment":{"url":"https://api.github.com/repos/aspnet/Caching/pulls/comments/32345206","id":32345206,"diff_hunk":"@@ -0,0 +1,395 @@\n+// Copyright (c) .NET Foundation. All rights reserved.\n+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\n+\n+using System;\n+using System.Data;\n+using System.Data.SqlClient;\n+using System.Threading;\n+using System.Threading.Tasks;\n+using Microsoft.Framework.Caching.Distributed;\n+using Microsoft.Framework.Internal;\n+using Microsoft.Framework.Logging;\n+using Microsoft.Framework.OptionsModel;\n+\n+namespace Microsoft.Framework.Caching.SqlServer\n+{\n+    public class SqlServerCache : IDistributedCache\n+    {\n+        internal const int ExpirationTimeMultiplier = 2;\n+\n+        // Table column names\n+        private const string CacheItemId = \"Id\";\n+        private const string CacheItemValue = \"Value\";\n+        private const string ExpiresAtTimeUTC = \"ExpiresAtTimeUTC\";\n+        private const string SlidingExpirationInTicks = \"SlidingExpirationInTicks\";\n+\n+        private readonly SqlServerCacheOptions _options;\n+        private readonly ILogger _logger;\n+        private readonly ISystemClock _systemClock;\n+        private readonly SqlQueries _sqlQueries;\n+        private DateTimeOffset _lastExpirationScan;\n+\n+        //TODO: ADD LOGGING\n+        public SqlServerCache(IOptions<SqlServerCacheOptions> options, ILoggerFactory loggerFactory)\n+        {\n+            _options = options.Options;\n+            _systemClock = _options.SystemClock;\n+            _sqlQueries = new SqlQueries(_options.TableName);\n+            _logger = loggerFactory.CreateLogger<SqlServerCache>();\n+        }\n+\n+        public void Connect()\n+        {\n+            //TODO:\n+        }\n+\n+        public Task ConnectAsync()\n+        {\n+            // TODO:\n+            return Task.FromResult(0);\n+        }\n+\n+        public byte[] Get([NotNull] string key)\n+        {\n+            byte[] cacheItemvalue = null;\n+            var utcNow = _systemClock.UtcNow;\n+\n+            using (var connection = new SqlConnection(_options.ConnectionString))\n+            {\n+                var command = new SqlCommand(_sqlQueries.GetCacheItem, connection);\n+                command.Parameters.AddWithValue(CacheItemId, key);\n+                command.Parameters.AddWithValue(\"UtcNow\", utcNow);\n+\n+                connection.Open();\n+\n+                var reader = command.ExecuteReader(CommandBehavior.SingleRow);\n+\n+                DateTimeOffset oldExpirationTimeUTC;\n+                TimeSpan? slidingExpiration = null;\n+                if (reader.Read())\n+                {\n+                    // TODO: cache these oridnals as they are not going to change once the app starts\n+                    var expiresAtTimeUTCIndex = reader.GetOrdinal(ExpiresAtTimeUTC);\n+                    var cacheItemValueIndex = reader.GetOrdinal(CacheItemValue);\n+                    var slidingExpirationInTicksIndex = reader.GetOrdinal(SlidingExpirationInTicks);\n+\n+                    oldExpirationTimeUTC = reader.GetFieldValue<DateTimeOffset>(expiresAtTimeUTCIndex);\n+                    cacheItemvalue = reader.GetFieldValue<byte[]>(cacheItemValueIndex);\n+\n+                    if (!reader.IsDBNull(slidingExpirationInTicksIndex))\n+                    {\n+                        slidingExpiration = TimeSpan.FromTicks(\n+                            reader.GetFieldValue<long>(slidingExpirationInTicksIndex));\n+                    }\n+                }\n+                else\n+                {\n+                    return null;\n+                }\n+\n+                // Check if the current item has a sliding expiration and is going to expire within the\n+                // next timeout period(ex:20 minutes), then extend the expiration time.\n+                if (slidingExpiration.HasValue)\n+                {\n+                    if (utcNow >= (oldExpirationTimeUTC - slidingExpiration.Value))\n+                    {\n+                        reader.Dispose();\n+\n+                        command = new SqlCommand(_sqlQueries.UpdateCacheItemExpiration, connection);\n+                        command.Parameters.AddWithValue(CacheItemId, key);\n+                        command.Parameters.AddWithValue(\n+                            ExpiresAtTimeUTC,\n+                            GetNewExpirationTime(slidingExpiration.Value, utcNow));\n+\n+                        var recordsAffected = command.ExecuteNonQuery();\n+                        //TODO: log if records not affected?\n+                    }\n+                }\n+            }\n+\n+            StartScanForExpiredCacheItems();\n+\n+            return cacheItemvalue;\n+        }\n+\n+        public async Task<byte[]> GetAsync([NotNull] string key)\n+        {\n+            byte[] cacheItemvalue = null;\n+            var utcNow = _systemClock.UtcNow;\n+\n+            using (var connection = new SqlConnection(_options.ConnectionString))\n+            {\n+                var command = new SqlCommand(_sqlQueries.GetCacheItem, connection);\n+                command.Parameters.AddWithValue(CacheItemId, key);\n+                command.Parameters.AddWithValue(\"UtcNow\", utcNow);\n+\n+                await connection.OpenAsync();\n+\n+                var reader = await command.ExecuteReaderAsync(CommandBehavior.SingleRow);\n+\n+                DateTimeOffset oldExpirationTimeUTC;\n+                TimeSpan? slidingExpiration = null;\n+                if (await reader.ReadAsync())\n+                {\n+                    // TODO: cache these oridnals as they are not going to change once the app starts\n+                    var expiresAtTimeUTCIndex = reader.GetOrdinal(ExpiresAtTimeUTC);\n+                    var cacheItemValueIndex = reader.GetOrdinal(CacheItemValue);\n+                    var slidingExpirationInTicksIndex = reader.GetOrdinal(SlidingExpirationInTicks);\n+\n+                    oldExpirationTimeUTC = await reader.GetFieldValueAsync<DateTimeOffset>(expiresAtTimeUTCIndex);\n+                    cacheItemvalue = await reader.GetFieldValueAsync<byte[]>(cacheItemValueIndex);\n+\n+                    if (!await reader.IsDBNullAsync(slidingExpirationInTicksIndex))\n+                    {\n+                        slidingExpiration = TimeSpan.FromTicks(\n+                            await reader.GetFieldValueAsync<long>(slidingExpirationInTicksIndex));\n+                    }\n+                }\n+                else\n+                {\n+                    return null;\n+                }\n+\n+                // Check if the current item has a sliding expiration and is going to expire within the\n+                // next timeout period(ex:20 minutes), then extend the expiration time.\n+                if (slidingExpiration.HasValue)\n+                {\n+                    if (utcNow >= (oldExpirationTimeUTC - slidingExpiration.Value))\n+                    {\n+                        reader.Dispose();\n+\n+                        command = new SqlCommand(_sqlQueries.UpdateCacheItemExpiration, connection);\n+                        command.Parameters.AddWithValue(CacheItemId, key);\n+                        command.Parameters.AddWithValue(\n+                            ExpiresAtTimeUTC,\n+                            GetNewExpirationTime(slidingExpiration.Value, utcNow));\n+\n+                        var recordsAffected = await command.ExecuteNonQueryAsync();\n+                        //TODO: log if records not affected?\n+                    }\n+                }\n+            }\n+\n+            StartScanForExpiredCacheItems();\n+\n+            return cacheItemvalue;\n+        }\n+\n+        public void Refresh([NotNull] string key)\n+        {\n+            var value = Get(key);\n+        }\n+\n+        public async Task RefreshAsync([NotNull] string key)\n+        {\n+            var value = await GetAsync(key);\n+        }\n+\n+        public void Remove([NotNull] string key)\n+        {\n+            using (var connection = new SqlConnection(_options.ConnectionString))\n+            {\n+                var command = new SqlCommand(_sqlQueries.DeleteCacheItem, connection);\n+                command.Parameters.AddWithValue(CacheItemId, key);\n+\n+                connection.Open();\n+\n+                var effectedRowCount = command.ExecuteNonQuery();\n+            }\n+\n+            StartScanForExpiredCacheItems();\n+        }\n+\n+        public async Task RemoveAsync([NotNull] string key)\n+        {\n+            using (var connection = new SqlConnection(_options.ConnectionString))\n+            {\n+                var command = new SqlCommand(_sqlQueries.DeleteCacheItem, connection);\n+                command.Parameters.AddWithValue(CacheItemId, key);\n+\n+                await connection.OpenAsync();\n+\n+                var effectedRowCount = await command.ExecuteNonQueryAsync();\n+            }\n+\n+            StartScanForExpiredCacheItems();\n+        }\n+\n+        public void Set([NotNull] string key, [NotNull] byte[] value, [NotNull] DistributedCacheEntryOptions options)\n+        {\n+            var utcNow = _systemClock.UtcNow;\n+            var expirationInfo = GetExpirationInfo(utcNow, options);\n+\n+            using (var connection = new SqlConnection(_options.ConnectionString))\n+            {\n+                // Try updating the cache item and if it does not succeed then its probably a new item\n+                var command = new SqlCommand(_sqlQueries.UpdateCacheItem, connection);\n+                command.Parameters.AddWithValue(CacheItemId, key);\n+                command.Parameters.AddWithValue(CacheItemValue, value);\n+                command.Parameters.AddWithValue(ExpiresAtTimeUTC, expirationInfo.ExpiresAtTimeUTC);\n+\n+                connection.Open();\n+\n+                var recordsAffected = command.ExecuteNonQuery();\n+\n+                // only add if update didn't affect any records\n+                if (recordsAffected <= 0)\n+                {\n+                    command = new SqlCommand(_sqlQueries.AddCacheItem, connection);\n+                    command.Parameters.AddWithValue(CacheItemId, key);\n+                    command.Parameters.AddWithValue(CacheItemValue, value);\n+                    command.Parameters.AddWithValue(ExpiresAtTimeUTC, expirationInfo.ExpiresAtTimeUTC);\n+\n+                    if(expirationInfo.SlidingExpiration.HasValue)\n+                    {\n+                        command.Parameters.AddWithValue(\n+                            SlidingExpirationInTicks,\n+                            expirationInfo.SlidingExpiration.Value.Ticks);\n+                    }\n+                    else\n+                    {\n+                        command.Parameters.AddWithValue(SlidingExpirationInTicks, DBNull.Value);\n+                    }\n+\n+                    recordsAffected = command.ExecuteNonQuery();\n+                }\n+            }\n+\n+            StartScanForExpiredCacheItems();\n+        }\n+\n+        public async Task SetAsync(\n+            [NotNull] string key,\n+            [NotNull] byte[] value,\n+            [NotNull] DistributedCacheEntryOptions options)\n+        {\n+            var utcNow = _systemClock.UtcNow;\n+            var expirationInfo = GetExpirationInfo(utcNow, options);\n+\n+            using (var connection = new SqlConnection(_options.ConnectionString))\n+            {\n+                // Try updating the cache item and if it does not succeed then its probably a new item\n+                var command = new SqlCommand(_sqlQueries.UpdateCacheItem, connection);\n+                command.Parameters.AddWithValue(CacheItemId, key);\n+                command.Parameters.AddWithValue(CacheItemValue, value);\n+                command.Parameters.AddWithValue(ExpiresAtTimeUTC, expirationInfo.ExpiresAtTimeUTC);\n+\n+                await connection.OpenAsync();\n+\n+                var recordsAffected = await command.ExecuteNonQueryAsync();\n+\n+                // only add if update didn't affect any records\n+                if (recordsAffected <= 0)\n+                {\n+                    command = new SqlCommand(_sqlQueries.AddCacheItem, connection);\n+                    command.Parameters.AddWithValue(CacheItemId, key);\n+                    command.Parameters.AddWithValue(CacheItemValue, value);\n+                    command.Parameters.AddWithValue(ExpiresAtTimeUTC, expirationInfo.ExpiresAtTimeUTC);\n+\n+                    if (expirationInfo.SlidingExpiration.HasValue)\n+                    {\n+                        command.Parameters.AddWithValue(\n+                            SlidingExpirationInTicks,\n+                            expirationInfo.SlidingExpiration.Value.Ticks);\n+                    }\n+                    else\n+                    {\n+                        command.Parameters.AddWithValue(SlidingExpirationInTicks, DBNull.Value);\n+                    }\n+\n+                    recordsAffected = await command.ExecuteNonQueryAsync();\n+                }\n+            }\n+\n+            StartScanForExpiredCacheItems();\n+        }\n+\n+        // Called by multiple actions to see how long it's been since we last checked for expired items.\n+        // If sufficient time has elapsed then a scan is initiated on a background task.\n+        private void StartScanForExpiredCacheItems()\n+        {\n+            var utcNow = _systemClock.UtcNow;\n+            if ((utcNow - _lastExpirationScan) > _options.ExpirationScanFrequency)\n+            {\n+                _lastExpirationScan = utcNow;\n+                ThreadPool.QueueUserWorkItem(DeleteExpiredCacheItems, state: utcNow);\n+            }\n+        }\n+\n+        // to enable unit testing\n+        internal void DeleteExpiredCacheItems(object state)\n+        {\n+            var utcNow = (DateTimeOffset)state;\n+            var connection = new SqlConnection(_options.ConnectionString);\n+            var command = new SqlCommand(_sqlQueries.DeleteExpiredCacheItems, connection);\n+            command.Parameters.AddWithValue(\"UtcNow\", utcNow);\n+\n+            try\n+            {\n+                connection.Open();\n+\n+                var effectedRowCount = command.ExecuteNonQuery();\n+            }\n+            catch (Exception ex)\n+            {\n+                _logger.LogError(\"Error deleting expired cache items\", ex);\n+                //TODO: should we throw exception here...if yes, will it be caught as this is a background thread","path":"src/Microsoft.Framework.Caching.SqlServer/SqlServerCache.cs","position":336,"original_position":336,"commit_id":"11c72b309c1e912199d77ee2f1ef24e054768532","original_commit_id":"11c72b309c1e912199d77ee2f1ef24e054768532","user":{"login":"Tratcher","id":1821173,"avatar_url":"https://avatars.githubusercontent.com/u/1821173?v=3","gravatar_id":"","url":"https://api.github.com/users/Tratcher","html_url":"https://github.com/Tratcher","followers_url":"https://api.github.com/users/Tratcher/followers","following_url":"https://api.github.com/users/Tratcher/following{/other_user}","gists_url":"https://api.github.com/users/Tratcher/gists{/gist_id}","starred_url":"https://api.github.com/users/Tratcher/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Tratcher/subscriptions","organizations_url":"https://api.github.com/users/Tratcher/orgs","repos_url":"https://api.github.com/users/Tratcher/repos","events_url":"https://api.github.com/users/Tratcher/events{/privacy}","received_events_url":"https://api.github.com/users/Tratcher/received_events","type":"User","site_admin":false},"body":"Don't throw from background tasks. Logging is your best option.","created_at":"2015-06-12T19:07:26Z","updated_at":"2015-06-12T19:07:26Z","html_url":"https://github.com/aspnet/Caching/pull/70#discussion_r32345206","pull_request_url":"https://api.github.com/repos/aspnet/Caching/pulls/70","_links":{"self":{"href":"https://api.github.com/repos/aspnet/Caching/pulls/comments/32345206"},"html":{"href":"https://github.com/aspnet/Caching/pull/70#discussion_r32345206"},"pull_request":{"href":"https://api.github.com/repos/aspnet/Caching/pulls/70"}}},"pull_request":{"url":"https://api.github.com/repos/aspnet/Caching/pulls/70","id":37491892,"html_url":"https://github.com/aspnet/Caching/pull/70","diff_url":"https://github.com/aspnet/Caching/pull/70.diff","patch_url":"https://github.com/aspnet/Caching/pull/70.patch","issue_url":"https://api.github.com/repos/aspnet/Caching/issues/70","number":70,"state":"open","locked":false,"title":"[Design] Implementation of SQL Server Cache","user":{"login":"kichalla","id":6559784,"avatar_url":"https://avatars.githubusercontent.com/u/6559784?v=3","gravatar_id":"","url":"https://api.github.com/users/kichalla","html_url":"https://github.com/kichalla","followers_url":"https://api.github.com/users/kichalla/followers","following_url":"https://api.github.com/users/kichalla/following{/other_user}","gists_url":"https://api.github.com/users/kichalla/gists{/gist_id}","starred_url":"https://api.github.com/users/kichalla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kichalla/subscriptions","organizations_url":"https://api.github.com/users/kichalla/orgs","repos_url":"https://api.github.com/users/kichalla/repos","events_url":"https://api.github.com/users/kichalla/events{/privacy}","received_events_url":"https://api.github.com/users/kichalla/received_events","type":"User","site_admin":false},"body":"@Tratcher @Eilon @muratg  @lodejard\r\n\r\nPlease note that I do not have tests yet, but I have verified that the following is working end-to-end with different samples(including the one in Session repo : https://github.com/aspnet/Session/pull/34)","created_at":"2015-06-11T18:08:52Z","updated_at":"2015-06-12T19:07:26Z","closed_at":null,"merged_at":null,"merge_commit_sha":"370b4fd22380e9a290df254d1a1f93ea9bf8ff16","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/aspnet/Caching/pulls/70/commits","review_comments_url":"https://api.github.com/repos/aspnet/Caching/pulls/70/comments","review_comment_url":"https://api.github.com/repos/aspnet/Caching/pulls/comments{/number}","comments_url":"https://api.github.com/repos/aspnet/Caching/issues/70/comments","statuses_url":"https://api.github.com/repos/aspnet/Caching/statuses/11c72b309c1e912199d77ee2f1ef24e054768532","head":{"label":"aspnet:kiran/sql-server-cache","ref":"kiran/sql-server-cache","sha":"11c72b309c1e912199d77ee2f1ef24e054768532","user":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"repo":{"id":22390590,"name":"Caching","full_name":"aspnet/Caching","owner":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aspnet/Caching","description":"Libraries for caching, including in-memory caching, distributed caching, and ASP.NET session caching.","fork":false,"url":"https://api.github.com/repos/aspnet/Caching","forks_url":"https://api.github.com/repos/aspnet/Caching/forks","keys_url":"https://api.github.com/repos/aspnet/Caching/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aspnet/Caching/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aspnet/Caching/teams","hooks_url":"https://api.github.com/repos/aspnet/Caching/hooks","issue_events_url":"https://api.github.com/repos/aspnet/Caching/issues/events{/number}","events_url":"https://api.github.com/repos/aspnet/Caching/events","assignees_url":"https://api.github.com/repos/aspnet/Caching/assignees{/user}","branches_url":"https://api.github.com/repos/aspnet/Caching/branches{/branch}","tags_url":"https://api.github.com/repos/aspnet/Caching/tags","blobs_url":"https://api.github.com/repos/aspnet/Caching/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aspnet/Caching/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aspnet/Caching/git/refs{/sha}","trees_url":"https://api.github.com/repos/aspnet/Caching/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aspnet/Caching/statuses/{sha}","languages_url":"https://api.github.com/repos/aspnet/Caching/languages","stargazers_url":"https://api.github.com/repos/aspnet/Caching/stargazers","contributors_url":"https://api.github.com/repos/aspnet/Caching/contributors","subscribers_url":"https://api.github.com/repos/aspnet/Caching/subscribers","subscription_url":"https://api.github.com/repos/aspnet/Caching/subscription","commits_url":"https://api.github.com/repos/aspnet/Caching/commits{/sha}","git_commits_url":"https://api.github.com/repos/aspnet/Caching/git/commits{/sha}","comments_url":"https://api.github.com/repos/aspnet/Caching/comments{/number}","issue_comment_url":"https://api.github.com/repos/aspnet/Caching/issues/comments{/number}","contents_url":"https://api.github.com/repos/aspnet/Caching/contents/{+path}","compare_url":"https://api.github.com/repos/aspnet/Caching/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aspnet/Caching/merges","archive_url":"https://api.github.com/repos/aspnet/Caching/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aspnet/Caching/downloads","issues_url":"https://api.github.com/repos/aspnet/Caching/issues{/number}","pulls_url":"https://api.github.com/repos/aspnet/Caching/pulls{/number}","milestones_url":"https://api.github.com/repos/aspnet/Caching/milestones{/number}","notifications_url":"https://api.github.com/repos/aspnet/Caching/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aspnet/Caching/labels{/name}","releases_url":"https://api.github.com/repos/aspnet/Caching/releases{/id}","created_at":"2014-07-29T18:25:32Z","updated_at":"2015-06-10T21:52:03Z","pushed_at":"2015-06-12T17:48:15Z","git_url":"git://github.com/aspnet/Caching.git","ssh_url":"git@github.com:aspnet/Caching.git","clone_url":"https://github.com/aspnet/Caching.git","svn_url":"https://github.com/aspnet/Caching","homepage":"","size":1549,"stargazers_count":75,"watchers_count":75,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":28,"mirror_url":null,"open_issues_count":12,"forks":28,"open_issues":12,"watchers":75,"default_branch":"dev"}},"base":{"label":"aspnet:dev","ref":"dev","sha":"e14fe9e39a839fbc4c23ee9c861c06d60ddf04a5","user":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"repo":{"id":22390590,"name":"Caching","full_name":"aspnet/Caching","owner":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aspnet/Caching","description":"Libraries for caching, including in-memory caching, distributed caching, and ASP.NET session caching.","fork":false,"url":"https://api.github.com/repos/aspnet/Caching","forks_url":"https://api.github.com/repos/aspnet/Caching/forks","keys_url":"https://api.github.com/repos/aspnet/Caching/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aspnet/Caching/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aspnet/Caching/teams","hooks_url":"https://api.github.com/repos/aspnet/Caching/hooks","issue_events_url":"https://api.github.com/repos/aspnet/Caching/issues/events{/number}","events_url":"https://api.github.com/repos/aspnet/Caching/events","assignees_url":"https://api.github.com/repos/aspnet/Caching/assignees{/user}","branches_url":"https://api.github.com/repos/aspnet/Caching/branches{/branch}","tags_url":"https://api.github.com/repos/aspnet/Caching/tags","blobs_url":"https://api.github.com/repos/aspnet/Caching/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aspnet/Caching/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aspnet/Caching/git/refs{/sha}","trees_url":"https://api.github.com/repos/aspnet/Caching/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aspnet/Caching/statuses/{sha}","languages_url":"https://api.github.com/repos/aspnet/Caching/languages","stargazers_url":"https://api.github.com/repos/aspnet/Caching/stargazers","contributors_url":"https://api.github.com/repos/aspnet/Caching/contributors","subscribers_url":"https://api.github.com/repos/aspnet/Caching/subscribers","subscription_url":"https://api.github.com/repos/aspnet/Caching/subscription","commits_url":"https://api.github.com/repos/aspnet/Caching/commits{/sha}","git_commits_url":"https://api.github.com/repos/aspnet/Caching/git/commits{/sha}","comments_url":"https://api.github.com/repos/aspnet/Caching/comments{/number}","issue_comment_url":"https://api.github.com/repos/aspnet/Caching/issues/comments{/number}","contents_url":"https://api.github.com/repos/aspnet/Caching/contents/{+path}","compare_url":"https://api.github.com/repos/aspnet/Caching/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aspnet/Caching/merges","archive_url":"https://api.github.com/repos/aspnet/Caching/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aspnet/Caching/downloads","issues_url":"https://api.github.com/repos/aspnet/Caching/issues{/number}","pulls_url":"https://api.github.com/repos/aspnet/Caching/pulls{/number}","milestones_url":"https://api.github.com/repos/aspnet/Caching/milestones{/number}","notifications_url":"https://api.github.com/repos/aspnet/Caching/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aspnet/Caching/labels{/name}","releases_url":"https://api.github.com/repos/aspnet/Caching/releases{/id}","created_at":"2014-07-29T18:25:32Z","updated_at":"2015-06-10T21:52:03Z","pushed_at":"2015-06-12T17:48:15Z","git_url":"git://github.com/aspnet/Caching.git","ssh_url":"git@github.com:aspnet/Caching.git","clone_url":"https://github.com/aspnet/Caching.git","svn_url":"https://github.com/aspnet/Caching","homepage":"","size":1549,"stargazers_count":75,"watchers_count":75,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":28,"mirror_url":null,"open_issues_count":12,"forks":28,"open_issues":12,"watchers":75,"default_branch":"dev"}},"_links":{"self":{"href":"https://api.github.com/repos/aspnet/Caching/pulls/70"},"html":{"href":"https://github.com/aspnet/Caching/pull/70"},"issue":{"href":"https://api.github.com/repos/aspnet/Caching/issues/70"},"comments":{"href":"https://api.github.com/repos/aspnet/Caching/issues/70/comments"},"review_comments":{"href":"https://api.github.com/repos/aspnet/Caching/pulls/70/comments"},"review_comment":{"href":"https://api.github.com/repos/aspnet/Caching/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/aspnet/Caching/pulls/70/commits"},"statuses":{"href":"https://api.github.com/repos/aspnet/Caching/statuses/11c72b309c1e912199d77ee2f1ef24e054768532"}}}}