Live from TechEd Day 2

I gave a chalk talk on channel development in the afternoon yesterday. I did a huge experiment for the second-half of the talk, which was to write a custom channel from scratch for the audience. That's the kind of theater that you can pull off in a chalk talk but not a regular breakout session. I started with a completely empty Visual Studio environment, wrote a web service, explained why the web service call crashed due to a protocol issue, and then fixed the crash by writing a custom channel.

We had 46 people in the audience at the start. The capacity of the chalk talk theaters is only 32 people, but we secured about a dozen extra chairs from the lounges and people stood along the back. Capacity was an issue for several of the chalk talks yesterday. I saw one chalk talk for BizTalk that had crammed almost 60 people into the room. If you're in the audience, it starts getting quite warm after a while. The breakout session rooms are quite spacious on the other hand so I haven't seen any problems there. The Orlando venue has a tremendous capacity and a generally good layout.

The only other difficulty was that the standard equipment was a non-adjustable over-ear headset microphone. In addition to making you look like a cross between a call-center worker and a pop star, that type of mic is pretty uncomfortable to wear for an hour and picks up a lot of extra noise if you move around while talking. I tried a half-dozen ways of putting the headset on to fix the issues before the AV guy offered a standard lapel microphone. They had one sitting in the AV case in the room. I don't know why they didn't make the proper equipment available from the start.

I promised at the end of the talk that I'd post some resources.

Ed Pinto and Kenny Wolf are giving a talk "Architecture of Microsoft Windows Communication Foundation and Common Extensibility Points" on Thursday at 4:30 PM.

Steve Maine is giving the buzzword titled talk "Microsoft Windows Communication Foundation Syndication, AJAX and REST Services in Web 2.0 with .NET Framework 3.5" on Friday at 1 PM.

There are six samples that I particularly recommend for channel developers.

  1. Chunking channel
  2. HttpCookieSession channel
  3. UDP transport
  4. WSE transport
  5. UDP activation
  6. Config code generator

Finally, here is the code that was written during the talk. The scenario was based on an HTTP problem I described back in August.

 using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;

public class TechEdChannel : ChannelBase, IRequestChannel
{
   IRequestChannel innerChannel;

   public TechEdChannel(ChannelManagerBase parent, IRequestChannel innerChannel) : base(parent)
   {
      this.innerChannel = innerChannel;

   }
   protected override void OnAbort()
   {
      innerChannel.Abort();
   }

   protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
   {
      return innerChannel.BeginClose(timeout, callback, state);
   }

   protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
   {
      return innerChannel.BeginOpen(timeout, callback, state);
   }

   protected override void OnClose(TimeSpan timeout)
   {
      innerChannel.Close(timeout);
   }

   protected override void OnEndClose(IAsyncResult result)
   {
      innerChannel.EndClose(result);
   }

   protected override void OnEndOpen(IAsyncResult result)
   {
      innerChannel.EndOpen(result);
   }

   protected override void OnOpen(TimeSpan timeout)
   {
      innerChannel.Open(timeout);
   }

   #region IRequestChannel Members

   public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
   {
      return innerChannel.BeginRequest(message, timeout, callback, state);
   }

   public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state)
   {
      return innerChannel.BeginRequest(message, callback, state);
   }

   Message FixMessage(Message message)
   {
      if (message == null)
         return null;
      HttpResponseMessageProperty property = (HttpResponseMessageProperty)message.Properties[HttpResponseMessageProperty.Name];
      if (property != null && property.StatusCode == System.Net.HttpStatusCode.Accepted)
      {
         return null;
      }
      return message;
   }

   public Message EndRequest(IAsyncResult result)
   {
      return FixMessage(innerChannel.EndRequest(result));
   }

   public EndpointAddress RemoteAddress
   {
      get { return innerChannel.RemoteAddress; }
   }

   public Message Request(Message message, TimeSpan timeout)
   {
      return FixMessage(innerChannel.Request(message, timeout));
   }

   public Message Request(Message message)
   {
      return FixMessage(innerChannel.Request(message));
   }

   public Uri Via
   {
      get { return innerChannel.Via; }
   }

   #endregion
}

public class TechEdChannelFactory : ChannelFactoryBase, IChannelFactory<IRequestChannel>
{
   IChannelFactory<IRequestChannel> innerFactory;

   public TechEdChannelFactory(IChannelFactory<IRequestChannel> innerFactory) : base()
   {
      this.innerFactory = innerFactory;
   }
   protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
   {
      return innerFactory.BeginOpen(timeout, callback, state);
   }

   protected override void OnEndOpen(IAsyncResult result)
   {
      innerFactory.EndOpen(result);
   }

   protected override void OnOpen(TimeSpan timeout)
   {
      innerFactory.Open(timeout);
   }

   #region IChannelFactory<IRequestChannel> Members

   public IRequestChannel CreateChannel(EndpointAddress to, Uri via)
   {
      return new TechEdChannel(this, innerFactory.CreateChannel(to, via));
   }

   public IRequestChannel CreateChannel(EndpointAddress to)
   {
      return new TechEdChannel(this, innerFactory.CreateChannel(to));
   }

   #endregion
}

public class TechEdBindingElement : BindingElement
{
   public TechEdBindingElement()
      : base()
   {
   }

   protected TechEdBindingElement(TechEdBindingElement element)
      : base(element)
   {
   }

   public override BindingElement Clone()
   {
      return new TechEdBindingElement(this);
   }

   public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
   {
      IChannelFactory<TChannel> innerFactory = base.BuildChannelFactory<TChannel>(context);
      return (IChannelFactory<TChannel>)new TechEdChannelFactory((IChannelFactory<IRequestChannel>)innerFactory);
   }

   public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
   {
      if (typeof(TChannel) != typeof(IRequestChannel))
         return false;
      return base.CanBuildChannelFactory<TChannel>(context);
   }

   public override T GetProperty<T>(BindingContext context)
   {
      return context.GetInnerProperty<T>();
   }
}

[ServiceContract]
public interface IService
{
   [OperationContract(Action="*", IsOneWay=true)]
   void DoNothing();
}

public class Service : IService
{
   public void DoNothing()
   {
      Console.WriteLine("Did nothing.");
   }
}

class Program
{
   static void Main(string[] args)
   {
      string uri = "https://localhost:8000/";

      TextMessageEncodingBindingElement be = new TextMessageEncodingBindingElement();
      be.MessageVersion = MessageVersion.None;

      Binding binding = new CustomBinding(
         new OneWayBindingElement(),
         new TechEdBindingElement(),
         be,
         new HttpTransportBindingElement()
         );

      ServiceHost host = new ServiceHost(typeof(Service));
      host.AddServiceEndpoint(typeof(IService), binding, uri);
      host.Open();
      ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, new EndpointAddress(uri));
      factory.Open();
      IService proxy = factory.CreateChannel();
      proxy.DoNothing();
      factory.Close();
      host.Close();
      Console.ReadLine();
   }
}

Next time: Live from TechEd Day 3