From 0201776d295cbc954c8d7ed1298a3988d6a2b33b Mon Sep 17 00:00:00 2001
From: Jan Tattermusch <jtattermusch@google.com>
Date: Wed, 23 Nov 2016 15:39:56 +0100
Subject: [PATCH] add C# server reflection

---
 src/csharp/Grpc.Core.Tests/SanityTest.cs      |    3 +-
 src/csharp/Grpc.Dotnet.sln                    |   14 +-
 src/csharp/Grpc.Reflection.Tests/.gitignore   |    2 +
 .../Grpc.Reflection.Tests.csproj              |   94 +
 .../Grpc.Reflection.Tests.project.json        |    8 +
 .../Grpc.Reflection.Tests.xproj               |   18 +
 src/csharp/Grpc.Reflection.Tests/NUnitMain.cs |   59 +
 .../Properties/AssemblyInfo.cs                |   44 +
 .../ReflectionClientServerTest.cs             |  154 ++
 .../SymbolRegistryTest.cs                     |   63 +
 .../Grpc.Reflection.Tests/packages.config     |    7 +
 src/csharp/Grpc.Reflection.Tests/project.json |   65 +
 src/csharp/Grpc.Reflection/.gitignore         |    2 +
 .../Grpc.Reflection/Grpc.Reflection.csproj    |   84 +
 .../Grpc.Reflection/Grpc.Reflection.nuspec    |   28 +
 .../Grpc.Reflection.project.json              |    8 +
 .../Grpc.Reflection/Grpc.Reflection.xproj     |   18 +
 .../Properties/AssemblyInfo.cs                |   44 +
 src/csharp/Grpc.Reflection/Reflection.cs      | 1556 +++++++++++++++++
 src/csharp/Grpc.Reflection/ReflectionGrpc.cs  |  132 ++
 .../Grpc.Reflection/ReflectionServiceImpl.cs  |  173 ++
 src/csharp/Grpc.Reflection/Settings.StyleCop  |   10 +
 src/csharp/Grpc.Reflection/SymbolRegistry.cs  |  160 ++
 src/csharp/Grpc.Reflection/packages.config    |    5 +
 src/csharp/Grpc.Reflection/project.json       |   41 +
 src/csharp/Grpc.sln                           |   16 +
 src/csharp/generate_proto_csharp.sh           |    4 +
 src/csharp/tests.json                         |    4 +
 .../project.json.template                     |   26 +
 .../Grpc.Reflection/project.json.template     |   43 +
 30 files changed, 2883 insertions(+), 2 deletions(-)
 create mode 100644 src/csharp/Grpc.Reflection.Tests/.gitignore
 create mode 100644 src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.csproj
 create mode 100644 src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.project.json
 create mode 100644 src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.xproj
 create mode 100644 src/csharp/Grpc.Reflection.Tests/NUnitMain.cs
 create mode 100644 src/csharp/Grpc.Reflection.Tests/Properties/AssemblyInfo.cs
 create mode 100644 src/csharp/Grpc.Reflection.Tests/ReflectionClientServerTest.cs
 create mode 100644 src/csharp/Grpc.Reflection.Tests/SymbolRegistryTest.cs
 create mode 100644 src/csharp/Grpc.Reflection.Tests/packages.config
 create mode 100644 src/csharp/Grpc.Reflection.Tests/project.json
 create mode 100644 src/csharp/Grpc.Reflection/.gitignore
 create mode 100644 src/csharp/Grpc.Reflection/Grpc.Reflection.csproj
 create mode 100644 src/csharp/Grpc.Reflection/Grpc.Reflection.nuspec
 create mode 100644 src/csharp/Grpc.Reflection/Grpc.Reflection.project.json
 create mode 100644 src/csharp/Grpc.Reflection/Grpc.Reflection.xproj
 create mode 100644 src/csharp/Grpc.Reflection/Properties/AssemblyInfo.cs
 create mode 100644 src/csharp/Grpc.Reflection/Reflection.cs
 create mode 100644 src/csharp/Grpc.Reflection/ReflectionGrpc.cs
 create mode 100644 src/csharp/Grpc.Reflection/ReflectionServiceImpl.cs
 create mode 100644 src/csharp/Grpc.Reflection/Settings.StyleCop
 create mode 100644 src/csharp/Grpc.Reflection/SymbolRegistry.cs
 create mode 100644 src/csharp/Grpc.Reflection/packages.config
 create mode 100644 src/csharp/Grpc.Reflection/project.json
 create mode 100644 templates/src/csharp/Grpc.Reflection.Tests/project.json.template
 create mode 100644 templates/src/csharp/Grpc.Reflection/project.json.template

diff --git a/src/csharp/Grpc.Core.Tests/SanityTest.cs b/src/csharp/Grpc.Core.Tests/SanityTest.cs
index f1eb13dffc..1c28277df5 100644
--- a/src/csharp/Grpc.Core.Tests/SanityTest.cs
+++ b/src/csharp/Grpc.Core.Tests/SanityTest.cs
@@ -115,7 +115,8 @@ namespace Grpc.Core.Tests
             var otherAssemblies = new[] {
                 "Grpc.Examples.Tests",
                 "Grpc.HealthCheck.Tests",
-                "Grpc.IntegrationTesting"
+                "Grpc.IntegrationTesting",
+                "Grpc.Reflection.Tests",
             };
             foreach (var assemblyName in otherAssemblies)
             {
diff --git a/src/csharp/Grpc.Dotnet.sln b/src/csharp/Grpc.Dotnet.sln
index 98b3cd54ab..824c6822f7 100644
--- a/src/csharp/Grpc.Dotnet.sln
+++ b/src/csharp/Grpc.Dotnet.sln
@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 14
-VisualStudioVersion = 14.0.25123.0
+VisualStudioVersion = 14.0.25420.1
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Grpc.Core", "Grpc.Core\Grpc.Core.xproj", "{DC9908B6-F291-4FC8-A46D-2EA2551790EC}"
 EndProject
@@ -31,6 +31,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Grpc.IntegrationTesting.Ser
 EndProject
 Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Grpc.IntegrationTesting.StressClient", "Grpc.IntegrationTesting.StressClient\Grpc.IntegrationTesting.StressClient.xproj", "{0EBC910B-8867-4D3E-8686-91F34183D839}"
 EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Grpc.Reflection", "Grpc.Reflection\Grpc.Reflection.xproj", "{2B372155-80BA-4CF9-82D6-4B938E8EC3A0}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Grpc.Reflection.Tests", "Grpc.Reflection.Tests\Grpc.Reflection.Tests.xproj", "{FE90181D-A4B3-4A5C-8490-F07561E18E3B}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -93,6 +97,14 @@ Global
 		{0EBC910B-8867-4D3E-8686-91F34183D839}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{0EBC910B-8867-4D3E-8686-91F34183D839}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{0EBC910B-8867-4D3E-8686-91F34183D839}.Release|Any CPU.Build.0 = Release|Any CPU
+		{2B372155-80BA-4CF9-82D6-4B938E8EC3A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{2B372155-80BA-4CF9-82D6-4B938E8EC3A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2B372155-80BA-4CF9-82D6-4B938E8EC3A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{2B372155-80BA-4CF9-82D6-4B938E8EC3A0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FE90181D-A4B3-4A5C-8490-F07561E18E3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FE90181D-A4B3-4A5C-8490-F07561E18E3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FE90181D-A4B3-4A5C-8490-F07561E18E3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FE90181D-A4B3-4A5C-8490-F07561E18E3B}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/src/csharp/Grpc.Reflection.Tests/.gitignore b/src/csharp/Grpc.Reflection.Tests/.gitignore
new file mode 100644
index 0000000000..1746e3269e
--- /dev/null
+++ b/src/csharp/Grpc.Reflection.Tests/.gitignore
@@ -0,0 +1,2 @@
+bin
+obj
diff --git a/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.csproj b/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.csproj
new file mode 100644
index 0000000000..cebcf59ce8
--- /dev/null
+++ b/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.csproj
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{B88F91D6-436D-4C78-8B99-47800FA8DE03}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Grpc.Reflection.Tests</RootNamespace>
+    <AssemblyName>Grpc.Reflection.Tests</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\ReleaseSigned</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <SignAssembly>True</SignAssembly>
+    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+    <Reference Include="nunit.framework">
+      <HintPath>..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll</HintPath>
+    </Reference>
+    <Reference Include="nunitlite">
+      <HintPath>..\packages\NUnitLite.3.2.0\lib\net45\nunitlite.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Protobuf">
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Interactive.Async">
+      <HintPath>..\packages\System.Interactive.Async.3.1.1\lib\net45\System.Interactive.Async.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\Grpc.Core\Version.cs">
+      <Link>Version.cs</Link>
+    </Compile>
+    <Compile Include="SymbolRegistryTest.cs" />
+    <Compile Include="ReflectionClientServerTest.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="NUnitMain.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Grpc.Core\Grpc.Core.csproj">
+      <Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
+      <Name>Grpc.Core</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Grpc.Reflection\Grpc.Reflection.csproj">
+      <Project>{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}</Project>
+      <Name>Grpc.Reflection</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Grpc.Reflection.Tests.project.json" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.project.json b/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.project.json
new file mode 100644
index 0000000000..c2f5bcb163
--- /dev/null
+++ b/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.project.json
@@ -0,0 +1,8 @@
+{
+  "frameworks": {
+    "net45": { }
+  },
+  "runtimes": {
+    "win": { }
+  }
+}
diff --git a/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.xproj b/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.xproj
new file mode 100644
index 0000000000..4a3100853d
--- /dev/null
+++ b/src/csharp/Grpc.Reflection.Tests/Grpc.Reflection.Tests.xproj
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0.25123" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25123</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+  </PropertyGroup>
+  <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>fe90181d-a4b3-4a5c-8490-f07561e18e3b</ProjectGuid>
+    <RootNamespace>Grpc.Reflection.Tests</RootNamespace>
+    <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
+    <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
+  </PropertyGroup>
+  <PropertyGroup>
+    <SchemaVersion>2.0</SchemaVersion>
+  </PropertyGroup>
+  <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
+</Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Reflection.Tests/NUnitMain.cs b/src/csharp/Grpc.Reflection.Tests/NUnitMain.cs
new file mode 100644
index 0000000000..a60d7b03eb
--- /dev/null
+++ b/src/csharp/Grpc.Reflection.Tests/NUnitMain.cs
@@ -0,0 +1,59 @@
+#region Copyright notice and license
+
+// Copyright 2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Reflection;
+using Grpc.Core;
+using Grpc.Core.Logging;
+using NUnit.Common;
+using NUnitLite;
+
+namespace Grpc.Reflection.Tests
+{
+    /// <summary>
+    /// Provides entry point for NUnitLite
+    /// </summary>
+    public class NUnitMain
+    {
+        public static int Main(string[] args)
+        {
+            // Make logger immune to NUnit capturing stdout and stderr to workaround https://github.com/nunit/nunit/issues/1406.
+            GrpcEnvironment.SetLogger(new TextWriterLogger(Console.Error));
+#if NETCOREAPP1_0
+            return new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args, new ExtendedTextWrapper(Console.Out), Console.In);
+#else
+            return new AutoRun().Execute(args);
+#endif
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Reflection.Tests/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Reflection.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..d29054a4d1
--- /dev/null
+++ b/src/csharp/Grpc.Reflection.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,44 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+[assembly: AssemblyTitle("Grpc.Reflection.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Google Inc.  All rights reserved.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
diff --git a/src/csharp/Grpc.Reflection.Tests/ReflectionClientServerTest.cs b/src/csharp/Grpc.Reflection.Tests/ReflectionClientServerTest.cs
new file mode 100644
index 0000000000..1d0845e276
--- /dev/null
+++ b/src/csharp/Grpc.Reflection.Tests/ReflectionClientServerTest.cs
@@ -0,0 +1,154 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Grpc.Core;
+using Grpc.Reflection;
+using Grpc.Reflection.V1Alpha;
+using NUnit.Framework;
+
+namespace Grpc.Reflection.Tests
+{
+    /// <summary>
+    /// Reflection client talks to reflection server.
+    /// </summary>
+    public class ReflectionClientServerTest
+    {
+        const string Host = "localhost";
+        Server server;
+        Channel channel;
+        ServerReflection.ServerReflectionClient client;
+        ReflectionServiceImpl serviceImpl;
+
+        [TestFixtureSetUp]
+        public void Init()
+        {
+            serviceImpl = new ReflectionServiceImpl(ServerReflection.Descriptor);
+
+            server = new Server
+            {
+                Services = { ServerReflection.BindService(serviceImpl) },
+                Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
+            };
+            server.Start();
+            channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure);
+
+            client = new ServerReflection.ServerReflectionClient(channel);
+        }
+
+        [TestFixtureTearDown]
+        public void Cleanup()
+        {
+            channel.ShutdownAsync().Wait();
+            server.ShutdownAsync().Wait();
+        }
+
+        [Test]
+        public async Task FileByFilename_NotFound()
+        {
+            var response = await SingleRequestAsync(new ServerReflectionRequest
+            {
+                FileByFilename = "somepackage/nonexistent.proto"
+            });
+            Assert.AreEqual((int)StatusCode.NotFound, response.ErrorResponse.ErrorCode);
+        }
+
+        [Test]
+        public async Task FileByFilename()
+        {
+            var response = await SingleRequestAsync(new ServerReflectionRequest
+            {
+                FileByFilename = "grpc/reflection/v1alpha/reflection.proto"
+            });
+            Assert.AreEqual(1, response.FileDescriptorResponse.FileDescriptorProto.Count);
+            Assert.AreEqual(ReflectionReflection.Descriptor.SerializedData, response.FileDescriptorResponse.FileDescriptorProto[0]);
+        }
+
+        [Test]
+        public async Task FileContainingSymbol()
+        {
+            var response = await SingleRequestAsync(new ServerReflectionRequest
+            {
+                FileContainingSymbol = "grpc.reflection.v1alpha.ServerReflection"
+            });
+            Assert.AreEqual(1, response.FileDescriptorResponse.FileDescriptorProto.Count);
+            Assert.AreEqual(ReflectionReflection.Descriptor.SerializedData, response.FileDescriptorResponse.FileDescriptorProto[0]);
+        }
+
+        [Test]
+        public async Task FileContainingSymbol_NotFound()
+        {
+            var response = await SingleRequestAsync(new ServerReflectionRequest
+            {
+                FileContainingSymbol = "somepackage.Nonexistent"
+            });
+            Assert.AreEqual((int)StatusCode.NotFound, response.ErrorResponse.ErrorCode);
+        }
+
+        [Test]
+        public async Task ListServices()
+        {
+            var response = await SingleRequestAsync(new ServerReflectionRequest
+            {
+                ListServices = ""
+            });
+            Assert.AreEqual(1, response.ListServicesResponse.Service.Count);
+            Assert.AreEqual(ServerReflection.Descriptor.FullName, response.ListServicesResponse.Service[0].Name);
+        }
+
+        [Test]
+        public async Task FileContainingExtension()
+        {
+            var response = await SingleRequestAsync(new ServerReflectionRequest
+            {
+                FileContainingExtension = new ExtensionRequest()
+            });
+            Assert.AreEqual((int)StatusCode.Unimplemented, response.ErrorResponse.ErrorCode);
+        }
+
+        private async Task<ServerReflectionResponse> SingleRequestAsync(ServerReflectionRequest request)
+        {
+            var call = client.ServerReflectionInfo();
+            await call.RequestStream.WriteAsync(request);
+            Assert.IsTrue(await call.ResponseStream.MoveNext());
+
+            var response = call.ResponseStream.Current;
+            await call.RequestStream.CompleteAsync();
+            Assert.IsFalse(await call.ResponseStream.MoveNext());
+            return response;
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Reflection.Tests/SymbolRegistryTest.cs b/src/csharp/Grpc.Reflection.Tests/SymbolRegistryTest.cs
new file mode 100644
index 0000000000..68ee6dc10d
--- /dev/null
+++ b/src/csharp/Grpc.Reflection.Tests/SymbolRegistryTest.cs
@@ -0,0 +1,63 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Grpc.Reflection;
+using Grpc.Reflection.V1Alpha;
+using NUnit.Framework;
+
+
+namespace Grpc.Reflection.Tests
+{
+    /// <summary>
+    /// Tests for ReflectionServiceImpl
+    /// </summary>
+    public class SymbolRegistryTest
+    {
+        SymbolRegistry registry = SymbolRegistry.FromFiles(new[] { ReflectionReflection.Descriptor, Google.Protobuf.WellKnownTypes.Duration.Descriptor.File });
+
+        [Test]
+        public void FileByName()
+        {
+            Assert.AreSame(Google.Protobuf.WellKnownTypes.Duration.Descriptor.File, registry.FileByName("google/protobuf/duration.proto"));
+            Assert.IsNull(registry.FileByName("somepackage/nonexistent.proto"));
+        }
+
+        [Test]
+        public void FileContainingSymbol()
+        {
+            Assert.AreSame(Google.Protobuf.WellKnownTypes.Duration.Descriptor.File, registry.FileContainingSymbol("google.protobuf.Duration"));
+            Assert.AreSame(ReflectionReflection.Descriptor, registry.FileContainingSymbol("grpc.reflection.v1alpha.ServerReflection.ServerReflectionInfo"));  // method
+            Assert.AreSame(ReflectionReflection.Descriptor, registry.FileContainingSymbol("grpc.reflection.v1alpha.ServerReflection"));  // service
+            Assert.AreSame(ReflectionReflection.Descriptor, registry.FileContainingSymbol("grpc.reflection.v1alpha.ServerReflectionRequest"));  // message
+            Assert.IsNull(registry.FileContainingSymbol("somepackage.Nonexistent"));
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Reflection.Tests/packages.config b/src/csharp/Grpc.Reflection.Tests/packages.config
new file mode 100644
index 0000000000..0fed4dbd41
--- /dev/null
+++ b/src/csharp/Grpc.Reflection.Tests/packages.config
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
+  <package id="NUnit" version="3.2.0" targetFramework="net45" />
+  <package id="NUnitLite" version="3.2.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.1.1" targetFramework="net45" />
+</packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Reflection.Tests/project.json b/src/csharp/Grpc.Reflection.Tests/project.json
new file mode 100644
index 0000000000..61d3b7e47b
--- /dev/null
+++ b/src/csharp/Grpc.Reflection.Tests/project.json
@@ -0,0 +1,65 @@
+{
+  "buildOptions": {
+    "emitEntryPoint": true
+  },
+  "configurations": {
+    "Debug": {
+      "buildOptions": {
+        "define": [ "SIGNED" ],
+        "keyFile": "../keys/Grpc.snk",
+        "xmlDoc": true,
+        "compile": {
+          "includeFiles": [ "../Grpc.Core/Version.cs" ]
+        },
+        "copyToOutput": {
+          "mappings": {
+            "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Debug/grpc_csharp_ext.dll",
+            "grpc_csharp_ext.x86.dll": "../../../vsprojects/Debug/grpc_csharp_ext.dll",
+            "libgrpc_csharp_ext.x64.so": "../../../libs/dbg/libgrpc_csharp_ext.so",
+            "libgrpc_csharp_ext.x64.dylib": "../../../libs/dbg/libgrpc_csharp_ext.dylib"
+          }
+        }
+      }
+    },
+    "Release": {
+      "buildOptions": {
+        "define": [ "SIGNED" ],
+        "keyFile": "../keys/Grpc.snk",
+        "xmlDoc": true,
+        "compile": {
+          "includeFiles": [ "../Grpc.Core/Version.cs" ]
+        },
+        "copyToOutput": {
+          "mappings": {
+            "grpc_csharp_ext.x64.dll": "../../../vsprojects/x64/Release/grpc_csharp_ext.dll",
+            "grpc_csharp_ext.x86.dll": "../../../vsprojects/Release/grpc_csharp_ext.dll",
+            "libgrpc_csharp_ext.x64.so": "../../../libs/opt/libgrpc_csharp_ext.so",
+            "libgrpc_csharp_ext.x64.dylib": "../../../libs/opt/libgrpc_csharp_ext.dylib"
+          }
+        }
+      }
+    }
+  },
+
+  "dependencies": {
+    "Grpc.Reflection": {
+      "target": "project"
+    },
+    "NUnit": "3.2.0",
+    "NUnitLite": "3.2.0-*"
+  },
+  "frameworks": {
+    "net45": { },
+    "netcoreapp1.0": {
+      "imports": [
+        "portable-net45"
+      ],
+      "dependencies": {
+        "Microsoft.NETCore.App": {
+          "type": "platform",
+          "version": "1.0.0"
+        }
+      }
+    }
+  }
+}
diff --git a/src/csharp/Grpc.Reflection/.gitignore b/src/csharp/Grpc.Reflection/.gitignore
new file mode 100644
index 0000000000..1746e3269e
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/.gitignore
@@ -0,0 +1,2 @@
+bin
+obj
diff --git a/src/csharp/Grpc.Reflection/Grpc.Reflection.csproj b/src/csharp/Grpc.Reflection/Grpc.Reflection.csproj
new file mode 100644
index 0000000000..06559c15f0
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/Grpc.Reflection.csproj
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Grpc.Reflection</RootNamespace>
+    <AssemblyName>Grpc.Reflection</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <DocumentationFile>bin\$(Configuration)\Grpc.Reflection.Xml</DocumentationFile>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\ReleaseSigned</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <SignAssembly>True</SignAssembly>
+    <AssemblyOriginatorKeyFile>..\keys\Grpc.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+    <Reference Include="Google.Protobuf">
+      <HintPath>..\packages\Google.Protobuf.3.0.0\lib\net45\Google.Protobuf.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Interactive.Async">
+      <HintPath>..\packages\System.Interactive.Async.3.1.1\lib\net45\System.Interactive.Async.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\Grpc.Core\Version.cs">
+      <Link>Version.cs</Link>
+    </Compile>
+    <Compile Include="SymbolRegistry.cs" />
+    <Compile Include="ReflectionServiceImpl.cs" />
+    <Compile Include="Reflection.cs" />
+    <Compile Include="ReflectionGrpc.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Grpc.Reflection.nuspec" />
+    <None Include="Grpc.Reflection.project.json" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Grpc.Core\Grpc.Core.csproj">
+      <Project>{ccc4440e-49f7-4790-b0af-feabb0837ae7}</Project>
+      <Name>Grpc.Core</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Reflection/Grpc.Reflection.nuspec b/src/csharp/Grpc.Reflection/Grpc.Reflection.nuspec
new file mode 100644
index 0000000000..c07fa96b1d
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/Grpc.Reflection.nuspec
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<package>
+  <metadata>
+    <id>Grpc.Reflection</id>
+    <title>gRPC C# Reflection</title>
+    <summary>Implementation of gRPC reflection service</summary>
+    <description>Provides information about services running on a gRPC C# server.</description>
+    <version>$version$</version>
+    <authors>Google Inc.</authors>
+    <owners>grpc-packages</owners>
+    <licenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</licenseUrl>
+    <projectUrl>https://github.com/grpc/grpc</projectUrl>
+    <requireLicenseAcceptance>false</requireLicenseAcceptance>
+    <copyright>Copyright 2016, Google Inc.</copyright>
+    <tags>gRPC reflection</tags>
+	<dependencies>
+	  <dependency id="Google.Protobuf" version="$ProtobufVersion$" />
+	  <dependency id="Grpc.Core" version="$version$" />
+	  <dependency id="System.Interactive.Async" version="3.1.1" />
+    </dependencies>
+  </metadata>
+  <files>
+    <file src="bin/ReleaseSigned/Grpc.Reflection.dll" target="lib/net45" />
+    <file src="bin/ReleaseSigned/Grpc.Reflection.pdb" target="lib/net45" />
+    <file src="bin/ReleaseSigned/Grpc.Reflection.xml" target="lib/net45" />
+    <file src="**\*.cs" target="src" />
+  </files>
+</package>
diff --git a/src/csharp/Grpc.Reflection/Grpc.Reflection.project.json b/src/csharp/Grpc.Reflection/Grpc.Reflection.project.json
new file mode 100644
index 0000000000..c2f5bcb163
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/Grpc.Reflection.project.json
@@ -0,0 +1,8 @@
+{
+  "frameworks": {
+    "net45": { }
+  },
+  "runtimes": {
+    "win": { }
+  }
+}
diff --git a/src/csharp/Grpc.Reflection/Grpc.Reflection.xproj b/src/csharp/Grpc.Reflection/Grpc.Reflection.xproj
new file mode 100644
index 0000000000..833d98b121
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/Grpc.Reflection.xproj
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0.25123" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25123</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+  </PropertyGroup>
+  <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>2b372155-80ba-4cf9-82d6-4b938e8ec3a0</ProjectGuid>
+    <RootNamespace>Grpc.Reflection</RootNamespace>
+    <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
+    <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
+  </PropertyGroup>
+  <PropertyGroup>
+    <SchemaVersion>2.0</SchemaVersion>
+  </PropertyGroup>
+  <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
+</Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Reflection/Properties/AssemblyInfo.cs b/src/csharp/Grpc.Reflection/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..3104ecdd54
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/Properties/AssemblyInfo.cs
@@ -0,0 +1,44 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+[assembly: AssemblyTitle("Grpc.Reflection")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Google Inc.  All rights reserved.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
diff --git a/src/csharp/Grpc.Reflection/Reflection.cs b/src/csharp/Grpc.Reflection/Reflection.cs
new file mode 100644
index 0000000000..bb92dfcbae
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/Reflection.cs
@@ -0,0 +1,1556 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: reflection.proto
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Grpc.Reflection.V1Alpha {
+
+  /// <summary>Holder for reflection information generated from reflection.proto</summary>
+  public static partial class ReflectionReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for reflection.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static ReflectionReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "ChByZWZsZWN0aW9uLnByb3RvEhdncnBjLnJlZmxlY3Rpb24udjFhbHBoYSKK",
+            "AgoXU2VydmVyUmVmbGVjdGlvblJlcXVlc3QSDAoEaG9zdBgBIAEoCRIaChBm",
+            "aWxlX2J5X2ZpbGVuYW1lGAMgASgJSAASIAoWZmlsZV9jb250YWluaW5nX3N5",
+            "bWJvbBgEIAEoCUgAEk4KGWZpbGVfY29udGFpbmluZ19leHRlbnNpb24YBSAB",
+            "KAsyKS5ncnBjLnJlZmxlY3Rpb24udjFhbHBoYS5FeHRlbnNpb25SZXF1ZXN0",
+            "SAASJwodYWxsX2V4dGVuc2lvbl9udW1iZXJzX29mX3R5cGUYBiABKAlIABIX",
+            "Cg1saXN0X3NlcnZpY2VzGAcgASgJSABCEQoPbWVzc2FnZV9yZXF1ZXN0IkUK",
+            "EEV4dGVuc2lvblJlcXVlc3QSFwoPY29udGFpbmluZ190eXBlGAEgASgJEhgK",
+            "EGV4dGVuc2lvbl9udW1iZXIYAiABKAUi0QMKGFNlcnZlclJlZmxlY3Rpb25S",
+            "ZXNwb25zZRISCgp2YWxpZF9ob3N0GAEgASgJEkoKEG9yaWdpbmFsX3JlcXVl",
+            "c3QYAiABKAsyMC5ncnBjLnJlZmxlY3Rpb24udjFhbHBoYS5TZXJ2ZXJSZWZs",
+            "ZWN0aW9uUmVxdWVzdBJTChhmaWxlX2Rlc2NyaXB0b3JfcmVzcG9uc2UYBCAB",
+            "KAsyLy5ncnBjLnJlZmxlY3Rpb24udjFhbHBoYS5GaWxlRGVzY3JpcHRvclJl",
+            "c3BvbnNlSAASWgoeYWxsX2V4dGVuc2lvbl9udW1iZXJzX3Jlc3BvbnNlGAUg",
+            "ASgLMjAuZ3JwYy5yZWZsZWN0aW9uLnYxYWxwaGEuRXh0ZW5zaW9uTnVtYmVy",
+            "UmVzcG9uc2VIABJOChZsaXN0X3NlcnZpY2VzX3Jlc3BvbnNlGAYgASgLMiwu",
+            "Z3JwYy5yZWZsZWN0aW9uLnYxYWxwaGEuTGlzdFNlcnZpY2VSZXNwb25zZUgA",
+            "EkAKDmVycm9yX3Jlc3BvbnNlGAcgASgLMiYuZ3JwYy5yZWZsZWN0aW9uLnYx",
+            "YWxwaGEuRXJyb3JSZXNwb25zZUgAQhIKEG1lc3NhZ2VfcmVzcG9uc2UiNwoW",
+            "RmlsZURlc2NyaXB0b3JSZXNwb25zZRIdChVmaWxlX2Rlc2NyaXB0b3JfcHJv",
+            "dG8YASADKAwiSwoXRXh0ZW5zaW9uTnVtYmVyUmVzcG9uc2USFgoOYmFzZV90",
+            "eXBlX25hbWUYASABKAkSGAoQZXh0ZW5zaW9uX251bWJlchgCIAMoBSJQChNM",
+            "aXN0U2VydmljZVJlc3BvbnNlEjkKB3NlcnZpY2UYASADKAsyKC5ncnBjLnJl",
+            "ZmxlY3Rpb24udjFhbHBoYS5TZXJ2aWNlUmVzcG9uc2UiHwoPU2VydmljZVJl",
+            "c3BvbnNlEgwKBG5hbWUYASABKAkiOgoNRXJyb3JSZXNwb25zZRISCgplcnJv",
+            "cl9jb2RlGAEgASgFEhUKDWVycm9yX21lc3NhZ2UYAiABKAkykwEKEFNlcnZl",
+            "clJlZmxlY3Rpb24SfwoUU2VydmVyUmVmbGVjdGlvbkluZm8SMC5ncnBjLnJl",
+            "ZmxlY3Rpb24udjFhbHBoYS5TZXJ2ZXJSZWZsZWN0aW9uUmVxdWVzdBoxLmdy",
+            "cGMucmVmbGVjdGlvbi52MWFscGhhLlNlcnZlclJlZmxlY3Rpb25SZXNwb25z",
+            "ZSgBMAFiBnByb3RvMw=="));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { },
+          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Reflection.V1Alpha.ServerReflectionRequest), global::Grpc.Reflection.V1Alpha.ServerReflectionRequest.Parser, new[]{ "Host", "FileByFilename", "FileContainingSymbol", "FileContainingExtension", "AllExtensionNumbersOfType", "ListServices" }, new[]{ "MessageRequest" }, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Reflection.V1Alpha.ExtensionRequest), global::Grpc.Reflection.V1Alpha.ExtensionRequest.Parser, new[]{ "ContainingType", "ExtensionNumber" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Reflection.V1Alpha.ServerReflectionResponse), global::Grpc.Reflection.V1Alpha.ServerReflectionResponse.Parser, new[]{ "ValidHost", "OriginalRequest", "FileDescriptorResponse", "AllExtensionNumbersResponse", "ListServicesResponse", "ErrorResponse" }, new[]{ "MessageResponse" }, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Reflection.V1Alpha.FileDescriptorResponse), global::Grpc.Reflection.V1Alpha.FileDescriptorResponse.Parser, new[]{ "FileDescriptorProto" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Reflection.V1Alpha.ExtensionNumberResponse), global::Grpc.Reflection.V1Alpha.ExtensionNumberResponse.Parser, new[]{ "BaseTypeName", "ExtensionNumber" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Reflection.V1Alpha.ListServiceResponse), global::Grpc.Reflection.V1Alpha.ListServiceResponse.Parser, new[]{ "Service" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Reflection.V1Alpha.ServiceResponse), global::Grpc.Reflection.V1Alpha.ServiceResponse.Parser, new[]{ "Name" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Reflection.V1Alpha.ErrorResponse), global::Grpc.Reflection.V1Alpha.ErrorResponse.Parser, new[]{ "ErrorCode", "ErrorMessage" }, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  /// <summary>
+  ///  The message sent by the client when calling ServerReflectionInfo method.
+  /// </summary>
+  public sealed partial class ServerReflectionRequest : pb::IMessage<ServerReflectionRequest> {
+    private static readonly pb::MessageParser<ServerReflectionRequest> _parser = new pb::MessageParser<ServerReflectionRequest>(() => new ServerReflectionRequest());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<ServerReflectionRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Grpc.Reflection.V1Alpha.ReflectionReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ServerReflectionRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ServerReflectionRequest(ServerReflectionRequest other) : this() {
+      host_ = other.host_;
+      switch (other.MessageRequestCase) {
+        case MessageRequestOneofCase.FileByFilename:
+          FileByFilename = other.FileByFilename;
+          break;
+        case MessageRequestOneofCase.FileContainingSymbol:
+          FileContainingSymbol = other.FileContainingSymbol;
+          break;
+        case MessageRequestOneofCase.FileContainingExtension:
+          FileContainingExtension = other.FileContainingExtension.Clone();
+          break;
+        case MessageRequestOneofCase.AllExtensionNumbersOfType:
+          AllExtensionNumbersOfType = other.AllExtensionNumbersOfType;
+          break;
+        case MessageRequestOneofCase.ListServices:
+          ListServices = other.ListServices;
+          break;
+      }
+
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ServerReflectionRequest Clone() {
+      return new ServerReflectionRequest(this);
+    }
+
+    /// <summary>Field number for the "host" field.</summary>
+    public const int HostFieldNumber = 1;
+    private string host_ = "";
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string Host {
+      get { return host_; }
+      set {
+        host_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "file_by_filename" field.</summary>
+    public const int FileByFilenameFieldNumber = 3;
+    /// <summary>
+    ///  Find a proto file by the file name.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string FileByFilename {
+      get { return messageRequestCase_ == MessageRequestOneofCase.FileByFilename ? (string) messageRequest_ : ""; }
+      set {
+        messageRequest_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        messageRequestCase_ = MessageRequestOneofCase.FileByFilename;
+      }
+    }
+
+    /// <summary>Field number for the "file_containing_symbol" field.</summary>
+    public const int FileContainingSymbolFieldNumber = 4;
+    /// <summary>
+    ///  Find the proto file that declares the given fully-qualified symbol name.
+    ///  This field should be a fully-qualified symbol name
+    ///  (e.g. &lt;package>.&lt;service>[.&lt;method>] or &lt;package>.&lt;type>).
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string FileContainingSymbol {
+      get { return messageRequestCase_ == MessageRequestOneofCase.FileContainingSymbol ? (string) messageRequest_ : ""; }
+      set {
+        messageRequest_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        messageRequestCase_ = MessageRequestOneofCase.FileContainingSymbol;
+      }
+    }
+
+    /// <summary>Field number for the "file_containing_extension" field.</summary>
+    public const int FileContainingExtensionFieldNumber = 5;
+    /// <summary>
+    ///  Find the proto file which defines an extension extending the given
+    ///  message type with the given field number.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public global::Grpc.Reflection.V1Alpha.ExtensionRequest FileContainingExtension {
+      get { return messageRequestCase_ == MessageRequestOneofCase.FileContainingExtension ? (global::Grpc.Reflection.V1Alpha.ExtensionRequest) messageRequest_ : null; }
+      set {
+        messageRequest_ = value;
+        messageRequestCase_ = value == null ? MessageRequestOneofCase.None : MessageRequestOneofCase.FileContainingExtension;
+      }
+    }
+
+    /// <summary>Field number for the "all_extension_numbers_of_type" field.</summary>
+    public const int AllExtensionNumbersOfTypeFieldNumber = 6;
+    /// <summary>
+    ///  Finds the tag numbers used by all known extensions of the given message
+    ///  type, and appends them to ExtensionNumberResponse in an undefined order.
+    ///  Its corresponding method is best-effort: it's not guaranteed that the
+    ///  reflection service will implement this method, and it's not guaranteed
+    ///  that this method will provide all extensions. Returns
+    ///  StatusCode::UNIMPLEMENTED if it's not implemented.
+    ///  This field should be a fully-qualified type name. The format is
+    ///  &lt;package>.&lt;type>
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string AllExtensionNumbersOfType {
+      get { return messageRequestCase_ == MessageRequestOneofCase.AllExtensionNumbersOfType ? (string) messageRequest_ : ""; }
+      set {
+        messageRequest_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        messageRequestCase_ = MessageRequestOneofCase.AllExtensionNumbersOfType;
+      }
+    }
+
+    /// <summary>Field number for the "list_services" field.</summary>
+    public const int ListServicesFieldNumber = 7;
+    /// <summary>
+    ///  List the full names of registered services. The content will not be
+    ///  checked.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string ListServices {
+      get { return messageRequestCase_ == MessageRequestOneofCase.ListServices ? (string) messageRequest_ : ""; }
+      set {
+        messageRequest_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        messageRequestCase_ = MessageRequestOneofCase.ListServices;
+      }
+    }
+
+    private object messageRequest_;
+    /// <summary>Enum of possible cases for the "message_request" oneof.</summary>
+    public enum MessageRequestOneofCase {
+      None = 0,
+      FileByFilename = 3,
+      FileContainingSymbol = 4,
+      FileContainingExtension = 5,
+      AllExtensionNumbersOfType = 6,
+      ListServices = 7,
+    }
+    private MessageRequestOneofCase messageRequestCase_ = MessageRequestOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public MessageRequestOneofCase MessageRequestCase {
+      get { return messageRequestCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void ClearMessageRequest() {
+      messageRequestCase_ = MessageRequestOneofCase.None;
+      messageRequest_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as ServerReflectionRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(ServerReflectionRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Host != other.Host) return false;
+      if (FileByFilename != other.FileByFilename) return false;
+      if (FileContainingSymbol != other.FileContainingSymbol) return false;
+      if (!object.Equals(FileContainingExtension, other.FileContainingExtension)) return false;
+      if (AllExtensionNumbersOfType != other.AllExtensionNumbersOfType) return false;
+      if (ListServices != other.ListServices) return false;
+      if (MessageRequestCase != other.MessageRequestCase) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Host.Length != 0) hash ^= Host.GetHashCode();
+      if (messageRequestCase_ == MessageRequestOneofCase.FileByFilename) hash ^= FileByFilename.GetHashCode();
+      if (messageRequestCase_ == MessageRequestOneofCase.FileContainingSymbol) hash ^= FileContainingSymbol.GetHashCode();
+      if (messageRequestCase_ == MessageRequestOneofCase.FileContainingExtension) hash ^= FileContainingExtension.GetHashCode();
+      if (messageRequestCase_ == MessageRequestOneofCase.AllExtensionNumbersOfType) hash ^= AllExtensionNumbersOfType.GetHashCode();
+      if (messageRequestCase_ == MessageRequestOneofCase.ListServices) hash ^= ListServices.GetHashCode();
+      hash ^= (int) messageRequestCase_;
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (Host.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Host);
+      }
+      if (messageRequestCase_ == MessageRequestOneofCase.FileByFilename) {
+        output.WriteRawTag(26);
+        output.WriteString(FileByFilename);
+      }
+      if (messageRequestCase_ == MessageRequestOneofCase.FileContainingSymbol) {
+        output.WriteRawTag(34);
+        output.WriteString(FileContainingSymbol);
+      }
+      if (messageRequestCase_ == MessageRequestOneofCase.FileContainingExtension) {
+        output.WriteRawTag(42);
+        output.WriteMessage(FileContainingExtension);
+      }
+      if (messageRequestCase_ == MessageRequestOneofCase.AllExtensionNumbersOfType) {
+        output.WriteRawTag(50);
+        output.WriteString(AllExtensionNumbersOfType);
+      }
+      if (messageRequestCase_ == MessageRequestOneofCase.ListServices) {
+        output.WriteRawTag(58);
+        output.WriteString(ListServices);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (Host.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Host);
+      }
+      if (messageRequestCase_ == MessageRequestOneofCase.FileByFilename) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(FileByFilename);
+      }
+      if (messageRequestCase_ == MessageRequestOneofCase.FileContainingSymbol) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(FileContainingSymbol);
+      }
+      if (messageRequestCase_ == MessageRequestOneofCase.FileContainingExtension) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(FileContainingExtension);
+      }
+      if (messageRequestCase_ == MessageRequestOneofCase.AllExtensionNumbersOfType) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(AllExtensionNumbersOfType);
+      }
+      if (messageRequestCase_ == MessageRequestOneofCase.ListServices) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ListServices);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(ServerReflectionRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Host.Length != 0) {
+        Host = other.Host;
+      }
+      switch (other.MessageRequestCase) {
+        case MessageRequestOneofCase.FileByFilename:
+          FileByFilename = other.FileByFilename;
+          break;
+        case MessageRequestOneofCase.FileContainingSymbol:
+          FileContainingSymbol = other.FileContainingSymbol;
+          break;
+        case MessageRequestOneofCase.FileContainingExtension:
+          FileContainingExtension = other.FileContainingExtension;
+          break;
+        case MessageRequestOneofCase.AllExtensionNumbersOfType:
+          AllExtensionNumbersOfType = other.AllExtensionNumbersOfType;
+          break;
+        case MessageRequestOneofCase.ListServices:
+          ListServices = other.ListServices;
+          break;
+      }
+
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 10: {
+            Host = input.ReadString();
+            break;
+          }
+          case 26: {
+            FileByFilename = input.ReadString();
+            break;
+          }
+          case 34: {
+            FileContainingSymbol = input.ReadString();
+            break;
+          }
+          case 42: {
+            global::Grpc.Reflection.V1Alpha.ExtensionRequest subBuilder = new global::Grpc.Reflection.V1Alpha.ExtensionRequest();
+            if (messageRequestCase_ == MessageRequestOneofCase.FileContainingExtension) {
+              subBuilder.MergeFrom(FileContainingExtension);
+            }
+            input.ReadMessage(subBuilder);
+            FileContainingExtension = subBuilder;
+            break;
+          }
+          case 50: {
+            AllExtensionNumbersOfType = input.ReadString();
+            break;
+          }
+          case 58: {
+            ListServices = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  ///  The type name and extension number sent by the client when requesting
+  ///  file_containing_extension.
+  /// </summary>
+  public sealed partial class ExtensionRequest : pb::IMessage<ExtensionRequest> {
+    private static readonly pb::MessageParser<ExtensionRequest> _parser = new pb::MessageParser<ExtensionRequest>(() => new ExtensionRequest());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<ExtensionRequest> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Grpc.Reflection.V1Alpha.ReflectionReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ExtensionRequest() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ExtensionRequest(ExtensionRequest other) : this() {
+      containingType_ = other.containingType_;
+      extensionNumber_ = other.extensionNumber_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ExtensionRequest Clone() {
+      return new ExtensionRequest(this);
+    }
+
+    /// <summary>Field number for the "containing_type" field.</summary>
+    public const int ContainingTypeFieldNumber = 1;
+    private string containingType_ = "";
+    /// <summary>
+    ///  Fully-qualified type name. The format should be &lt;package>.&lt;type>
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string ContainingType {
+      get { return containingType_; }
+      set {
+        containingType_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "extension_number" field.</summary>
+    public const int ExtensionNumberFieldNumber = 2;
+    private int extensionNumber_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int ExtensionNumber {
+      get { return extensionNumber_; }
+      set {
+        extensionNumber_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as ExtensionRequest);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(ExtensionRequest other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ContainingType != other.ContainingType) return false;
+      if (ExtensionNumber != other.ExtensionNumber) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ContainingType.Length != 0) hash ^= ContainingType.GetHashCode();
+      if (ExtensionNumber != 0) hash ^= ExtensionNumber.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (ContainingType.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ContainingType);
+      }
+      if (ExtensionNumber != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(ExtensionNumber);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (ContainingType.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ContainingType);
+      }
+      if (ExtensionNumber != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(ExtensionNumber);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(ExtensionRequest other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ContainingType.Length != 0) {
+        ContainingType = other.ContainingType;
+      }
+      if (other.ExtensionNumber != 0) {
+        ExtensionNumber = other.ExtensionNumber;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 10: {
+            ContainingType = input.ReadString();
+            break;
+          }
+          case 16: {
+            ExtensionNumber = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  ///  The message sent by the server to answer ServerReflectionInfo method.
+  /// </summary>
+  public sealed partial class ServerReflectionResponse : pb::IMessage<ServerReflectionResponse> {
+    private static readonly pb::MessageParser<ServerReflectionResponse> _parser = new pb::MessageParser<ServerReflectionResponse>(() => new ServerReflectionResponse());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<ServerReflectionResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Grpc.Reflection.V1Alpha.ReflectionReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ServerReflectionResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ServerReflectionResponse(ServerReflectionResponse other) : this() {
+      validHost_ = other.validHost_;
+      OriginalRequest = other.originalRequest_ != null ? other.OriginalRequest.Clone() : null;
+      switch (other.MessageResponseCase) {
+        case MessageResponseOneofCase.FileDescriptorResponse:
+          FileDescriptorResponse = other.FileDescriptorResponse.Clone();
+          break;
+        case MessageResponseOneofCase.AllExtensionNumbersResponse:
+          AllExtensionNumbersResponse = other.AllExtensionNumbersResponse.Clone();
+          break;
+        case MessageResponseOneofCase.ListServicesResponse:
+          ListServicesResponse = other.ListServicesResponse.Clone();
+          break;
+        case MessageResponseOneofCase.ErrorResponse:
+          ErrorResponse = other.ErrorResponse.Clone();
+          break;
+      }
+
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ServerReflectionResponse Clone() {
+      return new ServerReflectionResponse(this);
+    }
+
+    /// <summary>Field number for the "valid_host" field.</summary>
+    public const int ValidHostFieldNumber = 1;
+    private string validHost_ = "";
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string ValidHost {
+      get { return validHost_; }
+      set {
+        validHost_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "original_request" field.</summary>
+    public const int OriginalRequestFieldNumber = 2;
+    private global::Grpc.Reflection.V1Alpha.ServerReflectionRequest originalRequest_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public global::Grpc.Reflection.V1Alpha.ServerReflectionRequest OriginalRequest {
+      get { return originalRequest_; }
+      set {
+        originalRequest_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "file_descriptor_response" field.</summary>
+    public const int FileDescriptorResponseFieldNumber = 4;
+    /// <summary>
+    ///  This message is used to answer file_by_filename, file_containing_symbol,
+    ///  file_containing_extension requests with transitive dependencies. As
+    ///  the repeated label is not allowed in oneof fields, we use a
+    ///  FileDescriptorResponse message to encapsulate the repeated fields.
+    ///  The reflection service is allowed to avoid sending FileDescriptorProtos
+    ///  that were previously sent in response to earlier requests in the stream.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public global::Grpc.Reflection.V1Alpha.FileDescriptorResponse FileDescriptorResponse {
+      get { return messageResponseCase_ == MessageResponseOneofCase.FileDescriptorResponse ? (global::Grpc.Reflection.V1Alpha.FileDescriptorResponse) messageResponse_ : null; }
+      set {
+        messageResponse_ = value;
+        messageResponseCase_ = value == null ? MessageResponseOneofCase.None : MessageResponseOneofCase.FileDescriptorResponse;
+      }
+    }
+
+    /// <summary>Field number for the "all_extension_numbers_response" field.</summary>
+    public const int AllExtensionNumbersResponseFieldNumber = 5;
+    /// <summary>
+    ///  This message is used to answer all_extension_numbers_of_type requst.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public global::Grpc.Reflection.V1Alpha.ExtensionNumberResponse AllExtensionNumbersResponse {
+      get { return messageResponseCase_ == MessageResponseOneofCase.AllExtensionNumbersResponse ? (global::Grpc.Reflection.V1Alpha.ExtensionNumberResponse) messageResponse_ : null; }
+      set {
+        messageResponse_ = value;
+        messageResponseCase_ = value == null ? MessageResponseOneofCase.None : MessageResponseOneofCase.AllExtensionNumbersResponse;
+      }
+    }
+
+    /// <summary>Field number for the "list_services_response" field.</summary>
+    public const int ListServicesResponseFieldNumber = 6;
+    /// <summary>
+    ///  This message is used to answer list_services request.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public global::Grpc.Reflection.V1Alpha.ListServiceResponse ListServicesResponse {
+      get { return messageResponseCase_ == MessageResponseOneofCase.ListServicesResponse ? (global::Grpc.Reflection.V1Alpha.ListServiceResponse) messageResponse_ : null; }
+      set {
+        messageResponse_ = value;
+        messageResponseCase_ = value == null ? MessageResponseOneofCase.None : MessageResponseOneofCase.ListServicesResponse;
+      }
+    }
+
+    /// <summary>Field number for the "error_response" field.</summary>
+    public const int ErrorResponseFieldNumber = 7;
+    /// <summary>
+    ///  This message is used when an error occurs.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public global::Grpc.Reflection.V1Alpha.ErrorResponse ErrorResponse {
+      get { return messageResponseCase_ == MessageResponseOneofCase.ErrorResponse ? (global::Grpc.Reflection.V1Alpha.ErrorResponse) messageResponse_ : null; }
+      set {
+        messageResponse_ = value;
+        messageResponseCase_ = value == null ? MessageResponseOneofCase.None : MessageResponseOneofCase.ErrorResponse;
+      }
+    }
+
+    private object messageResponse_;
+    /// <summary>Enum of possible cases for the "message_response" oneof.</summary>
+    public enum MessageResponseOneofCase {
+      None = 0,
+      FileDescriptorResponse = 4,
+      AllExtensionNumbersResponse = 5,
+      ListServicesResponse = 6,
+      ErrorResponse = 7,
+    }
+    private MessageResponseOneofCase messageResponseCase_ = MessageResponseOneofCase.None;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public MessageResponseOneofCase MessageResponseCase {
+      get { return messageResponseCase_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void ClearMessageResponse() {
+      messageResponseCase_ = MessageResponseOneofCase.None;
+      messageResponse_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as ServerReflectionResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(ServerReflectionResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ValidHost != other.ValidHost) return false;
+      if (!object.Equals(OriginalRequest, other.OriginalRequest)) return false;
+      if (!object.Equals(FileDescriptorResponse, other.FileDescriptorResponse)) return false;
+      if (!object.Equals(AllExtensionNumbersResponse, other.AllExtensionNumbersResponse)) return false;
+      if (!object.Equals(ListServicesResponse, other.ListServicesResponse)) return false;
+      if (!object.Equals(ErrorResponse, other.ErrorResponse)) return false;
+      if (MessageResponseCase != other.MessageResponseCase) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ValidHost.Length != 0) hash ^= ValidHost.GetHashCode();
+      if (originalRequest_ != null) hash ^= OriginalRequest.GetHashCode();
+      if (messageResponseCase_ == MessageResponseOneofCase.FileDescriptorResponse) hash ^= FileDescriptorResponse.GetHashCode();
+      if (messageResponseCase_ == MessageResponseOneofCase.AllExtensionNumbersResponse) hash ^= AllExtensionNumbersResponse.GetHashCode();
+      if (messageResponseCase_ == MessageResponseOneofCase.ListServicesResponse) hash ^= ListServicesResponse.GetHashCode();
+      if (messageResponseCase_ == MessageResponseOneofCase.ErrorResponse) hash ^= ErrorResponse.GetHashCode();
+      hash ^= (int) messageResponseCase_;
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (ValidHost.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(ValidHost);
+      }
+      if (originalRequest_ != null) {
+        output.WriteRawTag(18);
+        output.WriteMessage(OriginalRequest);
+      }
+      if (messageResponseCase_ == MessageResponseOneofCase.FileDescriptorResponse) {
+        output.WriteRawTag(34);
+        output.WriteMessage(FileDescriptorResponse);
+      }
+      if (messageResponseCase_ == MessageResponseOneofCase.AllExtensionNumbersResponse) {
+        output.WriteRawTag(42);
+        output.WriteMessage(AllExtensionNumbersResponse);
+      }
+      if (messageResponseCase_ == MessageResponseOneofCase.ListServicesResponse) {
+        output.WriteRawTag(50);
+        output.WriteMessage(ListServicesResponse);
+      }
+      if (messageResponseCase_ == MessageResponseOneofCase.ErrorResponse) {
+        output.WriteRawTag(58);
+        output.WriteMessage(ErrorResponse);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (ValidHost.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ValidHost);
+      }
+      if (originalRequest_ != null) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(OriginalRequest);
+      }
+      if (messageResponseCase_ == MessageResponseOneofCase.FileDescriptorResponse) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(FileDescriptorResponse);
+      }
+      if (messageResponseCase_ == MessageResponseOneofCase.AllExtensionNumbersResponse) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(AllExtensionNumbersResponse);
+      }
+      if (messageResponseCase_ == MessageResponseOneofCase.ListServicesResponse) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ListServicesResponse);
+      }
+      if (messageResponseCase_ == MessageResponseOneofCase.ErrorResponse) {
+        size += 1 + pb::CodedOutputStream.ComputeMessageSize(ErrorResponse);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(ServerReflectionResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ValidHost.Length != 0) {
+        ValidHost = other.ValidHost;
+      }
+      if (other.originalRequest_ != null) {
+        if (originalRequest_ == null) {
+          originalRequest_ = new global::Grpc.Reflection.V1Alpha.ServerReflectionRequest();
+        }
+        OriginalRequest.MergeFrom(other.OriginalRequest);
+      }
+      switch (other.MessageResponseCase) {
+        case MessageResponseOneofCase.FileDescriptorResponse:
+          FileDescriptorResponse = other.FileDescriptorResponse;
+          break;
+        case MessageResponseOneofCase.AllExtensionNumbersResponse:
+          AllExtensionNumbersResponse = other.AllExtensionNumbersResponse;
+          break;
+        case MessageResponseOneofCase.ListServicesResponse:
+          ListServicesResponse = other.ListServicesResponse;
+          break;
+        case MessageResponseOneofCase.ErrorResponse:
+          ErrorResponse = other.ErrorResponse;
+          break;
+      }
+
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 10: {
+            ValidHost = input.ReadString();
+            break;
+          }
+          case 18: {
+            if (originalRequest_ == null) {
+              originalRequest_ = new global::Grpc.Reflection.V1Alpha.ServerReflectionRequest();
+            }
+            input.ReadMessage(originalRequest_);
+            break;
+          }
+          case 34: {
+            global::Grpc.Reflection.V1Alpha.FileDescriptorResponse subBuilder = new global::Grpc.Reflection.V1Alpha.FileDescriptorResponse();
+            if (messageResponseCase_ == MessageResponseOneofCase.FileDescriptorResponse) {
+              subBuilder.MergeFrom(FileDescriptorResponse);
+            }
+            input.ReadMessage(subBuilder);
+            FileDescriptorResponse = subBuilder;
+            break;
+          }
+          case 42: {
+            global::Grpc.Reflection.V1Alpha.ExtensionNumberResponse subBuilder = new global::Grpc.Reflection.V1Alpha.ExtensionNumberResponse();
+            if (messageResponseCase_ == MessageResponseOneofCase.AllExtensionNumbersResponse) {
+              subBuilder.MergeFrom(AllExtensionNumbersResponse);
+            }
+            input.ReadMessage(subBuilder);
+            AllExtensionNumbersResponse = subBuilder;
+            break;
+          }
+          case 50: {
+            global::Grpc.Reflection.V1Alpha.ListServiceResponse subBuilder = new global::Grpc.Reflection.V1Alpha.ListServiceResponse();
+            if (messageResponseCase_ == MessageResponseOneofCase.ListServicesResponse) {
+              subBuilder.MergeFrom(ListServicesResponse);
+            }
+            input.ReadMessage(subBuilder);
+            ListServicesResponse = subBuilder;
+            break;
+          }
+          case 58: {
+            global::Grpc.Reflection.V1Alpha.ErrorResponse subBuilder = new global::Grpc.Reflection.V1Alpha.ErrorResponse();
+            if (messageResponseCase_ == MessageResponseOneofCase.ErrorResponse) {
+              subBuilder.MergeFrom(ErrorResponse);
+            }
+            input.ReadMessage(subBuilder);
+            ErrorResponse = subBuilder;
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  ///  Serialized FileDescriptorProto messages sent by the server answering
+  ///  a file_by_filename, file_containing_symbol, or file_containing_extension
+  ///  request.
+  /// </summary>
+  public sealed partial class FileDescriptorResponse : pb::IMessage<FileDescriptorResponse> {
+    private static readonly pb::MessageParser<FileDescriptorResponse> _parser = new pb::MessageParser<FileDescriptorResponse>(() => new FileDescriptorResponse());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<FileDescriptorResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Grpc.Reflection.V1Alpha.ReflectionReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public FileDescriptorResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public FileDescriptorResponse(FileDescriptorResponse other) : this() {
+      fileDescriptorProto_ = other.fileDescriptorProto_.Clone();
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public FileDescriptorResponse Clone() {
+      return new FileDescriptorResponse(this);
+    }
+
+    /// <summary>Field number for the "file_descriptor_proto" field.</summary>
+    public const int FileDescriptorProtoFieldNumber = 1;
+    private static readonly pb::FieldCodec<pb::ByteString> _repeated_fileDescriptorProto_codec
+        = pb::FieldCodec.ForBytes(10);
+    private readonly pbc::RepeatedField<pb::ByteString> fileDescriptorProto_ = new pbc::RepeatedField<pb::ByteString>();
+    /// <summary>
+    ///  Serialized FileDescriptorProto messages. We avoid taking a dependency on
+    ///  descriptor.proto, which uses proto2 only features, by making them opaque
+    ///  bytes instead.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public pbc::RepeatedField<pb::ByteString> FileDescriptorProto {
+      get { return fileDescriptorProto_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as FileDescriptorResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(FileDescriptorResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if(!fileDescriptorProto_.Equals(other.fileDescriptorProto_)) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= fileDescriptorProto_.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      fileDescriptorProto_.WriteTo(output, _repeated_fileDescriptorProto_codec);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      size += fileDescriptorProto_.CalculateSize(_repeated_fileDescriptorProto_codec);
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(FileDescriptorResponse other) {
+      if (other == null) {
+        return;
+      }
+      fileDescriptorProto_.Add(other.fileDescriptorProto_);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 10: {
+            fileDescriptorProto_.AddEntriesFrom(input, _repeated_fileDescriptorProto_codec);
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  ///  A list of extension numbers sent by the server answering
+  ///  all_extension_numbers_of_type request.
+  /// </summary>
+  public sealed partial class ExtensionNumberResponse : pb::IMessage<ExtensionNumberResponse> {
+    private static readonly pb::MessageParser<ExtensionNumberResponse> _parser = new pb::MessageParser<ExtensionNumberResponse>(() => new ExtensionNumberResponse());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<ExtensionNumberResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Grpc.Reflection.V1Alpha.ReflectionReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ExtensionNumberResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ExtensionNumberResponse(ExtensionNumberResponse other) : this() {
+      baseTypeName_ = other.baseTypeName_;
+      extensionNumber_ = other.extensionNumber_.Clone();
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ExtensionNumberResponse Clone() {
+      return new ExtensionNumberResponse(this);
+    }
+
+    /// <summary>Field number for the "base_type_name" field.</summary>
+    public const int BaseTypeNameFieldNumber = 1;
+    private string baseTypeName_ = "";
+    /// <summary>
+    ///  Full name of the base type, including the package name. The format
+    ///  is &lt;package>.&lt;type>
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string BaseTypeName {
+      get { return baseTypeName_; }
+      set {
+        baseTypeName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "extension_number" field.</summary>
+    public const int ExtensionNumberFieldNumber = 2;
+    private static readonly pb::FieldCodec<int> _repeated_extensionNumber_codec
+        = pb::FieldCodec.ForInt32(18);
+    private readonly pbc::RepeatedField<int> extensionNumber_ = new pbc::RepeatedField<int>();
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public pbc::RepeatedField<int> ExtensionNumber {
+      get { return extensionNumber_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as ExtensionNumberResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(ExtensionNumberResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (BaseTypeName != other.BaseTypeName) return false;
+      if(!extensionNumber_.Equals(other.extensionNumber_)) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (BaseTypeName.Length != 0) hash ^= BaseTypeName.GetHashCode();
+      hash ^= extensionNumber_.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (BaseTypeName.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(BaseTypeName);
+      }
+      extensionNumber_.WriteTo(output, _repeated_extensionNumber_codec);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (BaseTypeName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(BaseTypeName);
+      }
+      size += extensionNumber_.CalculateSize(_repeated_extensionNumber_codec);
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(ExtensionNumberResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.BaseTypeName.Length != 0) {
+        BaseTypeName = other.BaseTypeName;
+      }
+      extensionNumber_.Add(other.extensionNumber_);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 10: {
+            BaseTypeName = input.ReadString();
+            break;
+          }
+          case 18:
+          case 16: {
+            extensionNumber_.AddEntriesFrom(input, _repeated_extensionNumber_codec);
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  ///  A list of ServiceResponse sent by the server answering list_services request.
+  /// </summary>
+  public sealed partial class ListServiceResponse : pb::IMessage<ListServiceResponse> {
+    private static readonly pb::MessageParser<ListServiceResponse> _parser = new pb::MessageParser<ListServiceResponse>(() => new ListServiceResponse());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<ListServiceResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Grpc.Reflection.V1Alpha.ReflectionReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ListServiceResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ListServiceResponse(ListServiceResponse other) : this() {
+      service_ = other.service_.Clone();
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ListServiceResponse Clone() {
+      return new ListServiceResponse(this);
+    }
+
+    /// <summary>Field number for the "service" field.</summary>
+    public const int ServiceFieldNumber = 1;
+    private static readonly pb::FieldCodec<global::Grpc.Reflection.V1Alpha.ServiceResponse> _repeated_service_codec
+        = pb::FieldCodec.ForMessage(10, global::Grpc.Reflection.V1Alpha.ServiceResponse.Parser);
+    private readonly pbc::RepeatedField<global::Grpc.Reflection.V1Alpha.ServiceResponse> service_ = new pbc::RepeatedField<global::Grpc.Reflection.V1Alpha.ServiceResponse>();
+    /// <summary>
+    ///  The information of each service may be expanded in the future, so we use
+    ///  ServiceResponse message to encapsulate it.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public pbc::RepeatedField<global::Grpc.Reflection.V1Alpha.ServiceResponse> Service {
+      get { return service_; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as ListServiceResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(ListServiceResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if(!service_.Equals(other.service_)) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      hash ^= service_.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      service_.WriteTo(output, _repeated_service_codec);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      size += service_.CalculateSize(_repeated_service_codec);
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(ListServiceResponse other) {
+      if (other == null) {
+        return;
+      }
+      service_.Add(other.service_);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 10: {
+            service_.AddEntriesFrom(input, _repeated_service_codec);
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  ///  The information of a single service used by ListServiceResponse to answer
+  ///  list_services request.
+  /// </summary>
+  public sealed partial class ServiceResponse : pb::IMessage<ServiceResponse> {
+    private static readonly pb::MessageParser<ServiceResponse> _parser = new pb::MessageParser<ServiceResponse>(() => new ServiceResponse());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<ServiceResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Grpc.Reflection.V1Alpha.ReflectionReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ServiceResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ServiceResponse(ServiceResponse other) : this() {
+      name_ = other.name_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ServiceResponse Clone() {
+      return new ServiceResponse(this);
+    }
+
+    /// <summary>Field number for the "name" field.</summary>
+    public const int NameFieldNumber = 1;
+    private string name_ = "";
+    /// <summary>
+    ///  Full name of a registered service, including its package name. The format
+    ///  is &lt;package>.&lt;service>
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as ServiceResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(ServiceResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Name != other.Name) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (Name.Length != 0) {
+        output.WriteRawTag(10);
+        output.WriteString(Name);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(ServiceResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 10: {
+            Name = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  ///  The error code and error message sent by the server when an error occurs.
+  /// </summary>
+  public sealed partial class ErrorResponse : pb::IMessage<ErrorResponse> {
+    private static readonly pb::MessageParser<ErrorResponse> _parser = new pb::MessageParser<ErrorResponse>(() => new ErrorResponse());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<ErrorResponse> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Grpc.Reflection.V1Alpha.ReflectionReflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ErrorResponse() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ErrorResponse(ErrorResponse other) : this() {
+      errorCode_ = other.errorCode_;
+      errorMessage_ = other.errorMessage_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public ErrorResponse Clone() {
+      return new ErrorResponse(this);
+    }
+
+    /// <summary>Field number for the "error_code" field.</summary>
+    public const int ErrorCodeFieldNumber = 1;
+    private int errorCode_;
+    /// <summary>
+    ///  This field uses the error codes defined in grpc::StatusCode.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int ErrorCode {
+      get { return errorCode_; }
+      set {
+        errorCode_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "error_message" field.</summary>
+    public const int ErrorMessageFieldNumber = 2;
+    private string errorMessage_ = "";
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string ErrorMessage {
+      get { return errorMessage_; }
+      set {
+        errorMessage_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as ErrorResponse);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(ErrorResponse other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ErrorCode != other.ErrorCode) return false;
+      if (ErrorMessage != other.ErrorMessage) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ErrorCode != 0) hash ^= ErrorCode.GetHashCode();
+      if (ErrorMessage.Length != 0) hash ^= ErrorMessage.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (ErrorCode != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(ErrorCode);
+      }
+      if (ErrorMessage.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(ErrorMessage);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (ErrorCode != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(ErrorCode);
+      }
+      if (ErrorMessage.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(ErrorMessage);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(ErrorResponse other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ErrorCode != 0) {
+        ErrorCode = other.ErrorCode;
+      }
+      if (other.ErrorMessage.Length != 0) {
+        ErrorMessage = other.ErrorMessage;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            ErrorCode = input.ReadInt32();
+            break;
+          }
+          case 18: {
+            ErrorMessage = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/src/csharp/Grpc.Reflection/ReflectionGrpc.cs b/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
new file mode 100644
index 0000000000..229244e673
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/ReflectionGrpc.cs
@@ -0,0 +1,132 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: reflection.proto
+// Original file comments:
+// Copyright 2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Service exported by server reflection
+//
+#region Designer generated code
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+
+namespace Grpc.Reflection.V1Alpha {
+  public static class ServerReflection
+  {
+    static readonly string __ServiceName = "grpc.reflection.v1alpha.ServerReflection";
+
+    static readonly Marshaller<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest> __Marshaller_ServerReflectionRequest = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Reflection.V1Alpha.ServerReflectionRequest.Parser.ParseFrom);
+    static readonly Marshaller<global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> __Marshaller_ServerReflectionResponse = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Reflection.V1Alpha.ServerReflectionResponse.Parser.ParseFrom);
+
+    static readonly Method<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> __Method_ServerReflectionInfo = new Method<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse>(
+        MethodType.DuplexStreaming,
+        __ServiceName,
+        "ServerReflectionInfo",
+        __Marshaller_ServerReflectionRequest,
+        __Marshaller_ServerReflectionResponse);
+
+    /// <summary>Service descriptor</summary>
+    public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
+    {
+      get { return global::Grpc.Reflection.V1Alpha.ReflectionReflection.Descriptor.Services[0]; }
+    }
+
+    /// <summary>Base class for server-side implementations of ServerReflection</summary>
+    public abstract class ServerReflectionBase
+    {
+      /// <summary>
+      ///  The reflection service is structured as a bidirectional stream, ensuring
+      ///  all related requests go to a single server.
+      /// </summary>
+      public virtual global::System.Threading.Tasks.Task ServerReflectionInfo(IAsyncStreamReader<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest> requestStream, IServerStreamWriter<global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> responseStream, ServerCallContext context)
+      {
+        throw new RpcException(new Status(StatusCode.Unimplemented, ""));
+      }
+
+    }
+
+    /// <summary>Client for ServerReflection</summary>
+    public class ServerReflectionClient : ClientBase<ServerReflectionClient>
+    {
+      /// <summary>Creates a new client for ServerReflection</summary>
+      /// <param name="channel">The channel to use to make remote calls.</param>
+      public ServerReflectionClient(Channel channel) : base(channel)
+      {
+      }
+      /// <summary>Creates a new client for ServerReflection that uses a custom <c>CallInvoker</c>.</summary>
+      /// <param name="callInvoker">The callInvoker to use to make remote calls.</param>
+      public ServerReflectionClient(CallInvoker callInvoker) : base(callInvoker)
+      {
+      }
+      /// <summary>Protected parameterless constructor to allow creation of test doubles.</summary>
+      protected ServerReflectionClient() : base()
+      {
+      }
+      /// <summary>Protected constructor to allow creation of configured clients.</summary>
+      /// <param name="configuration">The client configuration.</param>
+      protected ServerReflectionClient(ClientBaseConfiguration configuration) : base(configuration)
+      {
+      }
+
+      /// <summary>
+      ///  The reflection service is structured as a bidirectional stream, ensuring
+      ///  all related requests go to a single server.
+      /// </summary>
+      public virtual AsyncDuplexStreamingCall<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> ServerReflectionInfo(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
+      {
+        return ServerReflectionInfo(new CallOptions(headers, deadline, cancellationToken));
+      }
+      /// <summary>
+      ///  The reflection service is structured as a bidirectional stream, ensuring
+      ///  all related requests go to a single server.
+      /// </summary>
+      public virtual AsyncDuplexStreamingCall<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> ServerReflectionInfo(CallOptions options)
+      {
+        return CallInvoker.AsyncDuplexStreamingCall(__Method_ServerReflectionInfo, null, options);
+      }
+      /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
+      protected override ServerReflectionClient NewInstance(ClientBaseConfiguration configuration)
+      {
+        return new ServerReflectionClient(configuration);
+      }
+    }
+
+    /// <summary>Creates service definition that can be registered with a server</summary>
+    public static ServerServiceDefinition BindService(ServerReflectionBase serviceImpl)
+    {
+      return ServerServiceDefinition.CreateBuilder()
+          .AddMethod(__Method_ServerReflectionInfo, serviceImpl.ServerReflectionInfo).Build();
+    }
+
+  }
+}
+#endregion
diff --git a/src/csharp/Grpc.Reflection/ReflectionServiceImpl.cs b/src/csharp/Grpc.Reflection/ReflectionServiceImpl.cs
new file mode 100644
index 0000000000..105c4c963b
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/ReflectionServiceImpl.cs
@@ -0,0 +1,173 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Grpc.Core;
+using Grpc.Core.Utils;
+using Grpc.Reflection.V1Alpha;
+using Google.Protobuf.Reflection;
+
+namespace Grpc.Reflection
+{
+    /// <summary>
+    /// Implementation of server reflection service.
+    /// </summary>
+    public class ReflectionServiceImpl : Grpc.Reflection.V1Alpha.ServerReflection.ServerReflectionBase
+    {
+        readonly List<string> services;
+        readonly SymbolRegistry symbolRegistry;
+
+        /// <summary>
+        /// Creates a new instance of <c>ReflectionServiceIml</c>.
+        /// </summary>
+        public ReflectionServiceImpl(IEnumerable<string> services, SymbolRegistry symbolRegistry)
+        {
+            this.services = new List<string>(services);
+            this.symbolRegistry = symbolRegistry;
+        }
+
+        /// <summary>
+        /// Creates a new instance of <c>ReflectionServiceIml</c>.
+        /// </summary>
+        public ReflectionServiceImpl(IEnumerable<ServiceDescriptor> serviceDescriptors)
+        {
+            this.services = new List<string>(serviceDescriptors.Select((serviceDescriptor) => serviceDescriptor.FullName));
+            this.symbolRegistry = SymbolRegistry.FromFiles(serviceDescriptors.Select((serviceDescriptor) => serviceDescriptor.File));
+        }
+
+        /// <summary>
+        /// Creates a new instance of <c>ReflectionServiceIml</c>.
+        /// </summary>
+        public ReflectionServiceImpl(params ServiceDescriptor[] serviceDescriptors) : this((IEnumerable<ServiceDescriptor>) serviceDescriptors)
+        {
+        }
+
+        public override async Task ServerReflectionInfo(IAsyncStreamReader<ServerReflectionRequest> requestStream, IServerStreamWriter<ServerReflectionResponse> responseStream, ServerCallContext context)
+        {
+            while (await requestStream.MoveNext())
+            {
+                var response = ProcessRequest(requestStream.Current);
+                await responseStream.WriteAsync(response);
+            }
+        }
+
+        ServerReflectionResponse ProcessRequest(ServerReflectionRequest request)
+        {
+            switch (request.MessageRequestCase)
+            {
+                case ServerReflectionRequest.MessageRequestOneofCase.FileByFilename:
+                    return FileByFilename(request.FileByFilename);
+                case ServerReflectionRequest.MessageRequestOneofCase.FileContainingSymbol:
+                    return FileContainingSymbol(request.FileContainingSymbol);
+                case ServerReflectionRequest.MessageRequestOneofCase.ListServices:
+                    return ListServices();
+                case ServerReflectionRequest.MessageRequestOneofCase.AllExtensionNumbersOfType:
+                case ServerReflectionRequest.MessageRequestOneofCase.FileContainingExtension:
+                default:
+                    return CreateErrorResponse(StatusCode.Unimplemented, "Request type not supported by C# reflection service.");
+            }
+        }
+
+        ServerReflectionResponse FileByFilename(string filename)
+        {
+            FileDescriptor file = symbolRegistry.FileByName(filename);
+            if (file == null)
+            {
+                return CreateErrorResponse(StatusCode.NotFound, "File not found.");
+            }
+
+            var transitiveDependencies = new HashSet<FileDescriptor>();
+            CollectTransitiveDependencies(file, transitiveDependencies);
+
+            return new ServerReflectionResponse
+            {
+                FileDescriptorResponse = new FileDescriptorResponse { FileDescriptorProto = { transitiveDependencies.Select((d) => d.SerializedData) } }
+            };
+        }
+
+        ServerReflectionResponse FileContainingSymbol(string symbol)
+        {
+            FileDescriptor file = symbolRegistry.FileContainingSymbol(symbol);
+            if (file == null)
+            {
+                return CreateErrorResponse(StatusCode.NotFound, "Symbol not found.");
+            }
+
+            var transitiveDependencies = new HashSet<FileDescriptor>();
+            CollectTransitiveDependencies(file, transitiveDependencies);
+
+            return new ServerReflectionResponse
+            {
+                FileDescriptorResponse = new FileDescriptorResponse { FileDescriptorProto = { transitiveDependencies.Select((d) => d.SerializedData) } }
+            };
+        }
+
+        ServerReflectionResponse ListServices()
+        {
+            var serviceResponses = new ListServiceResponse();
+            foreach (string serviceName in services)
+            {
+                serviceResponses.Service.Add(new ServiceResponse { Name = serviceName });
+            }
+
+            return new ServerReflectionResponse
+            {
+                ListServicesResponse = serviceResponses
+            };
+        }
+
+        ServerReflectionResponse CreateErrorResponse(StatusCode status, string message)
+        {
+            return new ServerReflectionResponse
+            {
+                ErrorResponse = new ErrorResponse { ErrorCode = (int) status, ErrorMessage = message }
+            };
+        }
+
+        void CollectTransitiveDependencies(FileDescriptor descriptor, HashSet<FileDescriptor> pool)
+        {
+            pool.Add(descriptor);
+            foreach (var dependency in descriptor.Dependencies)
+            {
+                if (pool.Add(dependency))
+                {
+                    // descriptors cannot have circular dependencies
+                    CollectTransitiveDependencies(dependency, pool);
+                }
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Reflection/Settings.StyleCop b/src/csharp/Grpc.Reflection/Settings.StyleCop
new file mode 100644
index 0000000000..2942add962
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/Settings.StyleCop
@@ -0,0 +1,10 @@
+<StyleCopSettings Version="105">
+  <SourceFileList>
+    <SourceFile>Health.cs</SourceFile>
+    <Settings>
+    <GlobalSettings>
+      <BooleanProperty Name="RulesEnabledByDefault">False</BooleanProperty>
+    </GlobalSettings>
+    </Settings>
+  </SourceFileList>
+</StyleCopSettings>
diff --git a/src/csharp/Grpc.Reflection/SymbolRegistry.cs b/src/csharp/Grpc.Reflection/SymbolRegistry.cs
new file mode 100644
index 0000000000..b7104ab2f9
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/SymbolRegistry.cs
@@ -0,0 +1,160 @@
+#region Copyright notice and license
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System.Collections.Generic;
+using Grpc.Core.Utils;
+using Google.Protobuf.Reflection;
+
+namespace Grpc.Reflection
+{
+    /// <summary>Registry of protobuf symbols</summary>
+    public class SymbolRegistry
+    {
+        private readonly Dictionary<string, FileDescriptor> filesByName;
+        private readonly Dictionary<string, FileDescriptor> filesBySymbol;
+        
+        private SymbolRegistry(Dictionary<string, FileDescriptor> filesByName, Dictionary<string, FileDescriptor> filesBySymbol)
+        {
+            this.filesByName = new Dictionary<string, FileDescriptor>(filesByName);
+            this.filesBySymbol = new Dictionary<string, FileDescriptor>(filesBySymbol);
+        }
+
+        /// <summary>
+        /// Creates a symbol registry from the specified set of file descriptors.
+        /// </summary>
+        /// <param name="fileDescriptors">The set of files to include in the registry. Must not contain null values.</param>
+        /// <returns>A symbol registry for the given files.</returns>
+        public static SymbolRegistry FromFiles(IEnumerable<FileDescriptor> fileDescriptors)
+        {
+            GrpcPreconditions.CheckNotNull(fileDescriptors);
+            var builder = new Builder();
+            foreach (var file in fileDescriptors)
+            {
+                builder.AddFile(file);
+            }
+            return builder.Build();
+        }
+
+        /// <summary>
+        /// Gets file descriptor for given file name (including package path). Returns <c>null</c> if not found.
+        /// </summary>
+        public FileDescriptor FileByName(string filename)
+        {
+            FileDescriptor file;
+            filesByName.TryGetValue(filename, out file);
+            return file;
+        }
+
+        /// <summary>
+        /// Gets file descriptor that contains definition of given symbol full name (including package path). Returns <c>null</c> if not found.
+        /// </summary>
+        public FileDescriptor FileContainingSymbol(string symbol)
+        {
+            FileDescriptor file;
+            filesBySymbol.TryGetValue(symbol, out file);
+            return file;
+        }
+
+        /// <summary>
+        /// Builder class which isn't exposed, but acts as a convenient alternative to passing round two dictionaries in recursive calls.
+        /// </summary>
+        private class Builder
+        {
+            private readonly Dictionary<string, FileDescriptor> filesByName;
+            private readonly Dictionary<string, FileDescriptor> filesBySymbol;
+            
+
+            internal Builder()
+            {
+                filesByName = new Dictionary<string, FileDescriptor>();
+                filesBySymbol = new Dictionary<string, FileDescriptor>();
+            }
+
+            internal void AddFile(FileDescriptor fileDescriptor)
+            {
+                if (filesByName.ContainsKey(fileDescriptor.Name))
+                {
+                    return;
+                }
+                filesByName.Add(fileDescriptor.Name, fileDescriptor);
+
+                foreach (var dependency in fileDescriptor.Dependencies)
+                {
+                    AddFile(dependency);
+                }
+                foreach (var enumeration in fileDescriptor.EnumTypes)
+                {
+                    AddEnum(enumeration);
+                }
+                foreach (var message in fileDescriptor.MessageTypes)
+                {
+                    AddMessage(message);
+                }
+                foreach (var service in fileDescriptor.Services)
+                {
+                    AddService(service);
+                }
+            }
+
+            private void AddEnum(EnumDescriptor enumDescriptor)
+            {
+                filesBySymbol[enumDescriptor.FullName] = enumDescriptor.File;
+            }
+
+            private void AddMessage(MessageDescriptor messageDescriptor)
+            {
+                foreach (var nestedEnum in messageDescriptor.EnumTypes)
+                {
+                    AddEnum(nestedEnum);
+                }
+                foreach (var nestedType in messageDescriptor.NestedTypes)
+                {
+                    AddMessage(nestedType);
+                }
+                filesBySymbol[messageDescriptor.FullName] = messageDescriptor.File;
+            }
+
+            private void AddService(ServiceDescriptor serviceDescriptor)
+            {
+                foreach (var method in serviceDescriptor.Methods)
+                {
+                    filesBySymbol[method.FullName] = method.File;
+                }
+                filesBySymbol[serviceDescriptor.FullName] = serviceDescriptor.File;
+            }
+
+            internal SymbolRegistry Build()
+            {
+                return new SymbolRegistry(filesByName, filesBySymbol);
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Reflection/packages.config b/src/csharp/Grpc.Reflection/packages.config
new file mode 100644
index 0000000000..5ab40b7a8c
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Google.Protobuf" version="3.0.0" targetFramework="net45" />
+  <package id="System.Interactive.Async" version="3.1.1" targetFramework="net45" />
+</packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Reflection/project.json b/src/csharp/Grpc.Reflection/project.json
new file mode 100644
index 0000000000..fc37bb2682
--- /dev/null
+++ b/src/csharp/Grpc.Reflection/project.json
@@ -0,0 +1,41 @@
+{
+  "version": "1.1.0-dev",
+  "title": "gRPC C# Reflection",
+  "authors": [ "Google Inc." ],
+  "copyright": "Copyright 2016, Google Inc.",
+  "packOptions": {
+    "summary": "Implementation of gRPC reflection service",
+    "description": "Provides information about services running on a gRPC C# server.",
+    "owners": [ "grpc-packages" ],
+    "licenseUrl": "https://github.com/grpc/grpc/blob/master/LICENSE",
+    "projectUrl": "https://github.com/grpc/grpc",
+    "requireLicenseAcceptance": false,
+    "tags": [ "gRPC reflection" ]
+  },
+  "buildOptions": {
+    "define": [ "SIGNED" ],
+    "keyFile": "../keys/Grpc.snk",
+    "publicSign": true,
+    "xmlDoc": true,
+    "compile": {
+      "includeFiles": [ "../Grpc.Core/Version.cs" ]
+    }
+  },
+  "dependencies": {
+    "Grpc.Core": "1.1.0-dev",
+    "Google.Protobuf": "3.0.0"
+  },
+  "frameworks": {
+    "net45": {
+      "frameworkAssemblies": {
+        "System.Runtime": "",
+        "System.IO": ""
+      }
+    },
+    "netstandard1.5": {
+      "dependencies": {
+        "NETStandard.Library": "1.6.0"
+      }
+    }
+  }
+}
diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln
index 9be36c0caa..2e6a8fd435 100644
--- a/src/csharp/Grpc.sln
+++ b/src/csharp/Grpc.sln
@@ -36,6 +36,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.IntegrationTesting.Qps
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.IntegrationTesting.StressClient", "Grpc.IntegrationTesting.StressClient\Grpc.IntegrationTesting.StressClient.csproj", "{ADEBA147-80AE-4710-82E9-5B7F93690266}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Reflection", "Grpc.Reflection\Grpc.Reflection.csproj", "{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Reflection.Tests", "Grpc.Reflection.Tests\Grpc.Reflection.Tests.csproj", "{B88F91D6-436D-4C78-8B99-47800FA8DE03}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -55,6 +59,12 @@ Global
 		{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|Any CPU.Build.0 = Release|Any CPU
 		{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
 		{3D166931-BA2D-416E-95A3-D36E8F6E90B9}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
+		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+		{4F18CF52-B3DB-4A77-97C5-7F7F4B6C1715}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -103,6 +113,12 @@ Global
 		{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.Release|Any CPU.Build.0 = Release|Any CPU
 		{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.ReleaseSigned|Any CPU.ActiveCfg = Release|Any CPU
 		{B82B7DFE-7F7B-40EF-B3D6-064FF2B01294}.ReleaseSigned|Any CPU.Build.0 = Release|Any CPU
+		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.ReleaseSigned|Any CPU.ActiveCfg = ReleaseSigned|Any CPU
+		{B88F91D6-436D-4C78-8B99-47800FA8DE03}.ReleaseSigned|Any CPU.Build.0 = ReleaseSigned|Any CPU
 		{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{BF62FE08-373A-43D6-9D73-41CAA38B7011}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/src/csharp/generate_proto_csharp.sh b/src/csharp/generate_proto_csharp.sh
index 79488e02a5..429289b6fc 100755
--- a/src/csharp/generate_proto_csharp.sh
+++ b/src/csharp/generate_proto_csharp.sh
@@ -36,6 +36,7 @@ PROTOC=bins/opt/protobuf/protoc
 PLUGIN=protoc-gen-grpc=bins/opt/grpc_csharp_plugin
 EXAMPLES_DIR=src/csharp/Grpc.Examples
 HEALTHCHECK_DIR=src/csharp/Grpc.HealthCheck
+REFLECTION_DIR=src/csharp/Grpc.Reflection
 TESTING_DIR=src/csharp/Grpc.IntegrationTesting
 
 $PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \
@@ -43,6 +44,9 @@ $PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \
 
 $PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \
     -I src/proto/grpc/health/v1 src/proto/grpc/health/v1/health.proto
+    
+$PROTOC --plugin=$PLUGIN --csharp_out=$REFLECTION_DIR --grpc_out=$REFLECTION_DIR \
+    -I src/proto/grpc/reflection/v1alpha src/proto/grpc/reflection/v1alpha/reflection.proto
 
 $PROTOC --plugin=$PLUGIN --csharp_out=$TESTING_DIR --grpc_out=$TESTING_DIR \
     -I . src/proto/grpc/testing/{control,empty,messages,metrics,payloads,services,stats,test}.proto 
diff --git a/src/csharp/tests.json b/src/csharp/tests.json
index 7e7aee1093..4ce6769eee 100644
--- a/src/csharp/tests.json
+++ b/src/csharp/tests.json
@@ -48,5 +48,9 @@
     "Grpc.IntegrationTesting.MetadataCredentialsTest",
     "Grpc.IntegrationTesting.RunnerClientServerTest",
     "Grpc.IntegrationTesting.SslCredentialsTest"
+  ],
+  "Grpc.Reflection.Tests": [
+    "Grpc.Reflection.Tests.ReflectionClientServerTest",
+    "Grpc.Reflection.Tests.SymbolRegistryTest"
   ]
 }
\ No newline at end of file
diff --git a/templates/src/csharp/Grpc.Reflection.Tests/project.json.template b/templates/src/csharp/Grpc.Reflection.Tests/project.json.template
new file mode 100644
index 0000000000..2869609138
--- /dev/null
+++ b/templates/src/csharp/Grpc.Reflection.Tests/project.json.template
@@ -0,0 +1,26 @@
+%YAML 1.2
+--- |
+  {
+    <%include file="../build_options.include" args="executable=True"/>
+    "dependencies": {
+      "Grpc.Reflection": {
+        "target": "project"
+      },
+      "NUnit": "3.2.0",
+      "NUnitLite": "3.2.0-*"
+    },
+    "frameworks": {
+      "net45": { },
+      "netcoreapp1.0": {
+        "imports": [
+          "portable-net45"
+        ],
+        "dependencies": {
+          "Microsoft.NETCore.App": {
+            "type": "platform",
+            "version": "1.0.0"
+          }
+        }
+      }
+    }
+  }
diff --git a/templates/src/csharp/Grpc.Reflection/project.json.template b/templates/src/csharp/Grpc.Reflection/project.json.template
new file mode 100644
index 0000000000..c13b3a8d7d
--- /dev/null
+++ b/templates/src/csharp/Grpc.Reflection/project.json.template
@@ -0,0 +1,43 @@
+%YAML 1.2
+--- |
+  {
+    "version": "${settings.csharp_version}",
+    "title": "gRPC C# Reflection",
+    "authors": [ "Google Inc." ],
+    "copyright": "Copyright 2016, Google Inc.",
+    "packOptions": {
+      "summary": "Implementation of gRPC reflection service",
+      "description": "Provides information about services running on a gRPC C# server.",
+      "owners": [ "grpc-packages" ],
+      "licenseUrl": "https://github.com/grpc/grpc/blob/master/LICENSE",
+      "projectUrl": "https://github.com/grpc/grpc",
+      "requireLicenseAcceptance": false,
+      "tags": [ "gRPC reflection" ]
+    },
+    "buildOptions": {
+      "define": [ "SIGNED" ],
+      "keyFile": "../keys/Grpc.snk",
+      "publicSign": true,
+      "xmlDoc": true,
+      "compile": {
+        "includeFiles": [ "../Grpc.Core/Version.cs" ]
+      }
+    },
+    "dependencies": {
+      "Grpc.Core": "${settings.csharp_version}",
+      "Google.Protobuf": "3.0.0"
+    },
+    "frameworks": {
+      "net45": {
+        "frameworkAssemblies": {
+          "System.Runtime": "",
+          "System.IO": ""
+        }
+      },
+      "netstandard1.5": {
+        "dependencies": {
+          "NETStandard.Library": "1.6.0"
+        }
+      }
+    }
+  }
-- 
GitLab