diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc
index fb11d98fe471a064d75d64951cdaa68f6524083d..bc40880e5ca39a40c22adbc9481ef7496a6dbf4d 100644
--- a/src/compiler/csharp_generator.cc
+++ b/src/compiler/csharp_generator.cc
@@ -73,10 +73,6 @@ std::string GetClientInterfaceName(const ServiceDescriptor* service) {
   return "I" + service->name() + "Client";
 }
 
-std::string GetClientBaseClassName(const ServiceDescriptor* service) {
-  return service->name() + "ClientBase";
-}
-
 std::string GetClientClassName(const ServiceDescriptor* service) {
   return service->name() + "Client";
 }
@@ -300,86 +296,6 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
   out->Print("\n");
 }
 
-void GenerateClientBaseClass(Printer* out, const ServiceDescriptor *service) {
-  out->Print("// abstract client class\n");
-  out->Print("public abstract class $name$ : ClientBase\n", "name",
-             GetClientBaseClassName(service));
-  out->Print("{\n");
-  out->Indent();
-
-  // constructors
-  out->Print(
-      "public $name$(Channel channel) : base(channel)\n",
-      "name", GetClientBaseClassName(service));
-  out->Print("{\n");
-  out->Print("}\n");
-
-  for (int i = 0; i < service->method_count(); i++) {
-    const MethodDescriptor *method = service->method(i);
-    MethodType method_type = GetMethodType(method);
-
-    if (method_type == METHODTYPE_NO_STREAMING) {
-      // unary calls have an extra synchronous stub method
-      out->Print(
-          "public virtual $response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n",
-          "methodname", method->name(), "request",
-          GetClassName(method->input_type()), "response",
-          GetClassName(method->output_type()));
-      out->Print("{\n");
-      out->Indent();
-      out->Print("return $methodname$(request, new CallOptions(headers, deadline, cancellationToken));\n",
-                 "methodname", method->name());
-      out->Outdent();
-      out->Print("}\n");
-
-      // overload taking CallOptions as a param
-      out->Print(
-          "public virtual $response$ $methodname$($request$ request, CallOptions options)\n",
-          "methodname", method->name(), "request",
-          GetClassName(method->input_type()), "response",
-          GetClassName(method->output_type()));
-      out->Print("{\n");
-      out->Indent();
-      out->Print("throw new NotImplementedException();\n");
-      out->Outdent();
-      out->Print("}\n");
-    }
-
-    std::string method_name = method->name();
-    if (method_type == METHODTYPE_NO_STREAMING) {
-      method_name += "Async";  // prevent name clash with synchronous method.
-    }
-    out->Print(
-        "public virtual $returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n",
-        "methodname", method_name, "request_maybe",
-        GetMethodRequestParamMaybe(method), "returntype",
-        GetMethodReturnTypeClient(method));
-    out->Print("{\n");
-    out->Indent();
-
-    out->Print("return $methodname$($request_maybe$new CallOptions(headers, deadline, cancellationToken));\n",
-               "methodname", method_name,
-               "request_maybe", GetMethodRequestParamMaybe(method, true));
-    out->Outdent();
-    out->Print("}\n");
-
-    // overload taking CallOptions as a param
-    out->Print(
-        "public virtual $returntype$ $methodname$($request_maybe$CallOptions options)\n",
-        "methodname", method_name, "request_maybe",
-        GetMethodRequestParamMaybe(method), "returntype",
-        GetMethodReturnTypeClient(method));
-    out->Print("{\n");
-    out->Indent();
-    out->Print("throw new NotImplementedException();\n");
-    out->Outdent();
-    out->Print("}\n");
-  }
-  out->Outdent();
-  out->Print("}\n");
-  out->Print("\n");
-}
-
 void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) {
   out->Print("// server-side interface\n");
   out->Print("[System.Obsolete(\"Service implementations should inherit"
@@ -433,18 +349,20 @@ void GenerateServerClass(Printer* out, const ServiceDescriptor *service) {
 void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
   out->Print("// client stub\n");
   out->Print(
-      "public class $name$ : $baseclass$\n",
-      "name", GetClientClassName(service),
-      "baseclass", GetClientBaseClassName(service));
+      "public class $name$ : ClientBase<$name$>\n",
+      "name", GetClientClassName(service));
   out->Print("{\n");
   out->Indent();
 
   // constructors
-  out->Print(
-      "public $name$(Channel channel) : base(channel)\n",
-      "name", GetClientClassName(service));
+  out->Print("public $name$(Channel channel) : base(channel)\n",
+             "name", GetClientClassName(service));
   out->Print("{\n");
   out->Print("}\n");
+  out->Print("public $name$(CallInvoker callInvoker) : base(callInvoker)\n",
+             "name", GetClientClassName(service));
+  out->Print("{\n");
+  out->Print("}\n\n");
 
   for (int i = 0; i < service->method_count(); i++) {
     const MethodDescriptor *method = service->method(i);
@@ -452,16 +370,26 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
 
     if (method_type == METHODTYPE_NO_STREAMING) {
       // unary calls have an extra synchronous stub method
-      out->Print(
-                "public override $response$ $methodname$($request$ request, CallOptions options)\n",
-                "methodname", method->name(), "request",
-                GetClassName(method->input_type()), "response",
-                GetClassName(method->output_type()));
+      out->Print("public virtual $response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n",
+          "methodname", method->name(), "request",
+          GetClassName(method->input_type()), "response",
+          GetClassName(method->output_type()));
+      out->Print("{\n");
+      out->Indent();
+      out->Print("return $methodname$(request, new CallOptions(headers, deadline, cancellationToken));\n",
+                 "methodname", method->name());
+      out->Outdent();
+      out->Print("}\n");
+
+      // overload taking CallOptions as a param
+      out->Print("public virtual $response$ $methodname$($request$ request, CallOptions options)\n",
+          "methodname", method->name(), "request",
+          GetClassName(method->input_type()), "response",
+          GetClassName(method->output_type()));
       out->Print("{\n");
       out->Indent();
-      out->Print("var call = CreateCall($methodfield$, options);\n",
+      out->Print("return CallInvoker.BlockingUnaryCall($methodfield$, options, request);\n",
                  "methodfield", GetMethodFieldName(method));
-      out->Print("return Calls.BlockingUnaryCall(call, request);\n");
       out->Outdent();
       out->Print("}\n");
     }
@@ -471,27 +399,44 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
       method_name += "Async";  // prevent name clash with synchronous method.
     }
     out->Print(
-        "public override $returntype$ $methodname$($request_maybe$CallOptions options)\n",
+            "public virtual $returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n",
+            "methodname", method_name, "request_maybe",
+            GetMethodRequestParamMaybe(method), "returntype",
+            GetMethodReturnTypeClient(method));
+    out->Print("{\n");
+    out->Indent();
+
+    out->Print("return $methodname$($request_maybe$new CallOptions(headers, deadline, cancellationToken));\n",
+               "methodname", method_name,
+               "request_maybe", GetMethodRequestParamMaybe(method, true));
+    out->Outdent();
+    out->Print("}\n");
+
+    // overload taking CallOptions as a param
+    out->Print(
+        "public virtual $returntype$ $methodname$($request_maybe$CallOptions options)\n",
         "methodname", method_name, "request_maybe",
         GetMethodRequestParamMaybe(method), "returntype",
         GetMethodReturnTypeClient(method));
     out->Print("{\n");
     out->Indent();
-    out->Print("var call = CreateCall($methodfield$, options);\n",
-               "methodfield", GetMethodFieldName(method));
     switch (GetMethodType(method)) {
       case METHODTYPE_NO_STREAMING:
-        out->Print("return Calls.AsyncUnaryCall(call, request);\n");
+        out->Print("return CallInvoker.AsyncUnaryCall($methodfield$, options, request);\n",
+                   "methodfield", GetMethodFieldName(method));
         break;
       case METHODTYPE_CLIENT_STREAMING:
-        out->Print("return Calls.AsyncClientStreamingCall(call);\n");
+        out->Print("return CallInvoker.AsyncClientStreamingCall($methodfield$, options);\n",
+                   "methodfield", GetMethodFieldName(method));
         break;
       case METHODTYPE_SERVER_STREAMING:
         out->Print(
-            "return Calls.AsyncServerStreamingCall(call, request);\n");
+            "return CallInvoker.AsyncServerStreamingCall($methodfield$, options, request);\n",
+            "methodfield", GetMethodFieldName(method));
         break;
       case METHODTYPE_BIDI_STREAMING:
-        out->Print("return Calls.AsyncDuplexStreamingCall(call);\n");
+        out->Print("return CallInvoker.AsyncDuplexStreamingCall($methodfield$, options);\n",
+                   "methodfield", GetMethodFieldName(method));
         break;
       default:
         GOOGLE_LOG(FATAL)<< "Can't get here.";
@@ -499,6 +444,17 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
     out->Outdent();
     out->Print("}\n");
   }
+
+  // override NewInstance method
+  out->Print("protected override $name$ NewInstance(CallInvoker callInvoker)\n",
+             "name", GetClientClassName(service));
+  out->Print("{\n");
+  out->Indent();
+  out->Print("return new $name$(callInvoker);\n",
+             "name", GetClientClassName(service));
+  out->Outdent();
+  out->Print("}\n");
+
   out->Outdent();
   out->Print("}\n");
   out->Print("\n");
@@ -567,7 +523,7 @@ void GenerateService(Printer* out, const ServiceDescriptor *service) {
   }
   GenerateServiceDescriptorProperty(out, service);
   GenerateClientInterface(out, service);
-  GenerateClientBaseClass(out, service);
+  //GenerateClientBaseClass(out, service);
   GenerateServerInterface(out, service);
   GenerateServerClass(out, service);
   GenerateClientStub(out, service);