November 2004 - Posts

Reflection is one of the damn great technologies in the .NET Framework. Currently, I'm creating some demos on how to create a plug-in driven architecture for an application framework. What it basically should do, is allowing a user to load modules at runtime (e.g. by specifying a series of assemblies/types in a configuration file). Thus, without recompiling the original solution, it should still be possible to integrate new functionality in the application just by using configuration. Let's show you how...

 

1. Create interfaces for the plug-ins you want to support

An example looks as follows:

using System;

namespace
Demo.Interfaces
{
     ///
<summary>
     ///
Summary description for Class1.
     ///
</summary>
     public interface
IPlugin
     {
          int Calculate(int
input);
     }
}

Create this class in a separate class library (so that it becomes a separate assembly file). In this example, I'm just using an interface with one method in order to calculate a left-associative calculation on integer values. Basically, what I want to do is something like this:

(((((input operation1) operation2) operation3) operation4) operation5)

Where every operation is execute by a class implementing the IPlugin interface. An example of some implementations is this:

using System;
using Demo.Interfaces;

namespace Demo.Test
{
      /// <summary>
      /// Summary description for Class1.
      /// </summary>
      public class Test1 : IPlugin
      {
            public int Calculate(int input)
            {
                  return input * 2;
            }
      }

      public
class
Test2 : IPlugin
      {
            public int Calculate(int input)
            {
                  return input + 3;
            }
      }
}

 

2. Create a loader application

Next, we need to create the application that will use the plug-ins. For the sake of simplicity you can just go ahead and create a Windows Forms application.

 

2.a. Specifying the configuration in an XML file

One of the things we want to do is load the implementations of the IPlugin dynamically. Therefore, we need to have a file that contains the information needed to load the assembly and instantiate the class (that should implement IPlugin). For this purpose, I created a simple sample XML file:

<?xml version="1.0" standalone="yes"?>
<
Plugins xmlns
="http://tempuri.org/Plugins.xsd">
     <Calculators
>
          <File>test1</File
>
          <Type>Demo.Test.Test1</Type
>
     </Calculators
>
     <Calculators
>
          <File>test1</File
>
          <Type>Demo.Test.Test2</Type
>
     </Calculators
>
</Plugins>

In order to load this file in a nice and smooth way, you can create a typed DataSet with the following XSD schema:

<?xml version="1.0" encoding="utf-8" ?>
<
xs:schema id="Plugins" targetNamespace="http://tempuri.org/Plugins.xsd" elementFormDefault="qualified" attributeFormDefault="qualified" xmlns="http://tempuri.org/Plugins.xsd" xmlns:mstns=http://tempuri.org/Plugins.xsd xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata
="urn:schemas-microsoft-com:xml-msdata">
     <xs:element name="Plugins" msdata:IsDataSet
="true">
          <xs:complexType
>
               <xs:choice maxOccurs
="unbounded">
                    <xs:element name
="Calculators">
                         <xs:complexType
>
                              <xs:sequence
>
                                   <xs:element name="Type" type="xs:string" minOccurs="0"
/>
                                   <xs:element name="File" type="xs:string" minOccurs="0"
/>
                              </xs:sequence
>
                         </xs:complexType
>
                    </xs:element
>
               </xs:choice
>
          </xs:complexType
>
     </xs:element
>
</
xs:schema>

The code to load the XML file then looks like this:

plugins.ReadXml("plugins.xml"); //plugins is a DataSet control on the Windows Form of the type of the typed dataset.

This code can be placed in the event handler for a button or just in the Form_Load event handler. Note: for better performance, XmlReader implementations would be better (instead of using the DataSet approach).

 

2.b. Load the assemblies and instantiate the types

Once we have loaded the XML configuration file, we can go ahead to instantiate the types that we want to execute dynamically. A code snippet looks like this:

private ArrayList lst;

private void button1_Click(object sender, System.EventArgs e)
{
     lst =
new ArrayList();

    
foreach(Plugins.CalculatorsRow r in plugins2.Calculators)
     {
          IPlugin p = (IPlugin) Activator.CreateInstance(Assembly.Load(r.File).GetType(r.Type));
          lst.Add(p);
     }
}

The ArrayList (unfortunately not using generics yet because of the v1.x coverage) can be seen as a cache for the instances of the plug-ins. For a better framework, you would have a IsReusable property defined in the interface, so that you can ask the plug-in whether it can be used multiple times or not. If not, you need to instantiate the type every time you want to use the plug-in's functionality. However, this would lead us too far for now.

Basically, what happens is this. The assembly is loaded based on the "File" column of our DataTable (thus the <File> element in the XML file). This value contains the name of the assembly file that contains the type specified in the <Type> element. Note that the file name should not have an extension (.dll nor .exe). Next, we can ask the assembly to get the specified type by using GetType. This gives us a Type instance that can be used by the Activator in order to instantiate it (in this case without constructor arguments). The result of this method call - finally - has to be casted to the appropriate type, which is of course the IPlugin interface type. This is where the magic happens. All this stuff requires System.Reflection to be imported in the class definition.

Note: in order to execute all this code, the plugins.xml file needs to be in the same folder as the executable. Furthermore, the assemblies specified in this file (thus the implementations of IPlugin) need to live in that same folder as well.

 

2.c. Calling the plug-ins in the application

Finally, we can use the loaded plug-in instances to do some calculations as defined in the plug-ins. Look at the following code snippet for an example of this:

private void btnCalculate_Click(object sender, System.EventArgs e)
{
     int input;

     try
     {
          input =
int.Parse(txtInput.Text);
     }
     catch
     {
          MessageBox.Show("Invalid input value specified!");
          return;
     }

     StringBuilder sb =
new StringBuilder();
     sb.AppendFormat("Start value: {0}\r\n\r\n", input);

     foreach
(object o in
lst)
     {
          IPlugin p = (IPlugin) o;
          input = p.Calculate(input);
          sb.AppendFormat("Plugin: {0}\r\nNew value: {1}\r\n\r\n", p.ToString(), input);
     }

     txtOutput.Text = sb.ToString();
     MessageBox.Show("Result: " + input);
}

So, actually this is a kind of chained model. The same kind of technique can be used to implement a filter as well (compare with the way HttpModules work in ASP.NET, which also are specified in machine.config or web.config and which are loaded dynamically as well).

 

3. Testing it yourself

The code of this sample can be found on www.bartdesmet.net/download/pluginssample.zip. It's actually a complete Visual Studio .NET 2003 solution with the right configuration in order to compile and execute in the development environment.

Please note it's just a simple and stupid sample in order to illustrate the principle. It has not been fully tested and further exception handling needs to be in place to make it fool-proof (e.g. to avoid loading a type that does not implement the IPlugin interface). Additional improvements can be made as well.

 

For further questions, you can drop me a mail or send in some feedback.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

I'm really convinced about what Microsoft will be doing on the field of web search with MSN Search. However, I still find myself using Google quite often, so I started to analyse why. I found out that the major reason is the speed I have developed over time to go to that site. In Internet Explorer, I'm just doing this: ALT-D (go to address bar), type "google" (without quotes of course) and press CTRL-ENTER (will add http://www. in front of the typed text and .com after it). Damn easy to do this. MSN Search doesn't have the same effectiveness however: you have to type search.msn.com manually each time. So I decided to force myself to use MSN instead by applying some tricks:

  • Tweak the SearchUrl key in the registry under HKCU\Software\Microsoft\Internet Explorer as described on various places around the net (just Google MSN for it).
  • Force yourself to go to MSN instead of Google as follows:
    • Create a website on your local pc in IIS with host header www.google.com
    • Add a simple page to it (default.aspx) with the following content:

      <% Response.Redirect("http://search.msn.com") %>
    • Go to the hosts file (c:\windows\system32\drivers\etc\hosts) and add the line

      127.0.0.1    www.google.com

      to it

It works damn effectively for me and I'm still finding whatever I'm looking for (but now with MSN). However, it's a pitty MSN still does not incorporate image search (although this does not work very well in Google either in my very opinion).

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

As you probably noticed, I didn't find the time to do extensive posting on my blog during the event. So let me write some comments now, back at home. IT (Forum) was just great. Although I'm primarily focusing on development, the IT folks world is also a very exciting one and it's a great aid to get some insights in their minds too (which is important as a developer, for example to have some clue about manageability, DBA tasks, etc). Let's go over some day-to-day log:

Monday

  • Arrived at Copenhagen about 9:30 PM (had a nice flight with SN), arrived in the hotel at 10:00 PM
  • Discovered my tiny but functional hotel room; the television has 69 channels of which approx 60 display "snow"; so I went out for a little walk around the city

Tuesday

  • Arrived at the event venue; the subway net is very good (a metro every 5 minutes or so) and modern (metros don't have drivers anymore @ Copenhagen; cool metro stations, at least when compared with the Belgian ones)
  • Visited my ATE booth for Virtual Server 2005, setup was okay (which wasn't the case at TechEd this summer unfortunately) so we could take a jumpstart later that day without having to worry about the config of the machines (because of that reason, I missed the keynote at TechEd)
  • So I went to the keynote to hear Bill Gates talking about the "magic moments in software"; but first a magician came up to warm up the 3500 attendeed
  • Sessions that day:
    • Microsoft Identity Management Strategy and Roadmap (covered MIIS and AD/AM stuff, but rather general talk)
    • Security Policies? Ugh, Just Give me a Firewall (great session btw, as always the case with SteRiley's talks)

Wednesday

Serious stuff first:

  • No ATE for me today, so I went to Hans Verbeeck's session on CLR integration in SQL Server 2005. Nice session to hear the worries of DBAs on this field. It was - however - a difficult decision for me to go to Hans' session instead of Rafal Lukawiecki's session on Digital Trust Foundation. But since Hans' session had level 300 (Rafal's one only level 200) I chose for HansVB's talk.
  • Scaling up to 64-bit Windows was my second session that day. Unfortunately a little too much HP-focuses, but it gave me a good overview of Intel's Itanium (2) architecture versus x64 and AMD Opteron.
  • "Privacy in the Digital World" by SteRiley was a level 200 series (although HansVB was talking again on SQL Server 2005 XML in the Database during that timeslot I chose for Steve this time; sorry Hans). The main reason was that I had been working with XPath, XQuery, XML Web Services, etc stuff in SQL 2005 already the week before and I reviewed the slides on-line already. Btw, it was a difficult decision for that timeslot since there was an interesting talk (first of two) on Active Directory Object and Attribute Security. I guess I'll review that on the post-conference DVD later.
  • I joined the panel discussion on the SQL Server Roadmap during lunch (so I missed lunch for the second day in row in favor of two portions of salty chips :-)). Some great questions from the audience and great answers of the panel (e.g. "Will Exchange v.next use SQL Server"; I won't repeat the answer over here now)
  • Next, I went to Allen Stewart's talk on Virtual Server 2005 (Technical Overview) to learn something more about VSMT and to know the general message of the team in order to answer the ATE booth questions (in order to deliver a uniform story towards the customers).
  • 4:30 PM, session about W2K3 SP1 (primarily focused on security enhancements)
  • Last but not least, I went to the talk of Euan Garden on SQL Server 2005 A-Z (although level 200, but the second part of "AD Object and Attribute Security" required to be attending the first part of the session ealier, so I decided for Euan's talk). It was almost the same content as on TechEd 2003 (where the "Yukon A-Z" was rather confidential, the ppts never made it on the DVD or on the net; I remember that the session back then was plenty of people and some guys on the first rows decided to take a photo of every slide; luckily this was not the case now). The same timeslot a Kimberly Tripp session was held, but I made TechEd 2004 earlier this year my "Tripp event", so I decided not to Tripp again :-). However, I'll be reviewing these sessions later on the post-conf DVD.

That evening the Benelux party was held in the heart of the city (Rosie McGee's) and of course I was attending that party. Not much Belgians I knew, except for Philippe Lemmens (Microsoft EMEA) and Gerd De Bruycker (Microsoft EMEA), but still I enjoyed the party (learned to know new people).

Tuesday

The day of the big IT Forum party (which I'll not be posting about, since others certainly will do). Beside of that there was of course the content itself and the ATE booth from 11 AM to 2 PM). It was also the one and only day I went out to the dining hall for a full meal (well-prepared salmon).

The sessions:

  • Arrived a little late, so I took some food to the session room (although prohibited, I found a door to the session room that did not mention the fact that food and drinks were not allowed in the session rooms :-)). I went to Don Vilen's session on SQL Server 2000 Security first, which was pretty intersting. Originally I planned for another 64-bit session but since I didn't really like the first one I saw, I decided to choose for SQL Server 2000 Security instead.
  • Since I was on ATE I missed an interesting session on WinPE :-(. However, I'm reviewing it now at home.
  • In the afternoon, the big Cheese (Andrew Cheeseman) was giving a session on ISA 2004, which was my choice. The "big cheese" (as he calls himself) is always interesting, no comment required.
  • TCP/IP for Security Administrators (SteRiley) was great as well
  • Implementing PKI in the Enterprise was boring (sorry for the speaker) but maybe it was due to the IT Forum party that was waiting for us...

Friday

Attended sessions on WUS, a part of Cheeseman's classic talk on "How we built the IT Forum IT infrastructure" (but needed to leavy to start up the ATE booth again), "Encryption in Detail" (Lukawiecki) and Gert Drapers' session on "Why is SQL Server Waiting?" (since I missed a part of that earlier on the SQL Server Days in Brussels and it was pretty heavy level 400; but now I have the feeling I got the message). Last but not least I visited Copenhagen somewhat more (I did this on Tuesday night as well quite late), so I went to Tivoli but unfortunately most restaurants were full (all because of reservations) so I ended up in Mc Donalds and took a little dessert of two maxi Twixes later :-).

Saturday

Left Copenhagen at 11:45 (had to wait very long at the airport because of the message "Wait for gate" on the screens - I hate this message) after a great week full of IT power.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Here I am, posting from the ATE booth of Virtual Server 2005. The demos are allright, so there is not that much I need to do before we're ready to kick off. And by the way, I love Denmark. Beautiful country, nice people, great hotel (but small rooms), cool public transportation. More information yet to come later on my blog.Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

It's the month of the books in Belgium (cf. "Boekenbeurs" @ Antwerp Expo). Veni, vidi, emo (he came, he saw and he bought). I hope it's correct since I'm at the end of my Latin now for 5 years or so. To the point ... I have completed my collection of the master pieces of computer software books. Just to name of few books I already had (and read) over here: Design Patters (of the "gang of four"), Code Complete (McConnell), UML Distilled (Fowler), Writing Secure Code (Howard & Leblanc), An introduction to Database Systems (Date), Eploiting software (Hoglund, McGraw) and Stroustrup's C++ bible. This weekend, I added the books "Patterns of Enterprise Application Architecture" (Fowler) and "Refactoring to Patterns" (Kerievsky). Cool stuff. Unfortunately, I'm buying more books than I can read for the moment, but weekends and holidays (and train travel trips) are the ideal moments to fill the gap.

For the moment I'm reading some books parallel to each other. These include the books new MSPress titles about SQL 2005, VB 2005, ASP.NET 2.0 previews (ideal for "big overviews", thus light meal for lost evenings), .NET Patterns, Active Directory for Windows Server 2003, .NET Remoting and SQL Server 2000 Notification Services of course (on which I'll be delivering a presentation soon).

I planned to post about it earlier, but finally here's the evaluation of my summer months (July-September). These were very productive months, done a lot of (personal and professional) work, at home (do-it-yourself with false ceilings etc), at Microsoft (SchoolServer), at my former secondary school (network maintenance and bulk account/mailbox creation), on the train (reading books), at TechEd (ATE), on my holidays (no, I left my laptop at home, weird), etc. From the perspective of reading books I finished only 3 books (planned to do 4) but I'm more than happy about this, since I did the readings more thoroughly because of this. Topics included: ASP.NET v2.0 beta, web application security and .NET Framework "internal kitchen".

I'm planning (on a long term probably) to put my local database of books and the completion state (currently in Access) on-line in the format of an ASP.NET app which can be useful to centralize my summaries and share some ideas and feedback with others. Maybe a blog would be more suitable for this, if I can link it to the books database. Or some wiki. To be investigated, if I ever have some time... The same holds for bringing some book reviews online :-( It will happen, ever...

Back to SQL-NS 2000 and some code reviews now (and to bed later on).

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

It's already pretty simple to do using System.DirectoryServices (connecting to the IIS configuration using ADSI), it will become easier in the near future in version 2.0 of the .NET Framework using the metabase.dll assembly (namespace Microsoft.Iis.Metabase). Using classes such as ApplicationDomain, Website, Binding, WebDirectory, WebExtension, etc it's possible to manage/modify every aspect of the IIS metabase. You can find the beta information on http://msdn2.microsoft.com/library/0k32eta8.aspx. Looks promising and it simplifies this kind of development dramatically. I love it :-)Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Yesterday, I had a meeting with some guys who're working on a .NET-based development project that covers quite some stuff: database access, web service layers (facade pattern), smart device applications and WinForms clients. Although rather basic, still interesting stuff to see people walking the first couple of .NET-miles. One of the problems they came across was a multi-threading problem. As the matter in fact, it was partially my fault since I recommended them to call their webservices asynchronously in the Windows Forms application (and Smart Device application). So they did. However, what they did as well was performing a UI update inside the callback method of the web service call. Let's illustrate with a simple sample:

private void button1_Click(object sender, System.EventArgs e)
{
  
localhost.Service1 svc =
new localhost.Service1();
  
svc.BeginGetProducts(
new AsyncCallback(ProductsFinished), svc);
}

private void ProductsFinished(IAsyncResult res)
{
  
localhost.Service1 svc = (localhost.Service1) res.AsyncState;
  
DataSet ds = svc.EndGetProducts(res);
  
//UI updates
}

It seems to be correct but it isn't. Instead of performing UI-updating logic directly inside the ProductsFinished method, you should propagate this back to the UI-thread by using the Invoke method. Of course some modifications to the skeleton of the code are required in order to gain access to the retrieved data etc, but basically it looks like this:

private void button1_Click(object sender, System.EventArgs e)
{
  
localhost.Service1 svc =
new localhost.Service1();
  
svc.BeginGetProducts(
new AsyncCallback(ProductsFinished), svc);
}

private delegate void UpdateUI(DataSet ds);

private void ProductsFinished(IAsyncResult res)
{
  
localhost.Service1 svc = (localhost.Service1) res.AsyncState;
  
DataSet ds = svc.EndGetProducts(res);
   this.Invoke(new UpdateUI(ProductsFinishedUpdate), new object[] { ds });

}

private void ProductsFinishedUpdate(DataSet ds)
{
   //UI updates
}

Another problem they were faced with was a weird (but obvious) non-deterministic behavoir. Again it was caused by my "asynchronous web services calls recommendation". Guess what happens if you're doing this?

private void Form1_Load(object sender, System.EventArgs e)
{
   localhost.Service1 svc = new
localhost.Service1();
   svc.BeginGetCategories(new
AsyncCallback(CategoriesFinished), svc);
   svc.BeginGetProducts(new AsyncCallback(ProductsFinished), svc);
}

and you rely on the assumption that CategoriesFinished will complete before ProductsFinished? Indeed, non-deterministic behavior (and of course this happens the very first time during a demo you're giving :-)). Actually, what they told me was that it did not work the first time they did a build of the solution but the second time it worked well. That really did sound strange to me (I didn't see the code yet) but as an afterthought (now it's solved of course) I was thinking that the web service project was part of the solution and thus the ProductsFinished occurred before the CategoriesFinished because of the compile delay of the first hit on the web application. CategoriesFinished has far less processing to do, so that's the only reason I can think of (combined with the fact that the problem only occurred during the first debugging cycle after a rebuild step).

It's also worth to mention that the .NET Framework v2 will have support for this kind of things by means of the System.ComponentModel.BackgroundWorker class (component) that can be used in all kinds of applications to do background work. I've been experimenting with this last week in depth and I hope this component will help people to solve problems like this (although it doesn't help to learn the basics of threading to newbies). For me, it's a welcome gift since I'm using this kind of things regularly (cf. the SchoolServer project that contains quite some wizards with a progress-bar that reflects background work).

Back to the project I was assisting on today. I decided to show them the use of a typed DataSet on the web service layer and how to link two "tables" (i.e. elements in DataSet terminology) using a relation and what the impact of this is on the code that xsd.exe (through Visual Studio .NET of course) is spitting out. I took me a bit to convince the guys of the usefulness of a typed DataSet (code generation, easy iteration, easy rows manipulation, parent-child data retrieval methods, etc) and the demonstration resulted in moving around the typed DataSet throughout the solution (from the web services layer to a separate class project to the DAL) to get a strong n-tiered model in the end. This made me think again of the power of Visual Studio .NET to copy-paste files between projects within a solution but with one big remark: don't forget to modify the namespace in the associated code-file (something that newbies often forget as I saw dozens of times before). Another great point is the power behind the XSD deferring in the Add Web Reference dialog. When adding a web reference to a web service that returns a typed dataset, the dataset is reconstructed client-side as part of the generated proxy (therefore avoiding the need to have a shared class client-side and server-side to share the common types). It was however the first time I tried this in a smart device application and guess o what ... the typed dataset was added on the .NET Compact Framework application as well (although the .NET CF doesn't provide support for typed datasets through the use of the Visual Studio tools or xsd.exe). I still have to check the richness of this workaround (if you can call it like that) but it seems to be great at first sight (instead of having to use xsdcf.exe, which is a 3rd party tool that can be downloaded from the net http://www.tmgdevelopment.co.uk/xsdcf.htm - please take a look at the known bug since I came across this issue a couple of times already).

And last but not least: if you have to update the Url of a web service reference, please do it using the Properties and not in the code behind files of the web reference (i.e. the generated proxy class) since the latter is always reconstructed when updating the web reference. I saw this "problem" (which is behavior by design, I can't think of another way to avoid this when designing tools like wsdl.exe) popping up again today. The URL was changed in the proxy class a couple of weeks ago but my change was discarded of course since the guys in question performed a web reference refresh and thus they were trying to use http://localhost on a pocket pc application (in the emulator). Unfortunately, we couldn't debug this problem completely since name resolution of a NetBIOS name did fail in the emulator (the NIC was disconnected, thus a dynamic IP wasn't available as well). I'm trying to find a workaround for this debugging trouble for smart device applications when you're on the road. If anyone has a suggestion, please drop me a line.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Today, I performed a setup of the Windows Media Services on Windows Server 2003 for some testing. However, one of the problems you might come around if you try this is the fact that .mp3 files for example are non-streamable by the Windows Media Services. Fortunately, a tool called the "Windows Media Encoder" can be downloaded from the Microsoft website to do this conversion. However, this tool only allows to convert one file at a time, which is not handy if you have a bunch of files that you want to convert. Luckily, there is a also an SDK (see http://www.microsoft.com/downloads/details.aspx?FamilyID=000a16f5-d62b-4303-bb22-f0c0861be25b&DisplayLang=en) that allows you to program against the Encoder SDK. Conversion of files using the SDK is pretty simple and if you don't want to write the code yourself, there is a very handy VBS script included in the SDK called Wmcmd.vbs. God, I like this :-). A quick sample:

wmcmd -input c:\wmpub\wmroot -output c:\wmpub\mymusic -profile a64

About 10-15 seconds per music file and you're ready to start streaming. The only problem I still have to solve is the tagging of the files, but that's something for later. For the moment, I'm writing some C# code to create a screen-capturing application (I know Microsoft Producer has this, but I'd like to find out how to accomplish a really good result for demo-recordings).

I'll keep you posted guys.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Congratulations with the victory on the US election, Mr. Bush.

Not that I expect the White House to visit my blog, but you never know :-).

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

More Posts