Standing on the shoulders of giants.
 Monday, August 11, 2008

Earlier today .NET Framework 3.5 and Visual Studio 2008 SP 1 were released. I wrote about the new features earlier and this still looks like an amazing service pack. Some of the highlights:

  1. The improved performance of the installation of the service pack compared to the vs2005 sp. And the fact that the final sp will install over the beta, no need to uninstall. Update: The final version won't install over the Beta SP, you have to run the "Service Pack Preparation Tool" first.
  2. Inclusion of the Entity Framework, this is no longer a separate download. Some of the fixes in this version of the Entity Framework include support for SQL Server 2008 and improved support for iterative development (the "Update model from database" wizard).
  3. Inclusion of ADO.NET Data Service Framework (Astoria)
  4. Improvements to DataContracts in WCF. The serializer now supports types that aren't annotated with any serialization attributes and better support for object references (and circular references) in DataContracts.

Update: the MSDN Library for Visual Studio 2008 SP1 is also available for download.

11 Aug 2008 19:47 W. Europe Daylight Time  #    Comments [0] - Trackback
Development | Visual Studio 2008

 Thursday, July 17, 2008

Understanding Windows Cardspace This book (Understanding Windows CardSpace by Vittorio Bertocci, Garrett Serack and Caleb Baker) is not a guide how to implement Windows CardSpace in your website or webservice, but this book helps you understand the reasoning behind Windows CardSpace and how it fits in the Identity Metasystem. As such it is a much better book, than a book which just explains how to add a widget to your website to authenticate users, could ever be.

The parts of the book follow a logical structure. Part 1 discusses the problems we face on the Internet: identity theft, phishing and others and a technology independent solution is proposed. Finally in part 2 CardSpace is introduced and the implementation of CardSpace (both managed and self-issued) in websites and webservices is discussed. Part 3 shows the practical and business considerations when working with the Identity Metasystem and Windows CardSpace.

Even if you're a regular reader of Vittorio's blog, and are familiar with the Seven Laws of Identity, this book still has value. If you're not familiar with one or the other, you really should read this book, since it's the first book which really made me understand the problems we face on the Internet today with respect to identity and why and how Windows CardSpace provides a solution.

With the release of Zermatt this book really has proven it's value: Zermatt makes it much easier to implement a Security Token Service and a Relying Party, but it won't help you understand the concepts behind them or why you need to implement them (or not).

17 Jul 2008 21:29 W. Europe Daylight Time  #    Comments [0] - Trackback
Development | Reading | Security

 Thursday, May 29, 2008

Since the beta of sp1 for Visual Studio and the .NET Framework has been released earlier this month, there have been a number of articles about what's included. What makes this service pack more interesting than for example the service pack for Visual Studio 2005, is that new features have been added. New features have not only been added to Visual Studio but also to the .NET Framework.

Since a lot of posts have been made about the changes and improvements, I won't compile another list. I'll just point out some of the more interesting changes. (If you are interested in a list of changes, Scott Gu has a pretty complete list.)

The most interesting changes and additions for me are:

  1. The improved performance of the installation of the service pack compared to the vs2005 sp. And the fact that the final sp will install over the beta, no need to uninstall.
  2. Inclusion of the Entity Framework, this is no longer a separate download. Some of the fixes in this version of the Entity Framework include support for SQL Server 2008 and improved support for iterative development (the "Update model from database" wizard).
  3. Inclusion of ADO.NET Data Service Framework (Astoria)
  4. Improvements to DataContracts in WCF. The serializer now supports types that aren't annotated with any serialization attributes and better support for object references (and circular references) in DataContracts.

Additional info about the service pack here (yes, more lists ;-)):

Update: Scott Hanselmann used nDepend to see what API's have changed with this servicepack. All changes are additive, so the service pack shouldn't break any existing applications. Patrick Smacchia has a complete list of changes.

29 May 2008 17:23 W. Europe Daylight Time  #    Comments [0] - Trackback
Development | Visual Studio 2008

 Monday, April 28, 2008

Recently a client wanted to import binary files with metadata into SharePoint 2007 from a directory. Because BizTalk was available and I suspected there would be some scaling issues if I would go the custom NT service route, I decided to prototype the solution in BizTalk first. The prototype was finished in just 2 days, which is a lot faster than a custom solution which is as scalable and reliable.

Customer question

The customer wanted to import scanned documents and metadata from their OCR scanners into a SharePoint document library. A scanned document consists of a PDF file and an XML file with the OCR results, both files have the same name (with a different extension) and are uploaded to a fileshare.

Solution

The solution consists of a BizTalk server which uses a File Receive Adapter to monitor a specific directory for a combination of a PDF and XML file, those are combined in an orchestration and send to a document library using the WSS Adapter.

Import Process 

The BizTalk solution consists of several pieces:

  1. A schema to describe the received XML file,
  2. An orchestration, to receive the files, create the new message and send that to the document library,
  3. A pipeline component to rename the received files (and 2 pipelines to host it),
  4. A custom component to encode the values send in the metadata.

The schema

The schema can be any valid XML-schema, but to make it easier to add the properties to the WSS message I set the type of the nodes to string and promoted them. Note that if some of the fields are nullable, we have to take care when accessing those.

The orchestration

The orchestration consist of 3 steps:

  1. Receiving the file,
  2. Creating the new message,
  3. Sending the message.

Step 1: Receiving the file

 receive

There are 2 receive ports, one for each of the file types, both match to a receive action and are able to start the orchestration. Between the receive actions exists a correlation, which correlates the received messages on their (normalized) filename. The filenames are normalized using a custom pipeline component, see below. Only when both ports have received a file the next step of the orchestration is started.

Step 2: Creating the new message

Create message

Creating the message consists of 2 steps to make it easier to organize the code. In the first step, "SetWssProperties", a global variable is filled with a string containing an XML node with the custom properties send to SharePoint. All values are encoded using a custom component, the PropertyEncoder, see below.

WssProperties = "" 
+ "Team" 
+     ""+ Paulb.PropertyEncoder.Encode( InvoiceXml.fields.Team ) + "" 
+     "Client" 
+     ""+ Paulb.PropertyEncoder.Encode( InvoiceXml.fields.Client )+ "" 
+     "Document_x0020_type" 
+     ""+ Paulb.PropertyEncoder.Encode( InvoiceXml.fields.DocumentType )+ "" 
+    "Gross_x0020_amount" 
+     ""+ Paulb.PropertyEncoder.Encode(InvoiceXml.fields.TotalAmount) + ""; 
if( false == System.String.IsNullOrEmpty(InvoiceXml.fields.InvoiceDate)) {
      WssProperties = WssProperties + "Invoice_x0020_Date" 
      + ""+ InvoiceXml.fields.InvoiceDate + ""; 
}

WssProperties = WssProperties +    "Invoice_x0020_Number" 
+     ""+ Paulb.PropertyEncoder.Encode( InvoiceXml.fields.InvoiceNumber )+ "" 
+    "ScanID" 
+     ""+ Paulb.PropertyEncoder.Encode( InvoiceXml.fields.ScanID )+ "" 
+    "Vat_x0020_number" 
+     ""+ Paulb.PropertyEncoder.Encode ( InvoiceXml.fields.VatNumber )+ "" 
+ ""; 

In the second step, the incoming PDF message is copied to the outgoing WssMessage and it's properties are set. The filename is encoded using the custom ProperytEncoder component to meet the requirements for a filename in a SharePoint Document Library.

InvoicePdfMetaData = InvoicePdf;
InvoicePdfMetaData(WSS.Filename) = "FLT" + Paulb.PropertyEncoder.EncodeFileName(InvoiceXml.fields.ScanID) + ".pdf";

InvoicePdfMetaData(WSS.ConfigPropertiesXml) = WssProperties;

Step 3: Sending the message

Send Message

Sending the message is the final and simplest step: create a Send object and link it to the port. The configuration of the port is done using BizTalk Administration Console after deploying the solution to the BizTalk server.

The Pipeline

The solution involves 2 receive pipelines, one for each of the filetypes (xml and pdf). It is necessary to create 2 pipelines because the pipeline for the xml file requires the XML disassembler to recognize the schema and match it to the receive action. Both pipelines contain the custom FixFileName component, which normalizes the filename for the incoming file (it removes the path and the extension).

The actual work is done in the Execute method of the IComponent interface, the remaining interfaces either must be implemented, but are not used (IPersistPropertyBag) or are used by the designer environment (IComponentUI). The IBaseComponent interface supplies basic information about the component, such as Name and Version.

IBaseMessage IComponent.Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{

    // make sure we have something to work with
    if (pInMsg == null)
    {
        return pInMsg;
    }

    string receivedFileName = pInMsg.Context.Read("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties") as string;
    if (string.IsNullOrEmpty(receivedFileName))
    {
        // nothing we can do
        return pInMsg;
    }

    string newFileName = Path.GetFileNameWithoutExtension(receivedFileName);

    pInMsg.Context.Promote("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties", newFileName);

    return pInMsg;
}

One thing to remember, is to Promote the property you're writing otherwise you're not able to use it for routing.

Another thing about pipeline components is: BizTalk expects them to be deployed to a specific location on the filesystem, the Pipeline Components directory of the BizTalk installation directory (the default location is: C:\Program Files\Microsoft BizTalk Server 2006\Pipeline Components).

FixFileName.cs

Custom component for encoding properties

The component for encoding the properties is a standard .NET assembly. It is not necessary to implement any interfaces, all assemblies work when they are referenced in the BizTalk orchestration project. The only requirement is that the assembly is deployed to the GAC.

This project required some additional encoding of the XML values, beside the normal HTML encoding, to encode the characters used by BizTalk macros and to strip characters which are illegal in filenames in a document library.

PropertyEncoder.cs

See also:

Information about convoys

Developing pipeline components

28 Apr 2008 22:25 W. Europe Daylight Time  #    Comments [0] - Trackback
BizTalk | Development | Sharepoint

 Wednesday, November 14, 2007

PIC-0004

A nice surprise today when the mailman brought 4 new books:

  1. Programming Pearls, 2nd Edition by Jon Bentley
  2. The No Asshole Rule by Robert I. Sutton, PhD
  3. Mastering Regular Expressions, 3rd edition by Jeffrey E.F. Friedl
  4. Code Complete, 2nd Edition by Steve McConnell

1,2 and 4 I consider required reading for any serious developer, allthough I have only read Code Complete myself.

Now the only thing left is to actually read all the books I have collected in the last couple of weeks.

14 Nov 2007 17:10 W. Europe Standard Time  #    Comments [0] - Trackback
Development | Reading

 Monday, August 20, 2007

I was supprised with the last results from the XmlWriters and compression stream performance post and figured I did something wrong. So today I re-ran my test and got the results I expected after some data manipulation. The results aren't shocking; writing to a memory stream is about 8% faster when you increase the intialcapacity from 256 bytes to 131072 bytes, further increasing doesn't improve the performance further. Naturally this depends on the data, only 6 xmldocuments I used in this test were greater than 131072 bytes.

Relative performance with increasing initial capacity

 

Methodology:

I saved each xml document to the memorystream 100 times for each initial capacity and removed the 10 best and 10 worst results. The results are relative to the performance of the stream with an initial capacity of 256 bytes.

Notes:

My data is not your data; always do your own testing to determine the 'best' initial capacity for your situation.

20 Aug 2007 22:15 W. Europe Daylight Time  #    Comments [0] - Trackback
Development

 Friday, May 11, 2007
 Wednesday, December 27, 2006

 ... or why does your tool introduce bugs in my code and doesn't warn me?

When migrating an ASP.NET 1.1 project to 2.0, using the VS 2005 (SP1) conversion wizard and the Web Application Project conversion tool (included in service pack 1), the AutoEventWireup @page attribute is set to "True" for all pages, even when you previously had set it to "False".

This potentially breaks your application, when you have eventhandlers for init/load/prerender bound manually in places where the conversion tool doesn't change them, e.g. in the constructor of a common basepage. This will cause the events to be handled twice, with unexpected results.

The problem is not that the default is changed from 1.1 to 2.0, but that by modifying my code the tool introduces new (interesting) bugs. And all this without a big neon-sign, which warns for the changes being made and the potential problems it introduces.

27 Dec 2006 17:01 W. Europe Standard Time  #    Comments [0] - Trackback
Asp.Net | Development

 Thursday, October 05, 2006

"If you are designing a piece of tech gear - be it hardware or software - and you need to choose an arbitrary limit or range for something, make sure the limit is either 2^n or 2^n - 1."

Source: Eric Gunnerson

05 Oct 2006 15:21 W. Europe Daylight Time  #    Comments [0] - Trackback
Development | Fun

 Saturday, August 19, 2006

Frank just started his new blog and his first post details how to work with control adapters in SPS2007. Very good stuff.

19 Aug 2006 14:18 W. Europe Daylight Time  #    Comments [0] - Trackback
Development | Sharepoint

 Thursday, August 17, 2006

It seems VS 2003 has been out forever, but sp1 has been released for download yesterday. I never experieced any real problems with it, but it's great to see the DDCPX team is still working on improving its stability.

Download here: VS 2003 SP1 and release notes

List of Fixes: KB 918007

17 Aug 2006 11:48 W. Europe Daylight Time  #    Comments [0] - Trackback
Development

 Wednesday, August 16, 2006

Although there are several ways in ASP.Net to influence the way DateTimes are displayed some methods are better than others and some are evil.

The easiest way to display DateTime and have some influence on them, is to specify the culture and the uiCulture in the globalization tag in the web.config. This is the preferred way if you can use the default date/time formats in that culture.

Example:

<%=DateTime.Now %> is rendered as "16-8-2006 10:14:49" with this globalization tag <globalization uiCulture="nl-NL" culture="nl-NL" /> and rendered as "8/16/2006 10:18:37" with the culture and uiCulture set to en-US.

If you want a little more control, or when the default date/time formats don't offer enough flexibility you can specify a format in the ToString method of the DateTime object. This will still use the culture specified in the web.config for the (abbreviated) names of the months. The downside to this method is that you have to remember to set the correct format string, every time you want to display a date/time.

Example:

<%=DateTime.Now.ToString( "dd MMM \\'yy" )%> is rendered as "16 août '06" with the culture set to fr-FR ( the \\ are required for escaping the ' ).  

 When you want to offer your users a localized version of your web-application, you can't rely on the globalization settings in the web.config, instead you'll have to either set the Culture and UICulture of the current executing thread or use one of the ToString overrides that accepts an IFormatProvider. When you only specify an IFormatProvider, the default (or G) format is used to render the string and although you can override the default format, that is generally considered evil.

Example:

<%=DateTime.Now.ToString( new System.Globalization.CultureInfo("es-ES")) %> is rendered as "16/08/2006 11:43:11", while <%=DateTime.Now.ToString("dd MMM \\'yy", new System.Globalization.CultureInfo("es-ES")) %> is rendered as "16 ago '06".

These methods were the only methods that were available on version 1.x of the .Net framework. In .Net 2.0 you have the ability to define a custom culture using the CultureAndRegionInfoBuilder class. After creating a new culture, this new culture can be installed on a computer and used by any application on that computer. This allows all (web-)applications on that computer to use the same formatting for dates, and numbers, casing etc. This way you can change the default format for string rendering in a less evil way.

Example:

First setup the custom culture using a console application, since this requires administrator rights.

class Program{ 

static void Main( string[] args ) {

if (args.Length == 0 || args[0].EndsWith("i", StringComparison.OrdinalIgnoreCase)) {

RegisterNewRegion();
Console.WriteLine("Registered new region \"x-en-US-custom\".");
} else if (args[0].EndsWith("u", StringComparison.OrdinalIgnoreCase)) {
UnRegisterNewRegion();
Console.WriteLine("Unregistered new region \"x-en-US-custom\".");
} else {
Console.WriteLine("use /i to register and /u for unregister.");
}

Console.WriteLine("Press enter.");
Console.ReadLine();
}

static void RegisterNewRegion() {

// instantiate a new CultureAndRegionInfoBuilder object named x-en-US-demo
CultureAndRegionInfoBuilder cib = new CultureAndRegionInfoBuilder("x-en-US-custom", CultureAndRegionModifiers.None);
//load the culture data from the default en-US culture and region
cib.LoadDataFromCultureInfo(new CultureInfo("en-US"));
cib.LoadDataFromRegionInfo(new RegionInfo("US"));
// set the custom date/time format
cib.GregorianDateTimeFormat.ShortDatePattern = "dd-MM-yy";
cib.GregorianDateTimeFormat.LongDatePattern = "dd MMM \\'yy";
cib.GregorianDateTimeFormat.ShortTimePattern = "HH:mm";
cib.GregorianDateTimeFormat.LongTimePattern = "HH:mm";

// register the culture
cib.Register();
}

static void UnRegisterNewRegion() {

CultureAndRegionInfoBuilder.Unregister("x-en-US-custom");
}

}

Now you can use your custom culture in any web-application, like the previous examples.

<%=DateTime.Now %> is rendered as "16-8-2006 10:14:49" with this globalization tag <globalization uiCulture="x-en-US-custom" culture="x-en-US-custom" />

Another use would be for writing dates to a log-file, when you need to sort or parse them in future.

Thanks to michkap from Sorting It All Out for the tip on CultureAndRegionInfoBuilder and the post that inspired this post.

16 Aug 2006 13:59 W. Europe Daylight Time  #    Comments [0] - Trackback
Asp.Net | Development

 Thursday, January 12, 2006

Well, maybe...

 

public class GenericComparer : IComparer {

public GenericComparer( CompareDelegate doCompare ){

if( doCompare == null ){

throw new ArgumentNullException("doCompare");

}

this.doCompare = doCompare;

}

public int Compare( object x, object y ){

if( x == null ){

if( y == null ){

return 0;

}

return -1;

}

if( y == null ){

return 1;

}

return doCompare(x,y);

}

private CompareDelegate doCompare;

}

public delegate int CompareDelegate( object x, object y );

Update:
My colleague Branimir did a little experimentation and found this implementation is similar in performance to a 'regular' methodcall when sorting around 10.000 items.

LastComparer.zip (30.65 KB)
12 Jan 2006 13:42 W. Europe Standard Time  #    Comments [0] - Trackback
Codesnippet | Development

 Wednesday, January 04, 2006

See also: A horrible way to calculate a squareroot

using System;
using System.Query;

namespace SquareRoot
{
class Program
{
static void Main(string[] args)
{
int input = 16;
decimal margin = 0.01M;

Func<decimal, decimal> abs = (decimal x) =>
x < 0 ? -x : x;

Func<decimal, bool> goodEnough = (decimal guess) =>
abs(guess * guess - input) < margin;

Func<decimal, decimal> newGuess = (decimal guess) =>
(guess + input / guess) / 2;

Console.WriteLine( "Guess {0}", Try(1, goodEnough, newGuess) );
Console.ReadLine();
}

static decimal Try(decimal guess, Func<decimal, bool> goodEnough, Func<decimal, decimal> newGuess)
{
if (!goodEnough(guess))
{
return Try(newGuess(guess), goodEnough, newGuess);
}

return guess;
}
}
}

04 Jan 2006 14:32 W. Europe Standard Time  #    Comments [0] - Trackback
Development

 Monday, January 02, 2006

But fun to write:

using System;

class MyClass {
public static void Main() {

int input = 16;
decimal margin = 0.01M;

Func<decimal, decimal> abs = delegate(decimal x) {
return x < 0 ? -x : x;
};

Func<decimal, bool> goodEnough = delegate(decimal guess) {

return abs(guess * guess - input) < margin;
};

Func<decimal, decimal> newGuess = delegate(decimal guess) {
return (guess + input / guess) / 2;
};

Console.WriteLine("guess {0}", Try(1, goodEnough, newGuess) );
Console.ReadLine();
}

static decimal Try(decimal guess, Func<decimal, bool> goodEnough, Func<decimal, decimal> newGuess) {

if (!goodEnough(guess)) {
return Try(newGuess(guess), goodEnough, newGuess);
}

return guess;
}


delegate ReturnType Func<U, ReturnType>(U guess);
}

Inspired by Joel and Chris.

02 Jan 2006 14:37 W. Europe Standard Time  #    Comments [0] - Trackback
Development

 Wednesday, December 07, 2005

The lowest level useful description of the goal of any software is probably best put as "elicit a positive emotional response
in a human".

2005 – DJ Mort

07 Dec 2005 17:13 W. Europe Standard Time  #    Comments [0] - Trackback
Development

 Tuesday, December 06, 2005
Stef wrote an extension to NDoc, which can be used to create XmlDoc files for your database.
06 Dec 2005 20:47 W. Europe Standard Time  #    Comments [0] - Trackback
Development

 Wednesday, September 21, 2005

My highlights of the PDC 05:

  • Linq and the associated C# 3.0 features
  • IIS 7.0, plugable modules and xcopy deployment of configurations.
  • Windows Workflow Foundation

Expect more indepth articles in the coming weeks, when I’ve had some time to experiment with everything.

21 Sep 2005 10:56 W. Europe Daylight Time  #    Comments [0] - Trackback
Development | PDC

 Wednesday, July 27, 2005

public static string HashPasswordForStoringInConfigFile(
   string password,
   string passwordFormat
);

In the FormsAuthentication class in the System.Web.Security namespace.

The first parameter is the string you want hashed, the second is the hashalgorithm, “sha1” or “md5”, to use.

[more info on msdn]

27 Jul 2005 17:22 W. Europe Daylight Time  #    Comments [0] - Trackback
Codesnippet | Development

 Monday, July 18, 2005

SeaMen screenshot

Mart Muller: There is a lot of talk about AJAX lately. It's not new but there are a lot of cool things that you could do with it. Some guys at Tam Tam dove into it and gave a session about it last week. One of the "demo's" they've built is "Seamen - Massive Multiplayer Battleships". A cool AJAX example I do not want to keep away from you!

[note: it’s kinda slow right now, but that’s probably due to the hosting, it’s hosted from a home-adsl connection.]

18 Jul 2005 10:34 W. Europe Daylight Time  #    Comments [0] - Trackback
Development | Fun

 Friday, July 15, 2005

Returning an ArrayList is as bad or worse as returning an object from most methods, so don’t. When you return an ArrayList, anything could be in there, so I have to try and read your code, or hope your documentation is still correct, to find out what’s in there. A much nicer and more type-safe solution is to return an Array of the objects in your ArrayList, which is only 1 line of code more.

    public ArrayList BadNumbers{
        get{
            return this.items;
        }
    }
    
    public int[] GoodNumbers{
        get{
            if( numbers == null ){
                // it's not nice to return a null reference, when
                // an Array is expected.
                // That would mess up your foreach loop badly.
                return new int[0];
            }
            
            // conversion is done here instead of at the call site
            return (int[])numbers.ToArray(typeof(int));            
        }
    }
    
    private Arraylist numbers = new ArrayList();

15 Jul 2005 11:33 W. Europe Daylight Time  #    Comments [1] - Trackback
Codesnippet | Development

 Thursday, June 23, 2005

Interesting and annoying behaviour of Asp.Net with respect to HttpModules; the httpModules element in a web.config in a subdirectory, (note: not a seperate application) are ignored. No warning, no error, no nothing, just ignored.

After I spend the better part of yesterday afternoon working on this problem and google wasn’t very helpfull, I hope this will be helpfull to someone.

Proof of concept:

Solution

Layout of the project, notice the second web.config in the subdirectory.

HttpModules element in the root web.config:

<httpModules>

         <add name="FirstHttpModule" type="httpModule.HttpModules.FirstHttpModule, httpModule" />

</httpModules>

Interesting part of the FirstHttpModule class

private void context_BeginRequest(object sender, EventArgs e) {
    HttpApplication source = sender as HttpApplication;
    if( source != null ){
        source.Context.Response.Write( "Hello from FirstHttpModule. " );
    }
}

HttpModules element in the subdirectory web.config:

<httpModules>

<clear />

         <add name="SecondHttpModule" type="httpModule.HttpModules.SecondHttpModule, httpModule" />

</httpModules>

Interesting part of the SecondHttpModule class:

private void context_BeginRequest(object sender, EventArgs e) {

private void context_BeginRequest(object sender, EventArgs e) {
    HttpApplication source = sender as HttpApplication;
    if( source != null ){
        source.Context.Response.Write( "Hello from SecondHttpModule. " );
    }
}

Result of a request to webform1.aspx in the root:

FirstModule

Result of a request to webform1.aspx in the subdirectory:

SecondModule

 

Source for the proof of concept: httpModule.zip (17.17 KB)
23 Jun 2005 15:53 W. Europe Daylight Time  #    Comments [1] - Trackback
Asp.Net | Development

 Friday, May 20, 2005

Mart is experimenting with WSS 2.0 on windows 2003 R2, he just posted his first article on the installation.

20 May 2005 11:24 W. Europe Daylight Time  #    Comments [0] - Trackback
Asp.Net | Development

 Sunday, May 15, 2005

When serializing a class with an event, all classes subscribing to that event have to be serializable aswell. Usually you don't have any control over the classes subscribing to that event, or you don't want them to be remoted in the first place.

By adding the NonSerialized attribute to the event, with the field keyword, the field that holds the delegate for the event is stored is not serialized. So those subsribers no longer need to be serializable.

// ===============================================================================
// Copyright (C) 2005 Paul van Brenk
// All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR
// FITNESS FOR A PARTICULAR PURPOSE.
// ==============================================================================
// Serializing a class with an event sample.
// ==============================================================================


using System;

namespace Paulb.CodeSnippets
{
    [Serializable]
    public class SerializableClass
    {
        public SerializableClass()
        {
            ///
        }
        
        // adding the NonSerialized attribute this way,
        // we don't serialize the field where the delegate for this event is stored,
        // that way those subscribers don't need to be serializable.
        [field: NonSerialized]
        public event EventHandler Event;
    }

    /// <summary>
    /// This class can not be serialized, since the serializable attribute is missing.
    /// </summary>
    public class NotSerializedClass{

        public NotSerializedClass(){
        
            serializable.Event +=new EventHandler(serializable_Event);
        }

        private SerializableClass serializable = new SerializableClass();

        private void serializable_Event(object sender, EventArgs e) {
            // handle event here
        }
    }
}

Some background on how events are compiled from "The C# programming language" p330:

"When compiling a field-like event, the compiler automatically creates storage to hold the delegate and created accessors for the event that add or remove event handlers to the delegate field."

This results in the pseudo code generated for the SerializableClass:

.class public auto ansi serializable beforefieldinit SerializableClass
extends object
{
.event [mscorlib]System.EventHandler Event
{

// event accessors

.addon instance void Paulb.CodeSnippets.SerializableClass::add_Event([mscorlib]System.EventHandler)
.removeon instance void Paulb.CodeSnippets.SerializableClass::add_Event([mscorlib]System.EventHandler)
}


.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
}

// delegate storage
.field private [mscorlib]System.EventHandler Event

}


disclaimer: Use at your own risk. This code is not threatsafe. Bugs, omissions let me know.

SerializingSample.cs (1.42 KB)
15 May 2005 16:19 W. Europe Daylight Time  #    Comments [0] -