27 October 2005

Anti Intellisense with Test Driven Development

"I don’t think IntelliSense[note 1] is helping us become better programmers. The real objective is for us to become faster programmers, which also means that it’s cheapening our labor. " -- Charles Petzold A Talk Delivered at the NYC .NET Developer’s Group, October 20, 2005

I have to agree with Charles that Intellisense changes the way we code, it turns design and implementation solution to be kindof conversation with development environment and hence slave to it.

Luckily, here comes rescue: Test Driven Development. With it you can actually start your implementation with the requirement, demand and the burning need to fix the broken test case. You actually start with a question 'what do I want to achieve by doing this?' You then start with using a method, a member variable before had it defined. And with Re-Sharper, you can easily turn the undeclared member into a skeleton you can implement later.

Because they are undeclared when first used, Intellisense won't pop up.

Hurray!

Object Invocation Earliy binding vs Late binding


1) Use early binding – so the types are known at compile time.

2) Use Remoting Activator – under the hood, it is Reflection


//caller
Object instance = CreateInstanceUseReflection(myType, newArgs);
… …
private object CreateInstanceUseReflection(Type type, object[] args)
{
object instance = type.InvokeMember("",
BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance,
null,
null,
args);
return instance;
}

3) Use Reflection directly

//caller
Object instance = CreateInstanceUseRemoting(myType, newArgs);
… …
private object CreateInstanceUseRemoting(Type type, object[] args)
{
return Activator.CreateInstance(type, args);
}


The test

I stress it will a search on 10,000 records against a 2 million records DB. And here is the result: (in milliseconds; timing on average of three runs each.)

use Remoting Activator 33855.69480
use Reflection 33422.52055
use Early Binding 31251.81840

Interesting to know.

25 October 2005

Lesson learnt: Implement IXmlSerializable to override Wsdl definition (Part II)

It is fine to override IXmlSerializable if you only intend to use XmlSerializer. If you want to generate web service wsdl definition as well, there is a problem. Our http://MyNamespace.Jingye.com/SomeWebService.asmx?wsdl gives you something like this:


<s:element minOccurs="0" maxOccurs="unbounded" name="Foo">
<s:complexType>
<s:sequence>
<s:any namespace="http://mynamespace.jingye.com /Foo" />
</s:sequence>
</s:complexType>
</s:element>


<s:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace=" http://mynamespace.jingye.com" id="Foo">
<xs:element name="Foo">
<xs:complexType>
<xs:sequence>
<xs:element name="MemberVar" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</s:schema>

Although look like we have the schema definition for Foo and would be able to use it to validate a Foo object and provide type info for de-serialisation. It is not. Any short-circuits our wsdl tool to combine external and internal schemas into one service description. If the message object is of a different XML namespace than the service, duplicate schema information is generated in the subsequent WSDL file (one for the correct XML namespace, and one for the service). If your message object is of the same XML namespace as the service, then the WSDL generation fails because the document can't contain the same namespace twice. In either of these cases, you do not get the behaviour that you are looking for. Article WSE 2.0: Give Your Web Services Consumers the Exact XML They Need to Succeed explains this.

It seemed that .NET Framework 2.0, this problem is solved by allowing the object to return an element of a schema instead of an entire schema document, which makes it possible to merge schemas during the WSDL generation step. Read this "New Features for Web Service Developers in Beta 1 of the .NET Framework 2.0".

24 October 2005

Lesson learnt: Implement IXmlSerializable to override Wsdl definition (Part I)

This is part one of implementing IXmlSerializable to customised object serialization.

Overriding WSDl definition is easy. You just need to implement IXmlSerializable for your type definition. IXmlSerializable contains three methods that is used by wsdl serialisation.
GetSechma: Implement this method to supply our custom schema definition.
WriteXml: Implement this method to write xml fragment
ReadXml: Tells your object how to de-serialise xml doc into an object of this type.

Here is a sample:


public class Foo: IXmlSerializable
{
private string _memberVar;

/// default constructor used by XmlSerializer
public Foo(){ }


XmlSchema IXmlSerializable.GetSchema()
{
Stream s = null;
try
{
s = base.GetSchema(typeof (Foo), "Foo.xsd");
s.Position = 0;
s.Flush();
XmlSchema schema = XmlSchema.Read(s, null);
schema.Compile(null);
return schema;
}finally
{
s.Close();
}
}

// ... if use default behaviour for serialization
// [XmlElement("MemberVar")]
// public String MemberVar
// {
// get {return _memberVar;}
// set { //serializer requires this }
// }

/// we do not implement this method because we only intend to use it for serialisation (from object to xml data)
void IXmlSerializable.ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}

void IXmlSerializable.WriteXml(XmlWriter writer)
{
//
// dwiohfjwhfjekfhjsdkhf
//


// Do somthing to load _membervar?
writer.WriteElementString("MemberVa", _memberVar);

//WARNING: Don't close the writer here! Let serilizer do it for you!!!
//writer.Close();
}
}
}


Foo.xsd is supplied as an embedded resource:

<xs:schema id="Foo" targetNamespace="http://mynamespace.jingye.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="Foo">
<xs:complexType>
<xs:sequence>
<xs:element name="MemberVar" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

This would serialize a Foo object to:

<Foo>MemberVar>Some Data</MemberVar>l</Foo>

And the parent element (Say ‘Bar’) of Type Foo could have WriteXml like this

public class Bar : IXmlSerializable
{
public Foo[] Item;
...
public void WriteXml(XmlWriter writer)
{
foreach (Foo r in Item)
{
writer.WriteStartElement("Foo");
((IXmlSerializable)r).WriteXml(writer);
writer.WriteEndElement();
}
}

This would serialize a Bar object to:

<Bar>
<Foo><MemberVar>Some Data 1</MemberVar></Foo>
<Foo><MemberVar>Some Data 2</MemberVar></Foo>
</Bar>


So when does serialisation happen, when WriteXml and ReadXml gets called?

WriteXml is called when XmlSerializer is to marshal an object, you can verify this by knock off a quick (nunit) test case like this:

[Test]public void Test()
{

XmlDocument expectedXml = new XmlDocument();
//Build your expected result
///...

Foo aFoo = new Foo(...); //if your constructor takes param to initialize memberVar

XmlSerializer xs = new XmlSerializer(typeof (Foo));

xtwriter.Formatting = Formatting.None;


// WriteXml() is called here
xs.Serialize(xtwriter, aFoo);

XmlDocument doc = new XmlDocument();
xtwriter.Flush();
ms.Position = 0;
ms.Flush();
doc.Load(ms);

Assert.AreEqual(expectedXml.OuterXml, doc.OuterXml);
}

GetSchema is called when wsdl is invoked to generate the type definition. This could be using http://MyNamespace.Jingye.com/SomeWebService.asmx?wsdl or by using MS wsdl tool at command line to generate client proxy class.

However, if you do either of that, and if you look carefully, you will see a problem. I will talk about it in the second part.

06 October 2005

Passing double/single quote in Nant task

Tasks like in Nant doesn't like nested quotes. If you want to pass a commandline argurement that contains double quotes (for space in directory names need double quotes around it), you will have small problem.
For example, you cann't do this:


<exec workingdir="c:workingdir\bin" basedir="c:\executableDir" program="sar.exe" commandline="/s:ContentDB.config /d:..\..\ContentDB.config /f:"my file contains space" /i" />

Have tried a few ways to escape the double quote, not very sucessful. Suddenly I find we can do this:
1) add a property at top for double/single quote
2) use it!

<property name="quote" value='"' />
...
<exec workingdir="c:workingdir\bin" basedir="c:\executableDir" program="sar.exe" commandline="/s:ContentDB.config /d:..\..\ContentDB.config /f:${quote}my file contains space${quote} /i" />