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.
- Chunking channel
- HttpCookieSession channel
- UDP transport
- WSE transport
- UDP activation
- 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