Monday, September 24, 2007

Create A Compression extension using .NET 2.0
(original version can be found here: http://www.123aspx.com/redir.aspx?res=29459)

First of all we create a class that does all the work



public class CompressionExtension : SoapExtension {
Stream _OldStream;
Stream _NewStream;


public override object GetInitializer(LogicalMethodInfo methodInfo,
SoapExtensionAttribute attribute) {
return attribute;
}

// Get the Type
public override object GetInitializer(Type t) {
return typeof(CompressionExtension);
}

// Get the CompressionExtensionAttribute
public override void Initialize(object initializer) {
if (initializer is CompressionExtensionAttribute) {
CompressionExtensionAttribute attribute =
(CompressionExtensionAttribute)initializer;
}


return;
}

// Process the SOAP Message
public override void ProcessMessage(SoapMessage message) {
// Check for the various SOAP Message Stages
switch (message.Stage) {

case SoapMessageStage.BeforeSerialize:
break;

case SoapMessageStage.AfterSerialize:
// ZIP the contents of the SOAP Body after it has
// been serialized
Zip();
break;

case SoapMessageStage.BeforeDeserialize:
// Unzip the contents of the SOAP Body before it is
// deserialized
Unzip();
break;

case SoapMessageStage.AfterDeserialize:
break;

default:
throw new Exception("invalid stage");
}
}

// Gives us the ability to get hold of the RAW SOAP message
public override Stream ChainStream(Stream stream) {
_OldStream = stream;
_NewStream = new MemoryStream();
return _NewStream;
}

// Utility method to copy streams
void Copy(Stream from, Stream to) {
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}


// Zip the SOAP Body
private void Zip() {
_NewStream.Position = 0;
// Zip the SOAP Body
_NewStream = ZipSoap(_NewStream);
// Copy the streams
Copy(_NewStream, _OldStream);
}

// The actual ZIP method
private byte[] Zip(string stringToZip) {
byte[] inputByteArray = Encoding.UTF8.GetBytes(stringToZip);
MemoryStream ms = new MemoryStream();

// Check the #ziplib docs for more information
GZipStream zipOut = new GZipStream(ms, CompressionMode.Compress);
zipOut.Write(inputByteArray, 0, inputByteArray.Length);
zipOut.Close();

// Return the zipped contents
return ms.ToArray();
}

// Select and Zip the appropriate parts of the SOAP message
public MemoryStream ZipSoap(Stream streamToZip) {
streamToZip.Position = 0;
// Load a XML Reader
XmlTextReader reader = new XmlTextReader(streamToZip);
XmlDocument dom = new XmlDocument();
dom.Load(reader);
// Load a NamespaceManager to enable XPath selection
XmlNamespaceManager nsmgr = new XmlNamespaceManager(dom.NameTable);
nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
XmlNode node = dom.SelectSingleNode("//soap:Body", nsmgr);
// Select the contents within the method defined in the SOAP body
node = node.FirstChild.FirstChild;
// Check if there are any nodes selected
while (node != null) {
if (node.InnerXml.Length > 0) {
// Zip the data
byte[] outData = Zip(node.InnerXml);
// Convert it to Base64 for transfer over the internet
node.InnerXml = Convert.ToBase64String(outData);
}
// Move to the next parameter
node = node.NextSibling;
}
MemoryStream ms = new MemoryStream();
// Save the updated data
dom.Save(ms);
ms.Position = 0;

return ms;
}

// Unzip the SOAP Body
private void Unzip() {
MemoryStream unzipedStream = new MemoryStream();

TextReader reader = new StreamReader(_OldStream);
TextWriter writer = new StreamWriter(unzipedStream);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
// Unzip the SOAP Body
unzipedStream = UnzipSoap(unzipedStream);
// Copy the streams
Copy(unzipedStream, _NewStream);

_NewStream.Position = 0;
}

// Actual Unzip logic
private byte[] Unzip(string stringToUnzip) {
// Decode the Base64 encoding
byte[] inputByteArray = Convert.FromBase64String(stringToUnzip);
MemoryStream ms = new MemoryStream(inputByteArray);
MemoryStream ret = new MemoryStream();

// Refer to #ziplib documentation for more info on this
GZipStream zipIn = new GZipStream(ms, CompressionMode.Decompress);
Byte[] buffer = new Byte[2048];
int size = 2048;
while (true) {
size = zipIn.Read(buffer, 0, buffer.Length);
if (size > 0) {
ret.Write(buffer, 0, size);
} else {
break;
}
}
return ret.ToArray();
}

// Unzip the SOAP Body
public MemoryStream UnzipSoap(Stream streamToUnzip) {
streamToUnzip.Position = 0;
// Load a XmlReader
XmlTextReader reader = new XmlTextReader(streamToUnzip);
XmlDocument dom = new XmlDocument();
dom.Load(reader);

XmlNamespaceManager nsmgr = new XmlNamespaceManager(dom.NameTable);
nsmgr.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
// Select the SOAP Body node
XmlNode node = dom.SelectSingleNode("//soap:Body", nsmgr);
node = node.FirstChild.FirstChild;

// Check if node exists
while (node != null) {
if (node.InnerXml.Length > 0) {
// Send the node's contents to be unziped
byte[] outData = Unzip(node.InnerXml);
string sTmp = Encoding.UTF8.GetString(outData);
node.InnerXml = sTmp;
}
// Move to the next parameter
node = node.NextSibling;
}

MemoryStream ms = new MemoryStream();

dom.Save(ms);
ms.Position = 0;

return ms;
}

}


Next we create a class that will be able to use as an attribute



public class CompressionExtensionAttribute : System.Web.Services.Protocols.SoapExtensionAttribute {

private int _Priority;

// Override the base class properties
public override Type ExtensionType {
get { return typeof(CompressionExtension); }
}

public override int Priority {
get {
return _Priority;
}
set {
_Priority = value;
}
}
}



This approach involves updating the reference.cs manually. We are going to extend this in order that if we choose update webreference in Visual studion the attribute is added immediately to the proxy class. (For more detail goto: http://msdn2.microsoft.com/en-us/library/x4s9z3yc.aspx)

Friday, April 20, 2007

Create a Winforms ComboBox with a default text when nothing is selected.

Follow these easy steps to create a combox that shows a text instead of an empty line when nothing is selected


  • Create a new UserControl

  • in the code view make sure the Class Enherits from ComboBox instead of UserControl

  • In the default constructor set the DrawMode to OwnerDrawn.

  • Add two properties UnSelectedColor of type Color and UnSelectedText of type string

  • Override the OnDrawItem method


I will put a more detailed article on my personal homepage. Link will follow later.

Thanks to Richard Everett who provided me the example code

Monday, April 16, 2007

Easy way for filtering a BindingList


Found this opensource project: http://sourceforge.net/projects/blw/

As a replacement for datasets/dataTables

Tuesday, April 03, 2007

Guid vs Int

Rather interesting article on when tho use a guid or an int as a Primary Key:

http://databases.aspfaq.com/database/what-should-i-choose-for-my-primary-key.html

Summary:

IDENTITY
PROS
  • small storage footprint;
  • optimal join / index performance (e.g. for time range queries, most rows recently inserted will be on a limited number of pages);
  • highly useful for data warehousing;
  • native data type of the OS, and easy to work with in all languages;
  • easy to debug (where userid = 457);
  • generated automatically (retrieved through SCOPE_IDENTITY() rather than assigned);
  • not updateable (though some consider this a disadvantage, strangely enough).
CONS
  • cannot be reliably "predicted" by applications รข€” can only be retrieved after the INSERT;
  • need a complex scheme in multi-server environments, since IDENTITY is not allowed in some forms of replication;
  • can be duplicated, if not explicitly set to PRIMARY KEY:
    CREATE TABLE splunge(
    splungeID INT IDENTITY(1,1),
    foo CHAR(1)
    )
    GO
    SET NOCOUNT ON
    INSERT splunge(foo) VALUES('a')
    INSERT splunge(foo) VALUES('b')
    INSERT splunge(foo) VALUES('c')
    GO
    DBCC CHECKIDENT('splunge', RESEED, 2)
    GO
    INSERT splunge(foo) VALUES('d')
    GO
    SELECT * FROM splunge
    GO
    DROP TABLE splunge
    GO
  • if part of the clustered index on the table, this can create an insert hot-spot;
  • proprietary and not directly portable;
  • only unique within a single table;
  • gaps can occur (e.g. with a rolled back transaction), and this can cause chicken little-style alarms.
GUID()
PROS
  • since they are {more or less} guaranteed to be unique, multiple tables/databases/instances/servers/networks/data centers can generate them independently, then merged without clashes;
  • required for some forms of replication;
  • can be generated outside the database (e.g. by an application);
  • distributed values prevent hot-spot (as long as you don't cluster this column, which can lead to abnormally high fragmentation).
CONS
  • the wider datatype leads to a drop in index performance (if clustered, each insert almost guaranteed to 'dirty' a different page), and an increase in storage requirements;
  • cumbersome to debug (where userid = {BAE7DF4-DDF-3RG-5TY3E3RF456AS10});
  • updateable (need to propogate changes, or prevent the activity altogether);
  • sensitive to time rollbacks in certain environments (e.g. daylight savings time rollbacks);
  • GROUP BY and other set operations often require CAST/CONVERT;
  • not all languages and environments directly support GUIDs;
  • there is no statement like SCOPE_GUID() to determine the value that was generated, e.g. by NEWID();
  • there are display issues to consider, such as response.write issues (see Article #2358), and also consider cases where you display a large amount of rows as checkboxes or other form elements with IDs - your page just got a whole lot bigger, and you have to handle the IDs in a custom way because JavaScript can't deal with objects that have dashes in their name/id.

Tuesday, March 06, 2007

I came across an annoying little thing trying to trigger an event that updates the UI...

{"Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on."}


This happens because the UI runs in a specific thread. Now we create another thread and try to access the UI thread which throws the above exception. There is a solution in msdn but imo its not a good one.

Thus we need to marshal the event from one thread to the other thread as shown below.

the source (just create a new form called form1 and drag a label and a button on it):

ThreadComponent.cs


using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.ComponentModel;


namespace WindowsApplication3 {
public delegate void ShowSomethingHandler(string message);


public class ThreadedComponent {
private Thread _Thread;

public ThreadedComponent() {
_Thread = new Thread(Poll);
_Thread.Start();

}

private void Poll() {
int i = 0;
ISynchronizeInvoke invoker = ShowSomething.Target as ISynchronizeInvoke;
while(true) {
if(ShowSomething != null) {

//ShowSomething("Message N°:" + (++i));
if(!invoker.InvokeRequired) {
object[] args = {"Message N°:" + (++i)};
invoker.Invoke(ShowSomething, args);
} else {
ShowSomething("Message N°:" + (++i));
}


}
Thread.Sleep(1000);
}
}

public event ShowSomethingHandler ShowSomething;
}
}



Form1.cs

namespace WindowsApplication3 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e) {
ThreadedComponent c = new ThreadedComponent();
c.ShowSomething += new ShowSomethingHandler(c_ShowSomething);
}

void c_ShowSomething(string message) {
label1.Text = message;
}
}
}

Saturday, February 24, 2007

Finished my MCPD: Enterprise applications cerification last week

Sunday, February 04, 2007

Finally managed to solve a problem that has been bugging me for ages now. When tryinng to use the wizard for databinding to an object the Wizard gave an error :
"An unexpected error has occurred"
"Error Message: Object reference not set to an instance of an object"

The solution can be found here:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=380441&SiteId=1&PageID=3
Finished the 70-553 upgrade exam. I am now officially
MCTS: .NET Framework
  • Windows Applications
  • WebApplications