2 // Copyright (c) Microsoft Corporation. All Rights Reserved.
5 using System
.ServiceModel
;
7 namespace Microsoft
.ServiceModel
.Samples
9 //The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool.
11 // The "using" keyword in C# enables deterministic cleanup without writing a lot of code. It
12 // encourages correct cleanup because you do not have to write a lot of try/catch/finally code
13 // to clean up resources correctly. This works well when Dispose and Close rarely throw. For
14 // example, writing to a file rarely fails, and it may be acceptable for an application to fail
15 // under such circumstances.
17 // Unfortunately, network connections are much more prone to failure than most disposable
18 // resources. Because Dispose and Close on a client can throw based on factors outside the
19 // application's control, applications typically need to be hardened to handle these
20 // Exceptions. Handling these Exceptions correctly while using the C# "using" statement results
21 // in more complicated code than just writing the try/catch blocks.
23 // This sample illustrates some of the problems that can happen if you use the C# "using"
24 // statement with a client.
26 //Client implementation code.
31 DemonstrateProblemUsingCanThrow();
32 DemonstrateProblemUsingCanThrowAndMask();
33 DemonstrateCleanupWithExceptions();
36 Console
.WriteLine("Press <ENTER> to terminate client.");
40 // This method shows one problem with the "using" statement.
42 // The service Aborts the channel to indicate that the session failed. When that
43 // happens, the client gets an Exception from Close. Because Dispose is the same as
44 // Close, the client gets an Exception from Dispose as well. The close of the "using"
45 // block results in a call to client.Dispose. Typically developers use "using" to avoid
46 // having to write try/catch/finally code. However, because the closing brace can throw,
47 // the try/catch is necessary.
48 static void DemonstrateProblemUsingCanThrow()
50 Console
.WriteLine("=");
51 Console
.WriteLine("= Demonstrating problem: closing brace of using statement can throw.");
52 Console
.WriteLine("=");
56 // Create a new client.
57 using (CalculatorClient client
= new CalculatorClient())
59 // Call Divide and catch the associated Exception. This throws because the
60 // server aborts the channel before returning a reply.
63 client
.Divide(0.0, 0.0);
65 catch (CommunicationException e
)
67 Console
.WriteLine("Got {0} from Divide.", e
.GetType());
71 // The previous line calls Dispose on the client. Dispose and Close are the
72 // same thing, and the Close is not successful because the server Aborted the
73 // channel. This means that the code after the using statement does not run.
74 Console
.WriteLine("Hope this code wasn't important, because it might not happen.");
76 catch (CommunicationException e
)
78 // The closing brace of the "using" block throws, so we end up here. If you
79 // want to use using, you must surround it with a try/catch
80 Console
.WriteLine("Got {0}", e
.GetType());
84 // This method shows another problem with the "using" statement.
86 // The service Aborts the channel to indicate that the session failed. When that
87 // happens, the client gets an Exception from Close. Because Dispose is the same as
88 // Close, the client gets an Exception from Dispose as well. The close of the "using"
89 // block results in a call to client.Dispose. Because the closing brace executes
90 // regardless of whether an Exception occurred, Exceptions from Close can mask more
91 // important Exceptions from inside the "using" block.
92 static void DemonstrateProblemUsingCanThrowAndMask()
94 Console
.WriteLine("=");
95 Console
.WriteLine("= Demonstrating problem: closing brace of using statement can mask other Exceptions.");
96 Console
.WriteLine("=");
100 // Create a new client.
101 using (CalculatorClient client
= new CalculatorClient())
103 // Call Divide and catch the associated Exception. This throws because the
104 // server aborts the channel before returning a reply.
107 client
.Divide(0.0, 0.0);
109 catch (CommunicationException e
)
111 Console
.WriteLine("Got {0} from Divide.", e
.GetType());
113 throw new ObjectDisposedException("Hope this Exception wasn't important, because "+
114 "it might be masked by the Close Exception.");
116 // The following line calls Dispose on the client. Dispose and Close are the
117 // same thing, and the Close is not successful because the server Aborted the
118 // channel. This masks the ObjectDisposedException above so nobody outside the
119 // "using" block sees it.
122 catch (ObjectDisposedException
)
124 Console
.WriteLine("We do not come here because the ObjectDisposedException is masked.");
126 catch (CommunicationException e
)
128 Console
.WriteLine("Got {0}", e
.GetType());
132 // This method shows the correct way to clean up a client, including catching the
133 // approprate Exceptions.
134 static void DemonstrateCleanupWithExceptions()
136 Console
.WriteLine("=");
137 Console
.WriteLine("= Demonstrating cleanup with Exceptions.");
138 Console
.WriteLine("=");
141 CalculatorClient client
= new CalculatorClient();
144 // Demonstrate a successful client call.
145 Console
.WriteLine("Calling client.Add(0.0, 0.0);");
146 double addValue
= client
.Add(0.0, 0.0);
147 Console
.WriteLine(" client.Add(0.0, 0.0); returned {0}", addValue
);
149 // Demonstrate a failed client call.
150 Console
.WriteLine("Calling client.Divide(0.0, 0.0);");
151 double divideValue
= client
.Divide(0.0, 0.0);
152 Console
.WriteLine(" client.Divide(0.0, 0.0); returned {0}", divideValue
);
154 // Do a clean shutdown if everything works. In this sample we do not end up
155 // here, but correct code should Close the client if everything was successful.
156 Console
.WriteLine("Closing the client");
159 catch (CommunicationException e
)
161 // Because the server suffered an internal server error, it rudely terminated
162 // our connection, so we get a CommunicationException.
163 Console
.WriteLine("Got {0} from Divide.", e
.GetType());
166 catch (TimeoutException e
)
168 // In this sample we do not end up here, but correct code should catch
169 // TimeoutException when calling a client.
170 Console
.WriteLine("Got {0} from Divide.", e
.GetType());
175 // In this sample we do not end up here. It is best practice to clean up the
176 // client if some unexpected Exception occurs.
177 Console
.WriteLine("Got unexpected {0} from Divide, rethrowing.", e
.GetType());