SoapHeaders to Web Services in .Net

SoapHeaders can be used to send “extra” and optional parameters to web service methods and is a technique which can be extremely useful. See the following example:

Server-side (MyService.asmx):

public class MyService : System.Web.Services.WebService
{
   public class AddDateHeader : SoapHeader
   {
      public bool AddDate;
   }
   public AddDateHeader myheader;
   ...

   [WebMethod]
   [SoapHeader("myheader")]
   public string HelloWorld()
   {
      if (myheader != null && myheader.AddDate == true)
         return "Hello World " + DateTime.Now.ToString();
      else
         return "Hello World";
   }

Client-side:

localhost.MyService si = new localhost.MyService();
si.AddDateHeaderValue = new localhost.AddDateHeader();
si.AddDateHeaderValue.AddDate = true;
MessageBox.Show(si.HelloWorld());

Furthermore, these headers can be accessed in SoapExtension components. For example, there might be a SoapHeader that controls the transport of data.

Example:

Public Overrides Sub ProcessMessage(ByVal message As SoapMessage)
    Dim compress As Boolean = False
    Dim header As SoapHeader
    For Each header In message.Headers
        If header.GetType().Name = "Compress" Then
            compress = True
        End If
    Next
    ...

Sometimes it’s useful to have the header sent in both directions (both to and from the service). In this case, apply the attribute to the web method with the Direction attribute:

[SoapHeader("myheader", Direction = SoapHeaderDirection.InOut)]
public string HelloWorld()
{
    ...

Initializing arrays

Here’s a handy way of creating an array and pass it as a parameter for a function call:

MyFunc(new Kommentar[] {kom1, kom2} );

No need for clumsy temporary variables etc. Neat, isn’t it?

Here’s another variation:

VeckoFacit[] facitarr;
facitarr = new VeckoFacit[]
{
    new VeckoFacit(...),
    new VeckoFacit(...),
    new VeckoFacit(...)
};

Initializing a multidimensional array:

int[,] intArray = new int[3, 2] { {1, 2}, {3, 4}, {5, 6} };
int[,] intArray = new int[,] { {1, 2}, {3, 4}, {5, 6} };

Cloning an object using serialization

Here’s an easy way of cloning an object (deep copy):

public T CloneObject<T>(T obj) where T:class
{
    if (obj== null)
        return null;

    MemoryStream memstream = new MemoryStream();
    IFormatter formatter = new BinaryFormatter();
    formatter.Serialize(memstream, obj);
    memstream.Seek(0, SeekOrigin.Begin);
    T clone = (T)formatter.Deserialize(memstream);
    memstream.Close();
    return clone;
}

The requirement is of course that the object is serializable and marked with the [Serializable] attribute.

XmlValidatingReader is obsolete!

The way you read XML files with validation has been changed in .Net 2.0. Before we could do this:

StringReader sr = new StringReader(xmlfragment);
XmlTextReader tr = new XmlTextReader(sr);

XmlValidatingReader vr = new XmlValidatingReader(tr);
vr.Schemas.Add("", Path.Combine( _sInstallDir, @"Schemas\schema.xsd"));
vr.ValidationType = ValidationType.Schema;
vr.ValidationEventHandler += new ValidationEventHandler(ValidationHandler);

while (vr.Read())
{
   ...
}

That still works (in Beta 2 of VS2k5) but will give “obsolete” warnings. Apparently this is the way to go now:

StringReader sr = new StringReader(xmlfragment);

XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(null, Path.Combine(_sInstallDir, @"Schemas\schema.xsd"));
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += new ValidationEventHandler(ValidationHandler);

XmlReader reader = XmlReader.Create(sr, settings);


while (reader.Read())
{
   ...
}

It’s very similar so I’m not quite sure as to why the modification has been made. Maybe to limit the number of classes in the System.Xml namespace?

Note that you can also use this method with XmlWriter. I haven’t tried that yet, though.

String.Format tips

Format a number in different ways:

// Result is rounded to 1.9, 2.0, 2.1, etc.
string snitt = String.Format("{0:.0}",total / antal);

// Fill unused space with zeroes
// 000, 001, ..., 023, ... 122, ..., 1232
string s = String.Format("{0:000}", i);

// Result is rounded to 0, 1, 2, etc.
avtal.TimpottReguljar.TimmarKvar.ToString("0");

// Right-justified text
string s = String.Format("{0,3}", i);

// Separation into 3 digit groups ("323 232"):
string s = amount.ToString("### ### ###").Trim();

Formatting date strings

Standard DateTime Format Strings

String.Format("{0:u}", DateTime.Now);
// Result: "2005-07-26 15:48:16Z"

For more codes, look up “Standard DateTime Format Strings“.

Can also be used like this:

DateTime.Now.ToString("u");

More examples:

timevar.ToString("yyyy-MM-dd HH:mm");

Custom DateTime Format Strings

String.Format("{0:%d}/{0:%M}", DateTime.Now) ;
String.Format("{0:%d'/'%M}", DateTime.Now);
// Same result for both: "26/7"

“%” is needed for these codes to guarantee that Custom codes are used. The codes ‘d’ and ‘M’ also namely also exists as Standard codes, but with other meanings.

For more codes, look up “Custom DateTime Format Strings“.

Can also be used like this:

DateTime.Now.ToString("%d'/'%M");