Merge pull request #5073 from mleenhardt/calloptions-fluent

CallOptions fluent methods
diff --git a/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs b/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs
index a3a613b..99a2d47 100644
--- a/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs
+++ b/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs
@@ -54,10 +54,20 @@
             var deadline = DateTime.UtcNow;
             Assert.AreEqual(deadline, options.WithDeadline(deadline).Deadline.Value);
 
-            var token = new CancellationTokenSource().Token;
-            Assert.AreEqual(token, options.WithCancellationToken(token).CancellationToken);
+            var cancellationToken = new CancellationTokenSource().Token;
+            Assert.AreEqual(cancellationToken, options.WithCancellationToken(cancellationToken).CancellationToken);
 
-            // Change original instance is unchanged.
+            var writeOptions = new WriteOptions();
+            Assert.AreSame(writeOptions, options.WithWriteOptions(writeOptions).WriteOptions);
+
+            var propagationToken = new ContextPropagationToken(CallSafeHandle.NullInstance, DateTime.UtcNow, 
+                CancellationToken.None, ContextPropagationOptions.Default);
+            Assert.AreSame(propagationToken, options.WithPropagationToken(propagationToken).PropagationToken);
+
+            var credentials = new FakeCallCredentials();
+            Assert.AreSame(credentials, options.WithCredentials(credentials).Credentials);
+
+            // Check that the original instance is unchanged.
             Assert.IsNull(options.Headers);
             Assert.IsNull(options.Deadline);
             Assert.AreEqual(CancellationToken.None, options.CancellationToken);
diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs
index caf8210..9ca8884 100644
--- a/src/csharp/Grpc.Core/CallOptions.cs
+++ b/src/csharp/Grpc.Core/CallOptions.cs
@@ -100,10 +100,7 @@
         /// </summary>
         public WriteOptions WriteOptions
         {
-            get
-            {
-                return this.writeOptions;
-            }
+            get { return this.writeOptions; }
         }
 
         /// <summary>
@@ -111,10 +108,7 @@
         /// </summary>
         public ContextPropagationToken PropagationToken
         {
-            get
-            {
-                return this.propagationToken;
-            }
+            get { return this.propagationToken; }
         }
 
         /// <summary>
@@ -122,10 +116,7 @@
         /// </summary>
         public CallCredentials Credentials
         {
-            get
-            {
-                return this.credentials;
-            }
+            get { return this.credentials; }
         }
 
         /// <summary>
@@ -165,6 +156,42 @@
         }
 
         /// <summary>
+        /// Returns new instance of <see cref="CallOptions"/> with
+        /// <c>WriteOptions</c> set to the value provided. Values of all other fields are preserved.
+        /// </summary>
+        /// <param name="writeOptions">The write options.</param>
+        public CallOptions WithWriteOptions(WriteOptions writeOptions)
+        {
+            var newOptions = this;
+            newOptions.writeOptions = writeOptions;
+            return newOptions;
+        }
+
+        /// <summary>
+        /// Returns new instance of <see cref="CallOptions"/> with
+        /// <c>PropagationToken</c> set to the value provided. Values of all other fields are preserved.
+        /// </summary>
+        /// <param name="propagationToken">The context propagation token.</param>
+        public CallOptions WithPropagationToken(ContextPropagationToken propagationToken)
+        {
+            var newOptions = this;
+            newOptions.propagationToken = propagationToken;
+            return newOptions;
+        }
+
+        /// <summary>
+        /// Returns new instance of <see cref="CallOptions"/> with
+        /// <c>Credentials</c> set to the value provided. Values of all other fields are preserved.
+        /// </summary>
+        /// <param name="credentials">The call credentials.</param>
+        public CallOptions WithCredentials(CallCredentials credentials)
+        {
+            var newOptions = this;
+            newOptions.credentials = credentials;
+            return newOptions;
+        }
+
+        /// <summary>
         /// Returns a new instance of <see cref="CallOptions"/> with 
         /// all previously unset values set to their defaults and deadline and cancellation
         /// token propagated when appropriate.