Dec 1, 2008

[ThreadStatic] and ThreadPool threads

You can use [ThreadStatic] on static fields of a class to store state - per thread.

Please note that using the CLR ThreadPool may re-use a thread and the state is still sticking to that thread.

May 18, 2008

Creating a WSDL using C#

Have you ever thought about generating a WSDL file? For use in a DSL to create a web service description file directly?

Version one I found out was

ContractDescription cd = ContractDescription.GetContract(typeof(IContract));

System.ServiceModel.Description.WsdlExporter wexp = new System.ServiceModel.Description.WsdlExporter();

wexp.ExportContract(cd);

MetadataSet mds = wexp.GetGeneratedMetadata();

mds.WriteTo(new XmlTextWriter(@".\IContract.wsdl", Encoding.UTF8));



for a simple contract like

namespace WsdlExporter

{

    [MessageContract]

    public class RequestMessage

    {

        public Guid ID;

    }

 

    [MessageContract]

    public class ResponseMessage

    {

        public string Name;

    }

 

    [ServiceContract(Namespace="http://www.mleder.blogspot.com/WsdlExporter/2008/04")]

    public interface IContract

    {

        [OperationContract]

        void Op1(string s);

 

        [OperationContract]

        int Op2();

 

        [OperationContract]

        ResponseMessage Op3(RequestMessage req);

    }

}



I think this is verbose.


So I figured out version two, where I start with an "empty" WSDL file:

<?xml version="1.0" encoding="utf-8"?>

<wsdl:definitions xmlns:tns="http://localhost/remote"

                  xmlns:s="http://www.w3.org/2001/XMLSchema"

                  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"

                  targetNamespace="http://localhost/remote"

                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

  <wsdl:types>

    <s:schema elementFormDefault="qualified" targetNamespace="http://localhost/remote">

    </s:schema>

  </wsdl:types>

  <wsdl:portType name="RemoteAddServiceSoap12">

  </wsdl:portType>

  <wsdl:binding name="RemoteAddServiceSoap12" type="tns:RemoteAddServiceSoap12">

    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />

  </wsdl:binding>

  <wsdl:service name="RemoteAddService">

    <wsdl:port name="RemoteAddServiceSoap12" binding="tns:RemoteAddServiceSoap12">

      <soap12:address location="http://localhost:8765/RemoteAddService.asmx" />

    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>



and write some C# code:

// load an "empty" WSDL file (no types, messages, operations, ...)

// to start from and add operations by code

ServiceDescription wsdl = ServiceDescription.Read("empty.wsdl");

 

// add operation binding

OperationBinding myOperationBinding = new OperationBinding();

myOperationBinding.Name = "Add";

Soap12OperationBinding myOperation = new Soap12OperationBinding();

myOperation.Style = SoapBindingStyle.Document;

myOperation.SoapAction = "http://localhost/remote/Add";

myOperationBinding.Extensions.Add(myOperation);

 

// add input binding

InputBinding myInput = new InputBinding();

Soap12BodyBinding soap12BodyInputBinding = new Soap12BodyBinding();

soap12BodyInputBinding.Use = SoapBindingUse.Literal;

myInput.Extensions.Add(soap12BodyInputBinding);

myOperationBinding.Input = myInput;

 

// add output binding

OutputBinding myOutput = new OutputBinding();

Soap12BodyBinding soap12BodyOutputBinding = new Soap12BodyBinding();

soap12BodyOutputBinding.Use = SoapBindingUse.Literal;

myOutput.Extensions.Add(soap12BodyOutputBinding);

myOperationBinding.Output = myOutput;

wsdl.Bindings[0].Operations.Add(myOperationBinding);

 

// 1. ==> add operation to port type

Operation operation = new Operation();

operation.Name = "Add";

OperationMessage outputMessage = (OperationMessage)new OperationOutput();

outputMessage.Message = new XmlQualifiedName("tns:AddOutput");

OperationInput inputMessage = new OperationInput();

inputMessage.Message = new XmlQualifiedName("tns:AddInput");

operation.Messages.Add(inputMessage);

operation.Messages.Add(outputMessage);

wsdl.PortTypes[0].Operations.Add(operation);

 

// add first message

Message message1 = new Message();

message1.Name = "AddInput";

MessagePart messagePart1 = new MessagePart();

messagePart1.Name = "parameter1";

messagePart1.Type = new XmlQualifiedName("s:int");

MessagePart messagePart2 = new MessagePart();

messagePart2.Name = "parameter2";

messagePart2.Type = new XmlQualifiedName("s:int");

message1.Parts.Add(messagePart1);

message1.Parts.Add(messagePart2);

 

// add second message

Message message2 = new Message();

message2.Name = "AddOutput";

 

// add third message

MessagePart messagePart3 = new MessagePart();

messagePart3.Name = "result";

messagePart3.Type = new XmlQualifiedName("s:int");

message2.Parts.Add(messagePart3);

 

// 2. ==> add messages

wsdl.Messages.Add(message1);

wsdl.Messages.Add(message2);

 

// write the service description into a WSDL file.

wsdl.Write("empty_add.wsdl");

Console.WriteLine("WSDL file named empty_add.wsdl created successfully.");



Using version two, you have full control over all types, messages, operations, bindings, etc. and get a short and easy:

<?xml version="1.0" encoding="utf-8"?>

<wsdl:definitions xmlns:tns="http://localhost/remote" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" targetNamespace="http://localhost/remote" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

  <wsdl:types>

    <s:schema elementFormDefault="qualified" targetNamespace="http://localhost/remote" />

  </wsdl:types>

  <wsdl:message name="AddInput">

    <wsdl:part name="parameter1" type="s:int" />

    <wsdl:part name="parameter2" type="s:int" />

  </wsdl:message>

  <wsdl:message name="AddOutput">

    <wsdl:part name="result" type="s:int" />

  </wsdl:message>

  <wsdl:portType name="RemoteAddServiceSoap12">

    <wsdl:operation name="Add">

      <wsdl:input message="tns:AddInput" />

      <wsdl:output message="tns:AddOutput" />

    </wsdl:operation>

  </wsdl:portType>

  <wsdl:binding name="RemoteAddServiceSoap12" type="tns:RemoteAddServiceSoap12">

    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />

    <wsdl:operation name="Add">

      <soap12:operation soapAction="http://localhost/remote/Add" style="document" />

      <wsdl:input>

        <soap12:body use="literal" />

      </wsdl:input>

      <wsdl:output>

        <soap12:body use="literal" />

      </wsdl:output>

    </wsdl:operation>

  </wsdl:binding>

  <wsdl:service name="RemoteAddService">

    <wsdl:port name="RemoteAddServiceSoap12" binding="tns:RemoteAddServiceSoap12">

      <soap12:address location="http://localhost:8765/RemoteAddService.asmx" />

    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>



To create a WCF proxy for C# :
svcutil -n:*,srRemoteAddService empty_add.wsdl

Apr 30, 2008

Compiler generator CoCo/R for C#


During the preparation for a project bid for one of my customers I recently came across the CoCo/R compiler generator tool. Coco/R is a compiler generator, which takes an attributed grammar Backus-Naur-Form ( BNF) of a source language and generates a C# (and many other languages) scanner and a parser for this language.

To play around I created a small language that can speak and show message boxes. The grammer .ATG file looks the following:


COMPILER Talk

public System.Speech.Synthesis.SpeechSynthesizer ss = new System.Speech.Synthesis.SpeechSynthesizer();

void Say(string text)
{
ss.Speak(text);
}

void Show(string text)
{
System.Windows.Forms.MessageBox.Show(text);
}

IGNORECASE
// The $L option let you compile directly within your grammar
// You can comment and uncomment the line to fit your development requirements.
$LM

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
CHARACTERS
digit = "0123456789".
cr = '\r'.
lf = '\n'.
tab = '\t'.
bslash = '\\'.
quote = '"'.
chars = ANY - digit - cr - lf - tab - bslash - quote.

TOKENS
string = quote { chars | digit} quote.

IGNORE cr + lf + tab

PRODUCTIONS

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
Show
=
"Show" string (. Show(t.val); .)
.

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
Say
=
"Say" string (. Say(t.val); .)
.

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
Statement
=
Show | Say
.

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
Statements
=
Statement ';' { Statement ';' }
.

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
Talk
=
Statements
.

END Talk.


Please note the so-called semantic actions, like (. Say(t.val); .) that is executed when the corresponding token is parsed.

The code I had to write was reduced to the following:

private void btnTalk_Click(object sender, RoutedEventArgs e)
{
Parser p = new Parser(this.tbTalk.Text);
p.Parse();

tbOutput.Text = p.errors.count.ToString() + " errors";
}

Nice and easy.

Apr 22, 2008

Microsoft.TeamFoundation.WorkItemTracking.Client

I just figured out how easy it is to get access to TFS work items. In my case I was interested in linked work items of a given work item ID:

string workItemStore = ConfigurationManager.AppSettings["WorkItemStore"];

store = new WorkItemStore(workItemStore);


to connect to the TFS server and then

WorkItem m_workItem = store.GetWorkItem(int.Parse(this.tbID.Text));

 

if (m_workItem != null)

{

    lblWorkItem.Text = m_workItem.Title;

 

    foreach (Link link in m_workItem.Links)

    {

 

        RelatedLink rl = link as RelatedLink;

 

        if (rl != null)

        {

            WorkItem realtedWorkItem = m_workItem.Store.GetWorkItem(rl.RelatedWorkItemId);

            list.Add(realtedWorkItem);

        }

 

    }

}

 

dataGridView1.DataSource = list;


to list the linked work items in a simple grid.

When you want to query for WIs it should be possible to call:

// query retrieves all work items for the specified team project

string wiqlQuery = "SELECT [System.Id], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title] FROM WorkItems WHERE [System.TeamProject] = '" + teamProjectName + "' ORDER BY [System.WorkItemType], [System.Id]";

// execute the query and retrieve a collection of workitems

WorkItemCollection workitems = store.Projects[0].Store.Query(wiqlQuery);

// loop through work items

if (workitems.Count > 0)

{

    // select first Work Item

    retWi = workitems[0];

}


Note: To get rid of the assembly load exception just reference the shared assemblies from the disk path and do not use the GAC ones!