{"action":"created","comment":{"url":"https://api.github.com/repos/dotnet/orleans/pulls/comments/91597933","pull_request_review_id":12107351,"id":91597933,"diff_hunk":"@@ -34,74 +32,142 @@ internal class InterceptedMethodInvokerCache\n         /// </returns>\n         public InterceptedMethodInvoker GetOrCreate(Type implementationType, int interfaceId, IGrainMethodInvoker invoker)\n         {\n-            var implementation = invokers.GetOrAdd(\n-                implementationType,\n-                _ => new ConcurrentDictionary<int, InterceptedMethodInvoker>());\n+            // Get or create the mapping between interfaceId and invoker for the provided type.\n+            Dictionary<int, InterceptedMethodInvoker> invokerMap;\n+            if (!this.invokers.TryGetValue(implementationType, out invokerMap))\n+            {\n+                // Generate an the invoker mapping using the provided invoker.\n+                this.invokers[implementationType] = invokerMap = GetInterfaceToImplementationMap(implementationType, invoker);\n+            }\n \n+            // Attempt to get the invoker for the provided interfaceId.\n             InterceptedMethodInvoker interceptedMethodInvoker;\n-            if (implementation.TryGetValue(interfaceId, out interceptedMethodInvoker))\n+            if (!invokerMap.TryGetValue(interfaceId, out interceptedMethodInvoker))\n             {\n-                return interceptedMethodInvoker;\n+                throw new InvalidOperationException(\n+                    $\"Type {implementationType} does not implement interface with id {interfaceId} ({interfaceId:X}).\");\n             }\n \n-            // Create a mapping between the interface and the implementation.\n-            return implementation.GetOrAdd(\n-                interfaceId,\n-                _ => CreateInterceptedMethodInvoker(implementationType, interfaceId, invoker));\n+            return interceptedMethodInvoker;\n         }\n \n         /// <summary>\n-        /// Returns a new <see cref=\"InterceptedMethodInvoker\"/> for the provided values.\n+        /// Maps the interfaces of the provided <paramref name=\"implementationType\"/>.\n         /// </summary>\n-        /// <param name=\"implementationType\">The grain type.</param>\n-        /// <param name=\"interfaceId\">The interface id.</param>\n-        /// <param name=\"invoker\">The invoker.</param>\n-        /// <returns>A new <see cref=\"InterceptedMethodInvoker\"/> for the provided values.</returns>\n-        private static InterceptedMethodInvoker CreateInterceptedMethodInvoker(\n+        /// <param name=\"implementationType\">The implementation type.</param>\n+        /// <param name=\"invoker\">The grain method invoker.</param>\n+        /// <returns>The mapped interface.</returns>\n+        private static Dictionary<int, InterceptedMethodInvoker> GetInterfaceToImplementationMap(\n             Type implementationType,\n-            int interfaceId,\n             IGrainMethodInvoker invoker)\n         {\n-            // If a grain extension is being invoked, the implementation map must match the methods on the extension\n-            // and not the grain implementation.\n-            var extensionMap = invoker as IGrainExtensionMap;\n-            if (extensionMap != null)\n+            // This method creates a map from interfaceId -> intercepted invoker.\n+            if (implementationType.IsConstructedGenericType) return GetGenericInterfaceInvoker(implementationType, invoker);\n+            var implementationTypeInfo = implementationType.GetTypeInfo();\n+            var interfaces = implementationType.GetInterfaces();\n+\n+            // Create an invoker for every interface on the provided type.\n+            var result = new Dictionary<int, InterceptedMethodInvoker>(interfaces.Length);\n+            foreach (var iface in interfaces)\n             {\n-                IGrainExtension extension;\n-                if (extensionMap.TryGetExtension(interfaceId, out extension))\n+                var methods = GrainInterfaceUtils.GetMethods(iface);\n+\n+                // Map every method on this interface from the definition interface onto the implementation class.\n+                var methodMap = new Dictionary<int, MethodInfo>(methods.Length);\n+                var mapping = default(InterfaceMapping);\n+                foreach (var method in methods)\n                 {\n-                    implementationType = extension.GetType();\n+                    // If this method is not from the expected interface (eg, because it's from a parent interface), then\n+                    // get the mapping for the interface which it does belong to.\n+                    if (mapping.InterfaceType != method.DeclaringType)\n+                    {\n+                        mapping = implementationTypeInfo.GetRuntimeInterfaceMap(method.DeclaringType);\n+                    }\n+\n+                    // Find the index of the interface method and then get the implementation method at that position.\n+                    for (var k = 0; k < mapping.InterfaceMethods.Length; k++)\n+                    {\n+                        if (mapping.InterfaceMethods[k] != method) continue;\n+                        methodMap[GrainInterfaceUtils.ComputeMethodId(method)] = mapping.TargetMethods[k];\n+                        break;\n+                    }\n                 }\n+\n+                // Add the resulting map of methodId -> method to the interface map.\n+                var interfaceId = GrainInterfaceUtils.GetGrainInterfaceId(iface);\n+                result[interfaceId] = new InterceptedMethodInvoker(invoker, methodMap);\n             }\n \n-            var implementationMap = GetInterfaceToImplementationMap(interfaceId, implementationType);\n-            return new InterceptedMethodInvoker(invoker, implementationMap);\n+            return result;\n         }\n \n         /// <summary>\n-        /// Maps the provided <paramref name=\"interfaceId\"/> to the provided <paramref name=\"implementationType\"/>.\n+        /// Maps the interfaces of the provided <paramref name=\"implementationType\"/>.\n         /// </summary>\n-        /// <param name=\"interfaceId\">The interface id.</param>\n-        /// <param name=\"implementationType\">The implementation type.</param>\n+        /// <param name=\"implementationType\">The implementation type, which must be a concrete generic type.</param>\n+        /// <param name=\"invoker\">The grain method invoker.</param>\n         /// <returns>The mapped interface.</returns>\n-        private static Dictionary<int, MethodInfo> GetInterfaceToImplementationMap(\n-            int interfaceId,\n-            Type implementationType)\n+        private static Dictionary<int, InterceptedMethodInvoker> GetGenericInterfaceInvoker(\n+            Type implementationType,\n+            IGrainMethodInvoker invoker)\n         {\n-            var interfaceTypes = GrainInterfaceUtils.GetRemoteInterfaces(implementationType);\n-            \n-            // Get all interface mappings of all interfaces.\n-            var interfaceMapping = implementationType\n-                .GetInterfaces()\n-                .Select(i => implementationType.GetTypeInfo().GetRuntimeInterfaceMap(i))\n-                .SelectMany(map => map.InterfaceMethods\n-                    .Zip(map.TargetMethods, (interfaceMethod, targetMethod) => new { interfaceMethod, targetMethod }))\n-                .ToArray();\n-\n-            // Map the grain interface methods to the implementation methods.\n-            return GrainInterfaceUtils.GetMethods(interfaceTypes[interfaceId])\n-                .ToDictionary(GrainInterfaceUtils.ComputeMethodId,\n-                    m => interfaceMapping.SingleOrDefault(pair => pair.interfaceMethod == m)?.targetMethod);\n+            // This method creates a a map interfaceId -> intercepted invoker.","path":"src/Orleans/Core/InterceptedMethodInvokerCache.cs","position":152,"original_position":152,"commit_id":"fbeff1661a7a85e6b6a2adab771d250fb9005500","original_commit_id":"fbeff1661a7a85e6b6a2adab771d250fb9005500","user":{"login":"jason-bragg","id":10675494,"avatar_url":"https://avatars.githubusercontent.com/u/10675494?v=3","gravatar_id":"","url":"https://api.github.com/users/jason-bragg","html_url":"https://github.com/jason-bragg","followers_url":"https://api.github.com/users/jason-bragg/followers","following_url":"https://api.github.com/users/jason-bragg/following{/other_user}","gists_url":"https://api.github.com/users/jason-bragg/gists{/gist_id}","starred_url":"https://api.github.com/users/jason-bragg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jason-bragg/subscriptions","organizations_url":"https://api.github.com/users/jason-bragg/orgs","repos_url":"https://api.github.com/users/jason-bragg/repos","events_url":"https://api.github.com/users/jason-bragg/events{/privacy}","received_events_url":"https://api.github.com/users/jason-bragg/received_events","type":"User","site_admin":false},"body":"a a","created_at":"2016-12-08T20:16:45Z","updated_at":"2016-12-08T20:36:45Z","html_url":"https://github.com/dotnet/orleans/pull/2502#discussion_r91597933","pull_request_url":"https://api.github.com/repos/dotnet/orleans/pulls/2502","_links":{"self":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/comments/91597933"},"html":{"href":"https://github.com/dotnet/orleans/pull/2502#discussion_r91597933"},"pull_request":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/2502"}}},"pull_request":{"url":"https://api.github.com/repos/dotnet/orleans/pulls/2502","id":97060225,"html_url":"https://github.com/dotnet/orleans/pull/2502","diff_url":"https://github.com/dotnet/orleans/pull/2502.diff","patch_url":"https://github.com/dotnet/orleans/pull/2502.patch","issue_url":"https://api.github.com/repos/dotnet/orleans/issues/2502","number":2502,"state":"open","locked":false,"title":"Fix #2358: Invoke interceptor broken for generic grains","user":{"login":"ReubenBond","id":203839,"avatar_url":"https://avatars.githubusercontent.com/u/203839?v=3","gravatar_id":"","url":"https://api.github.com/users/ReubenBond","html_url":"https://github.com/ReubenBond","followers_url":"https://api.github.com/users/ReubenBond/followers","following_url":"https://api.github.com/users/ReubenBond/following{/other_user}","gists_url":"https://api.github.com/users/ReubenBond/gists{/gist_id}","starred_url":"https://api.github.com/users/ReubenBond/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ReubenBond/subscriptions","organizations_url":"https://api.github.com/users/ReubenBond/orgs","repos_url":"https://api.github.com/users/ReubenBond/repos","events_url":"https://api.github.com/users/ReubenBond/events{/privacy}","received_events_url":"https://api.github.com/users/ReubenBond/received_events","type":"User","site_admin":false},"body":"Fixes #2358. \r\n\r\n* Previously there were two methods used to get an id for a grain interface, with slightly different behaviour. That seemed to be erroneous and so `ComputeInterfaceId` was removed in favour of `GetGrainInterfaceId`.\r\n* `GetGrainInterfaceId` must take into account whether or not the grain is a constructed generic. A constructed generic cannot be a grain interface as specified in code. Therefore its generic type definition is used instead.\r\n* The `Type.GetRuntimeInterfaceMap(Type)` method does not take into account inherited interfaces, only the current interface. This would cause interceptors to receive `null` instead of the implementation's `MethodInfo`.\r\n* Mapping between methods on a generic interface and their counterparts on a concrete class is not entirely obvious, but it is now implemented (see `GetGenericInterfaceInvoker`). A different method was used in order to keep differences clear.\r\n* Added an additional test to ensure this case is covered.\r\n* Changed tests to BVTs since they're quick & fairly important.\r\n\r\nThank you for reporting, @dancvogel, and thanks for the prod, @richorama 😄 ","created_at":"2016-12-08T05:11:38Z","updated_at":"2016-12-08T20:36:45Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c3a24e998c55f82ad3e10c303d23e2cd60880891","assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/dotnet/orleans/milestones/14","html_url":"https://github.com/dotnet/orleans/milestone/14","labels_url":"https://api.github.com/repos/dotnet/orleans/milestones/14/labels","id":2017852,"number":14,"title":"1.4.0","description":"","creator":{"login":"sergeybykov","id":8248806,"avatar_url":"https://avatars.githubusercontent.com/u/8248806?v=3","gravatar_id":"","url":"https://api.github.com/users/sergeybykov","html_url":"https://github.com/sergeybykov","followers_url":"https://api.github.com/users/sergeybykov/followers","following_url":"https://api.github.com/users/sergeybykov/following{/other_user}","gists_url":"https://api.github.com/users/sergeybykov/gists{/gist_id}","starred_url":"https://api.github.com/users/sergeybykov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sergeybykov/subscriptions","organizations_url":"https://api.github.com/users/sergeybykov/orgs","repos_url":"https://api.github.com/users/sergeybykov/repos","events_url":"https://api.github.com/users/sergeybykov/events{/privacy}","received_events_url":"https://api.github.com/users/sergeybykov/received_events","type":"User","site_admin":false},"open_issues":31,"closed_issues":28,"state":"open","created_at":"2016-09-20T23:16:17Z","updated_at":"2016-12-08T05:11:38Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/dotnet/orleans/pulls/2502/commits","review_comments_url":"https://api.github.com/repos/dotnet/orleans/pulls/2502/comments","review_comment_url":"https://api.github.com/repos/dotnet/orleans/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dotnet/orleans/issues/2502/comments","statuses_url":"https://api.github.com/repos/dotnet/orleans/statuses/fbeff1661a7a85e6b6a2adab771d250fb9005500","head":{"label":"ReubenBond:fix-invoker-generic-grains","ref":"fix-invoker-generic-grains","sha":"fbeff1661a7a85e6b6a2adab771d250fb9005500","user":{"login":"ReubenBond","id":203839,"avatar_url":"https://avatars.githubusercontent.com/u/203839?v=3","gravatar_id":"","url":"https://api.github.com/users/ReubenBond","html_url":"https://github.com/ReubenBond","followers_url":"https://api.github.com/users/ReubenBond/followers","following_url":"https://api.github.com/users/ReubenBond/following{/other_user}","gists_url":"https://api.github.com/users/ReubenBond/gists{/gist_id}","starred_url":"https://api.github.com/users/ReubenBond/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ReubenBond/subscriptions","organizations_url":"https://api.github.com/users/ReubenBond/orgs","repos_url":"https://api.github.com/users/ReubenBond/repos","events_url":"https://api.github.com/users/ReubenBond/events{/privacy}","received_events_url":"https://api.github.com/users/ReubenBond/received_events","type":"User","site_admin":false},"repo":{"id":30130910,"name":"orleans","full_name":"ReubenBond/orleans","owner":{"login":"ReubenBond","id":203839,"avatar_url":"https://avatars.githubusercontent.com/u/203839?v=3","gravatar_id":"","url":"https://api.github.com/users/ReubenBond","html_url":"https://github.com/ReubenBond","followers_url":"https://api.github.com/users/ReubenBond/followers","following_url":"https://api.github.com/users/ReubenBond/following{/other_user}","gists_url":"https://api.github.com/users/ReubenBond/gists{/gist_id}","starred_url":"https://api.github.com/users/ReubenBond/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ReubenBond/subscriptions","organizations_url":"https://api.github.com/users/ReubenBond/orgs","repos_url":"https://api.github.com/users/ReubenBond/repos","events_url":"https://api.github.com/users/ReubenBond/events{/privacy}","received_events_url":"https://api.github.com/users/ReubenBond/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ReubenBond/orleans","description":"Repo for Orleans - Distributed Actor Model","fork":true,"url":"https://api.github.com/repos/ReubenBond/orleans","forks_url":"https://api.github.com/repos/ReubenBond/orleans/forks","keys_url":"https://api.github.com/repos/ReubenBond/orleans/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ReubenBond/orleans/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ReubenBond/orleans/teams","hooks_url":"https://api.github.com/repos/ReubenBond/orleans/hooks","issue_events_url":"https://api.github.com/repos/ReubenBond/orleans/issues/events{/number}","events_url":"https://api.github.com/repos/ReubenBond/orleans/events","assignees_url":"https://api.github.com/repos/ReubenBond/orleans/assignees{/user}","branches_url":"https://api.github.com/repos/ReubenBond/orleans/branches{/branch}","tags_url":"https://api.github.com/repos/ReubenBond/orleans/tags","blobs_url":"https://api.github.com/repos/ReubenBond/orleans/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ReubenBond/orleans/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ReubenBond/orleans/git/refs{/sha}","trees_url":"https://api.github.com/repos/ReubenBond/orleans/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ReubenBond/orleans/statuses/{sha}","languages_url":"https://api.github.com/repos/ReubenBond/orleans/languages","stargazers_url":"https://api.github.com/repos/ReubenBond/orleans/stargazers","contributors_url":"https://api.github.com/repos/ReubenBond/orleans/contributors","subscribers_url":"https://api.github.com/repos/ReubenBond/orleans/subscribers","subscription_url":"https://api.github.com/repos/ReubenBond/orleans/subscription","commits_url":"https://api.github.com/repos/ReubenBond/orleans/commits{/sha}","git_commits_url":"https://api.github.com/repos/ReubenBond/orleans/git/commits{/sha}","comments_url":"https://api.github.com/repos/ReubenBond/orleans/comments{/number}","issue_comment_url":"https://api.github.com/repos/ReubenBond/orleans/issues/comments{/number}","contents_url":"https://api.github.com/repos/ReubenBond/orleans/contents/{+path}","compare_url":"https://api.github.com/repos/ReubenBond/orleans/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ReubenBond/orleans/merges","archive_url":"https://api.github.com/repos/ReubenBond/orleans/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ReubenBond/orleans/downloads","issues_url":"https://api.github.com/repos/ReubenBond/orleans/issues{/number}","pulls_url":"https://api.github.com/repos/ReubenBond/orleans/pulls{/number}","milestones_url":"https://api.github.com/repos/ReubenBond/orleans/milestones{/number}","notifications_url":"https://api.github.com/repos/ReubenBond/orleans/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ReubenBond/orleans/labels{/name}","releases_url":"https://api.github.com/repos/ReubenBond/orleans/releases{/id}","deployments_url":"https://api.github.com/repos/ReubenBond/orleans/deployments","created_at":"2015-02-01T00:06:08Z","updated_at":"2016-10-31T01:45:02Z","pushed_at":"2016-12-08T06:01:18Z","git_url":"git://github.com/ReubenBond/orleans.git","ssh_url":"git@github.com:ReubenBond/orleans.git","clone_url":"https://github.com/ReubenBond/orleans.git","svn_url":"https://github.com/ReubenBond/orleans","homepage":"","size":29746,"stargazers_count":0,"watchers_count":0,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"dotnet:master","ref":"master","sha":"8fec273b042df6f92878175b0774d6d8f736e7dd","user":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"repo":{"id":28232663,"name":"orleans","full_name":"dotnet/orleans","owner":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dotnet/orleans","description":"Orleans - Distributed Virtual Actor Model","fork":false,"url":"https://api.github.com/repos/dotnet/orleans","forks_url":"https://api.github.com/repos/dotnet/orleans/forks","keys_url":"https://api.github.com/repos/dotnet/orleans/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dotnet/orleans/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dotnet/orleans/teams","hooks_url":"https://api.github.com/repos/dotnet/orleans/hooks","issue_events_url":"https://api.github.com/repos/dotnet/orleans/issues/events{/number}","events_url":"https://api.github.com/repos/dotnet/orleans/events","assignees_url":"https://api.github.com/repos/dotnet/orleans/assignees{/user}","branches_url":"https://api.github.com/repos/dotnet/orleans/branches{/branch}","tags_url":"https://api.github.com/repos/dotnet/orleans/tags","blobs_url":"https://api.github.com/repos/dotnet/orleans/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dotnet/orleans/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dotnet/orleans/git/refs{/sha}","trees_url":"https://api.github.com/repos/dotnet/orleans/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dotnet/orleans/statuses/{sha}","languages_url":"https://api.github.com/repos/dotnet/orleans/languages","stargazers_url":"https://api.github.com/repos/dotnet/orleans/stargazers","contributors_url":"https://api.github.com/repos/dotnet/orleans/contributors","subscribers_url":"https://api.github.com/repos/dotnet/orleans/subscribers","subscription_url":"https://api.github.com/repos/dotnet/orleans/subscription","commits_url":"https://api.github.com/repos/dotnet/orleans/commits{/sha}","git_commits_url":"https://api.github.com/repos/dotnet/orleans/git/commits{/sha}","comments_url":"https://api.github.com/repos/dotnet/orleans/comments{/number}","issue_comment_url":"https://api.github.com/repos/dotnet/orleans/issues/comments{/number}","contents_url":"https://api.github.com/repos/dotnet/orleans/contents/{+path}","compare_url":"https://api.github.com/repos/dotnet/orleans/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dotnet/orleans/merges","archive_url":"https://api.github.com/repos/dotnet/orleans/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dotnet/orleans/downloads","issues_url":"https://api.github.com/repos/dotnet/orleans/issues{/number}","pulls_url":"https://api.github.com/repos/dotnet/orleans/pulls{/number}","milestones_url":"https://api.github.com/repos/dotnet/orleans/milestones{/number}","notifications_url":"https://api.github.com/repos/dotnet/orleans/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dotnet/orleans/labels{/name}","releases_url":"https://api.github.com/repos/dotnet/orleans/releases{/id}","deployments_url":"https://api.github.com/repos/dotnet/orleans/deployments","created_at":"2014-12-19T14:32:50Z","updated_at":"2016-12-08T16:56:12Z","pushed_at":"2016-12-08T06:01:18Z","git_url":"git://github.com/dotnet/orleans.git","ssh_url":"git@github.com:dotnet/orleans.git","clone_url":"https://github.com/dotnet/orleans.git","svn_url":"https://github.com/dotnet/orleans","homepage":"http://dotnet.github.io/orleans","size":52904,"stargazers_count":2420,"watchers_count":2420,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":602,"mirror_url":null,"open_issues_count":253,"forks":602,"open_issues":253,"watchers":2420,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/2502"},"html":{"href":"https://github.com/dotnet/orleans/pull/2502"},"issue":{"href":"https://api.github.com/repos/dotnet/orleans/issues/2502"},"comments":{"href":"https://api.github.com/repos/dotnet/orleans/issues/2502/comments"},"review_comments":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/2502/comments"},"review_comment":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dotnet/orleans/pulls/2502/commits"},"statuses":{"href":"https://api.github.com/repos/dotnet/orleans/statuses/fbeff1661a7a85e6b6a2adab771d250fb9005500"}}}}
{"actor":{"display_login":"jason-bragg"}}