/*
This file accepts an XML message, does required processing, and returns JSON
back to the client. This file uses .NET tracing to monitor the success of
the usage of the service.
==.NET Tracing==
One of the most interesting things you can do in .NET in general is implement tracing. With
.NET tracing you can send messages to various places for various purposes in a multicast way.
Perhaps you want all errors in a certain potential exceptions in your applications to be logged
to the event log at the same time they are sent via a web service and you want the ability to
easily add more ways the same information is saved. For example, you may want to have the
ability to quickly have these same messages sent to you via e-mail.
The tracing model in .NET is much like the event model in that you have a publisher and
potentially multiple subscribers. In the tracing model, however, the subscribers are called
trace listeners and the publisher is simply a call to a method in the System.Diagnostics.Trace
class. By adding more subscribers, you are simply giving the tracing model one more channel
through which to send the same message.
Tracing also allows you to control how severe you want your logging to be with no changes to
the application itself by allows you to set tracing levels via a trace switch in the
application's configuration file. So, for example, if you normally log all errors to a
database you can set your normal tracing level to be low, but when your company has a major
sales event going on you can set your tracing level to be high to ensure you receive instance
notifications in e-mail when errors occur.
Tracing is setup in your web.config file by adding a trace listener object as an XML element
under the listeners collection under the trace element in the system.diagnostics section of
your configuration file. Here is an example of a trace listener that sends messages to your
event log:
Now, anywhere in your application you can make a call to System.Diagnostics.Trace.Write to send
a message to your event log. So, by the following single line of code you are writing to the
event log:
System.Diagnostics.Trace.Write("This will be logged in the event log.");
By adding more trace listeners to the listeners collection in the web.config file, that same
single line of code will send your message to multiple channels.
You can also write your own trace listeners by creating a class and inheriting from
System.Diagnostics.TraceListener and implementing two required abstract methods: Write and
WriteLine. You may then add these to your existing listeners collection and when you call the
Write or WriteLine methods in the System.Diagnostics.Trace class, your trace listener will act
as another publisher sending the message along. This comes in extremely handy when you want to
log messages to a web service, an XML file, to an FTP site, or via e-mail.
Setting the tracing levels is also rather simple, but it does require thought ahead of time.
Obviously, .NET isn't going to know what you consider to have a high priority or what to
consider low. You have to tell it what you mean or it won't have the first clue what you
want. So, you will need to change your Write and WriteLine statements to WriteIf and
WriteLineIf statements. Before we get into that though, it's rather important to understand
what the different tracing levels are.
The tracing levels are set as an object in the switches collection in the system.diagnostics
branch of your application's configuration file. The tracing level object you create in the
configuration file has a name which you think up and a value which states the tracing level.
The tracing levels go from 0 to 4 with increasing verbosity. So, 0 would give no messages and
4 would give everything. In normal operations, a setting of 1 would normally be sufficient as
that setting means to send error messages and nothing else. Having said all that, those are
just labels and you can use them for different purposes. This will make more sense when you
see how use tracing levels. For now, look at the XML for setting a switch:
It doesn't get much simpler than that. To access this you create a
System.Diagnostics.TraceSwitch object passing in the name of the switch with a description.
All the mechanics of accessing the configuration file are done fore you. Here is an example:
TraceSwitch ts = new TraceSwitch("TracingSwitch", "Global Tracing Switch");
One of the nice things about the TraceSwitch class is that it doesn't blow up if you forget to
add the specified trace switch in the configuration file, it simply defaults to 0. There's
also another constructor that has a third parameter which allows you to set the fault tracing
level if there isn't one set. This is rather nice if you want to always want to work in a
certain way and only want to use your configuration file to setup special circumstances or
disable tracing.
The only thing left is the actual tracing, which is done with the WriteIf and WriteLineIf
methods. These methods are the same as the Write and WriteLine methods exception that they are
conditional on the an argument you give it. The first argument of the WriteIf and WriteLineIf
method is a boolean which you would normally get from your trace switch. This is the confusing
part. Look at the following code in combination with the previously shown trace switch section
in the web.config file:
TraceSwitch ts = new TraceSwitch("TracingSwitch", "Global Tracing Switch");
System.Diagnostics.Trace.WriteIf(ts.TraceError, "taco");
In this example, the WriteIf method would have a boolean value of true in the first argument
because the TraceSwitch objects knows its place in the world and it knows how to do its job.
It's the job of the TraceSwitch object to answer the question "Are you at at least a tracing
level of 1?" The TraceSwitch objects look at the trace switch in the web.config and returns
either a boolean true or a boolean false. The TraceSwitch object has a property for each of
the different possible active trace levels. For example, the TraceWarning property maps to the
tracing level of 3 and would return false in this case, but would return true if our trace
level in our configuration file would have a trace level of 2, 3, or 4. There's also a
TraceInfo, which returns true for values of 3 and 4 and a TraceVerbose which only returns true
for a value of 4.
As I've stated earlier, the names are just labels and you can use them for whatever you want.
For example, instead of thinking of the 0 through 4 as being for Off, Errors, Warnings,
Informational Messages. and Verbosity you can just think of them merely as levels of severity.
So, for example, you if you have extremely critical parts of your system and you have to be
notified when there is an error in this part of this system. You may classify this as a
"level 1 error" and set your tracing level to 1. You may also classify other parts of the
system as being not as important, but you still want to watch their errors. In this case you
might classify these as "level 3 errors". So, in normal operation, you might keep your
tracing level at 1, but for major company events where you will be rather paranoid about each
part of the system, you might set the system's tracing to 3. So, even though your code will
be testing for TraceInfo, which maps to a level 3 trace level, there's nothing information
about those messages, you are monitoring an important part of the system, it's just not as
critical as a level 1 trace level.
.NET tracing is powerful feature that provides a solution to many notification problems today,
however, for even more power in large-scale applications, I would recommend using Microsoft's
Enterprise Library which includes a very powerful component for exception handling and another
very powerful component for logging, which can work in conjunction with the exception handling
component. The idea's in these components basically extend the idea of .NET tracing to an
extreme level.
Though this file uses .NET tracing, the purpose of this particular file is actually not to
setup or create any trace listeners, but it is to demonstrate how you can create a manual
service using XML Serialization.
For more information on JSON, please read Code/SampleDomManipulator.js.
For more information on creating manual services in .NET by using XML Serialization, see my
article on XmlHttp Service Interop (part 3) at the following link:
http://www.netfxharmonics.com/2007/05/XmlHttp-Service-Interop-Part-3-XML-Serialization.aspx
If you are completely unfamiliar with XmlHttp interop, see part 1 of the same series. For
information on using the techniques mentioned here for interop with Windows Communication
Foundation (WCF), then see part 1 of the same series.
*/
using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using Newtonsoft.Json;
using Sample;
using Sample.Service.Messages;
namespace SampleWebsite.Services
{
public partial class SampleAsyncSchemaService : System.Web.UI.Page
{
public void Page_Load(Object sender, EventArgs ea) {
SaveSampleListMessage message = null;
Byte[] buffer = new Byte[Request.InputStream.Length];
Request.InputStream.Read(buffer, 0, (Int32)Request.InputStream.Length);
MemoryStream s1 = new MemoryStream(buffer);
Response.ContentType = "text/plain";
try {
XmlSerializer s = new XmlSerializer(typeof(SaveSampleListMessage));
message = (SaveSampleListMessage)s.Deserialize(s1);
}
catch {
String error = "{ error: 'Invalid code' };";
Response.Write(error);
System.Diagnostics.Trace.WriteIf(SampleConfiguration.GlobalTraceSwitch.TraceInfo, error);
return;
}
if (message == null) {
String error = "{ error: 'Invalid message' };";
Response.Write(error);
System.Diagnostics.Trace.WriteIf(SampleConfiguration.GlobalTraceSwitch.TraceError, error);
return;
}
Response.Write(ProcessRequest(message));
}
public String ProcessRequest(SaveSampleListMessage message) {
SaveSampleListMessageResponse response = new SaveSampleListMessageResponse( );
// TODO: Process message somehow and obtain some data back.
response.StatusMessage = "Success";
response.ListId = 37319;
return JavaScriptConvert.SerializeObject(response);
}
}
}