# Tuesday, 14 August 2007

You all know this.  I (in theory) know this.  However, just to remind everyone (and apparently myself)...

When debugging anything related to WCF, there are a set of problems that are IMPOSSIBLE to discover without looking at the service trace logs.  In particular (this is the one that bit me) for very good reasons, nothing security related gets as far as application code.  You pretty much get "something bad happened". 

Anyway, after beating my head against the keyboard for a good solid day, I remembered this little maxim, and checked the logs.  There was the problem, plain as day.  Doh!  Turned out that I'd changed the namespace of a particular class, and failed to propagate that change to my .svc file.  There is absolutely no indication that this was the problem at the app level, but on looking at the logs, it's right there. 

Hopefully I'll remember that one next time.

Indigo | Work
Tuesday, 14 August 2007 14:20:17 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, 15 June 2007

I think I've come across a better solution (or at least one that I'm happier with) to this problem.  Turns out that the KnownType attribute can take a method name instead of a type.  The type it's applied to then has to implement a static method with that name that returns an array of Type.  That method can then decide which types to return.  So I could do something like this

[DataContract]

[KnownType("CheckForConfiguredOverrides")]

public class UserBase

{

    [DataMember]

    public string FirstName;

    [DataMember]

    public string LastName;

 

    public static Type[] CheckForConfiguredOverrides()

    {

        List<Type> result = new List<Type>();

 

        string thisTypeName = typeof(UserBase).FullName;

        string overrideType = System.Configuration.ConfigurationManager.AppSettings.Get(thisTypeName);

        if (!string.IsNullOrEmpty(overrideType))

        {

            Type type = Type.GetType(overrideType);

            if (type != null) result.Add(type);

        }

 

        return result.ToArray();

    }

}

Then in the config file (this ultimately will use our distributed configuration service) I add

<appSettings>

  <add key="UserBaseType.UserBase" value="UserBaseType.BankUser"/>

</appSettings>

 
To return the right type.  Everybody is happy!  As a side benefit, this allows me to limit "overridability" to only those types I apply the attribute to.  That lets me (as the designer) decide which types are OK to subclass and which aren't (obviously I could use sealed here as well, but that's a separate issue) using this KnownType attribute.  I think this will work out much better than marking up base classes with specific types.

Indigo | Work
Friday, 15 June 2007 12:43:46 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 

This may have been understood by the set of all people not me, but I didn't realize that the KnownType attribute doesn't seem to work on a MessageContract.  The KnownType attribute is supposed to tell the data contract serializer that there might be some extra types it needs to know about when serializing your objects.  It's equivalent to the XmlInclude attribute used by the XmlSerializer.  If I have some code that looks like this...

[MessageContract]

public class UserRequest

{

    [MessageBodyMember]

    public string UserName;

    [MessageBodyMember]

    public bool SendExtended = false;

}

 

[MessageContract]

public class UserResponse

{

    [MessageBodyMember]

    public string Message;

    [MessageBodyMember]

    public UserBase User;

}

 

[DataContract]

public class UserBase

{

    [DataMember]

    public string FirstName;

    [DataMember]

    public string LastName;

}

 

[DataContract]

public class BankUser : UserBase

{

    public BankUser() : base() { }

 

    [DataMember]

    public int TupperwarePoints;

 

}

In my server side implementation, if the "SendExtended" flag is true in the UserRequest, the object returned in the "User" property of the UserResponse is a BankUser, not a UserBase object.  If the code runs like this, that fails horribly, since the data contract serializer is set to serialize a UserBase, not a BankUser. 

I would have thought that I could mark up the UserResponse, like so

[MessageContract]

[KnownType(typeof(BankUser))]

public class UserResponse

{

    [MessageBodyMember]

    public string Message;

    [MessageBodyMember]

    public UserBase User;

}

Turns out this totally doesn't work.  I get the same serializer error, despite the KnownType attribute.  If I make UserResponse a DataContract instead of a MessageContract, it works fine, and the appropriate BankUser object is received on the client side. 

However, in my actual code, I need the MessageContract.  The alternative is marking up the UserBase class itself, like this:

[DataContract]

[KnownType(typeof(BankUser))]

public class UserBase

{

    [DataMember]

    public string FirstName;

    [DataMember]

    public string LastName;

}

This works just fine, but from a design standpoint it's teh suck.  This means that I have to tell my base class about all it's derived types, thus defeating the entire point of inheritance.  That's why I never used the XmlInclude attribute.  I might have to rethink how the messages are composed and see if there's a better way to encapsulate things.

On a slightly unrelated note, it seems that my ServiceHost takes much longer to start up when the Request and Response are defined as MessageContract than when they are straight DataContracts.  I haven't tested this extensively, so it's anecdotal, but interesting nonetheless. 

Indigo | Work
Friday, 15 June 2007 10:37:14 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  | 
# Friday, 13 April 2007

We've been building around WCF duplex contracts for a while now, as a way of limiting the number of threads active on our server.  The idea being since a duplex contract is really just a loosely coupled set of one-way operations, there's no thread blocking on the server side waiting for a response to be generated. 

It's been working well, but I'm starting to find that there are an awful lot of limitations engendered by using duplex contracts, mostly around the binding options you have.  It was very difficult to set up duplex contracts using issued token security, and in the end it turned out we couldn't set up such a binding through configuration, but were forced to resort to building the bindings in code.  Just recently, I discovered that you can't use transport level security with duplex contracts.  This makes sense, because a duplex contract involves an independent connection from the "server" back to the "client".  If you are using SSL for transport security, you would need certificates set up on the client, etc.  I'm not surprised that it doesn't work, but it's limiting.  Plus we have constant battles with the fact that processes tend to hold open the client side port that gets opened for the return calls back to the client.  Every time we run our integration tests, we have to remember to kill TestDriven.NET's hosting process, or on our next test run we get a failure because the client side port is in use already. 

All this has lead me to think that maybe we should go with standard request-response contracts until the server-side threading really shows itself to be a problem.  That's what I get for prematurely optimizing...

Indigo | Work
Friday, 13 April 2007 12:53:50 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, 11 December 2006

One of the (many) issues I encountered in creating a duplex http binding with IssuedToken security (details here) was that when using a duplex contract, the client has to open a "server" of its own to receive the callbacks from the "server" part of the contract.  Unfortunately, there are a couple of fairly critical settings that you'd want to change for that client side "server" that aren't exposed via the configuration system, and have to be changed via code.

One that was really causing me problems was that for each duplex channel I opened up (and for one of our web apps, that might be 2-3 per ASP.NET page request) I had to provide a unique BaseClientAddress for each channel, which quickly ran into problems with opening too many ports (a security hassle) and insuring uniqueness of the URI's (a programming hassle).  Turns out that you can ask WCF to create unique addresses for you, specific to your chosen transport in their method of uniqueness.  However, you make that request by setting the ListenUri and ListenUriMode properties on the ServiceEndpoint (or the BindingContext).  For the client-side "server" part of a duplex contract, that turns out to be one of those settings that isn't exposed via configuration (or much of anything else, either). 

Luckily, I found an answer in the good works of Nicholas Allen (yet again).  He mentioned that you could set such a property on the BindingContext for the client side of a duplex contract.  Not only that, but Mike Taulty was good enough to post a sample of said solution. 

There's a great summary of the solution here, but to summarize even more briefly, you have to create your own BindingElement, and inject it into the binding stack so that you can grab the BindingContext as it hastens past.  Now I'm setting the ListenUri base address on a specific port, and asking WCF to do the rest by unique-ifying the URI.  Not only do I not have to keep track of them myself, but I can easily control which ports are being used on the client side, which makes both IT and Security wonks really happy.

Monday, 11 December 2006 11:02:53 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, 05 December 2006

This took me WAY longer to figure out that either a) it shoudl have or b) I had hoped it would.  I finally got it working last week though.  I now have my client calling the server via a Dual Contract using IssuedToken security (requires a SAML token), said token being obtained from an STS written by me, which takes a custom token from the client for authentication. 

On the plus side, I know a whole bunch more about the depths of the ServiceModel now than I did before. :-)

It turns out (as far as I can tell) that the binding I needed for client and server cannot be described in a .config file, and must be created in code.  That code looks like this on the server

 

public static Binding CreateServerBinding(Uri baseAddress, Uri issuerAddress)

{

 

    CustomBinding binding = new CustomBinding();

    IssuedSecurityTokenParameters issuedTokenParameters =

        new IssuedSecurityTokenParameters();

    issuedTokenParameters.IssuerAddress = new EndpointAddress(issuerAddress);

    issuedTokenParameters.IssuerBinding = CreateStsBinding();

    issuedTokenParameters.KeyType = SecurityKeyType.SymmetricKey;

    issuedTokenParameters.KeySize = 256;

    issuedTokenParameters.TokenType

        = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1";

 

    SecurityBindingElement security

        = SecurityBindingElement.CreateIssuedTokenBindingElement(issuedTokenParameters);

    binding.Elements.Add(SecurityBindingElement.CreateSecureConversationBindingElement(security));

    binding.Elements.Add(new CompositeDuplexBindingElement());

    binding.Elements.Add(new OneWayBindingElement());

    binding.Elements.Add(new TextMessageEncodingBindingElement());

    binding.Elements.Add(new HttpTransportBindingElement());

 

    return binding;

}

and essentially the same on the client, with the addition of setting the clientBaseAddress on the CompositeDuplexBindingElement.

This works just fine, getting the token from the STS under the covers and then calling the Dual Contract server interface with the token. 

I ended up with a custom binding to the STS itself, mostly because I needed to pass more credentials than just user name and password.  So the STS binding gets created thusly:

 

public static Binding CreateStsBinding()

{

    Binding binding = null;

 

    SymmetricSecurityBindingElement messageSecurity

        = new SymmetricSecurityBindingElement();

 

    messageSecurity.EndpointSupportingTokenParameters.

     SignedEncrypted.Add(new VoyagerToken.VoyagerTokenParameters());

 

    X509SecurityTokenParameters x509ProtectionParameters

        = new X509SecurityTokenParameters( X509KeyIdentifierClauseType.Thumbprint);

    x509ProtectionParameters.InclusionMode = SecurityTokenInclusionMode.Never;

 

    messageSecurity.ProtectionTokenParameters = x509ProtectionParameters;

    HttpTransportBindingElement httpBinding = new HttpTransportBindingElement();

 

    binding = new CustomBinding(messageSecurity, httpBinding);

 

    return binding;         

}

Not as easy as I had hoped it would be, but it's working well now, so it's all good.  If you go the route of the custom token, it turns out there are all kinds of fun things you can do with token caching, etc.  It does require a fair amount of effort though, since there are 10-12 classes that you have to provide.

Tuesday, 05 December 2006 15:47:33 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, 28 September 2006

I'm working on a new project these days, and we're going all out with the bleeding edge goodies, including .NET 3.0.  To set the stage, we've got a server that internally calls a WF workflow instance, which is inherently asynchronous, so the interface we expose to clients is a WCF "Duplex Contract" which basically means two sets of one-way messages that are loosely coupled.  Think MSMQ, or other one-way message passing applications. 

[ServiceContract(CallbackContract=typeof(ITestServiceCallback))]

    public interface ITestService

    {

        [OperationContract(IsOneWay=true)]

        void SignOn(SignOnRequest request);

    }

 

    [ServiceContract()]

    public interface ITestServiceCallback

    {

        [OperationContract(IsOneWay = true)]

        void SignOnResponse(SignOnResponse response);

    }

However, we need to call this duplex contract from an ASP.NET application, since that's how we write out clients 80% of the time.  Since HTTP is inherently synchronous, this poses a bit of a challenge.  How do you call a duplex contract, but make it look synchronous to the caller?

The solution I came up with was to use a generated wrapper class that assigns each outgoing message a correlation id (a GUID), than uses that id as a key into a dictionary where it stores a System.Threading.WaitHandle, in this case really an AutoResetEvent.  Then the client waits on the WaitHandle.

When the response comes back (most likely on a different thread) the wrapper matches up the correlation id, gets the wait handle, and signals it, passing the response from the server in another dictionary which uses the WaitHandle as a key.  The resulting wrapper code looks like this:

 

public class Wrapper : ITestServiceCallback

{

    #region private implementation

    private static Dictionary<Guid, WaitHandle> correlationIdToWaitHandle = new Dictionary<Guid,WaitHandle>();

    private static Dictionary<WaitHandle, Response> waitHandleToResponse = new Dictionary<WaitHandle, Response>();

    object correlationLock = new object();

 

    private static void Cleanup(Guid correlationId, WaitHandle h)

    {

        if(correlationIdToWaitHandle.ContainsKey(correlationId))

            correlationIdToWaitHandle.Remove(correlationId);

        if(waitHandleToResponse.ContainsKey(h))

            waitHandleToResponse.Remove(h);

    }

 

    ITestService client = null;

 

    #endregion

 

    public Wrapper()

    {

        client = new TestServiceClient(new InstanceContext(this));

    }

 

    public SignOnResponse SignOn(SignOnRequest request)

    {

        request.CorrelationId = Guid.NewGuid();

        WaitHandle h = new AutoResetEvent(false);

 

        try

        {

            lock (correlationLock)

            {

                correlationIdToWaitHandle.Add(request.CorrelationId, h);

            }

 

            client.SignOn(request);

 

            h.WaitOne();

 

            SignOnResponse response = (SignOnResponse)waitHandleToResponse[h];

 

            return response;

        }

        finally

        {

            Cleanup(request.CorrelationId, h);

        }

    }

 

    #region ITestServiceCallback Members

 

    void ITestServiceCallback.SignOnResponse(SignOnResponse response)

    {

        Guid correlationId = response.CorrelationId;

 

        WaitHandle h = correlationIdToWaitHandle[correlationId];

 

        waitHandleToResponse.Add(h, response);

 

        ((AutoResetEvent)h).Set();

 

        return;

    }

 

    #endregion

}

This worked just fine in my command line test app, but when I moved to an actual Web app, hosted either in IIS or in VisualStudio 2005, I started getting random but very frequent exceptions popping up in my client app, AFTER the synchronous method had completed.  The exception had a very long and pretty incomprehensible call stack

System.ServiceModel.Diagnostics.FatalException was unhandled
Message="Object reference not set to an instance of an object."
Source="System.ServiceModel"
StackTrace:
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Dispatch(MessageRpc& rpc, Boolean isOperationContextSet)
at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
at System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Channels.InputQueue`1.AsyncQueueReader.Set(Item item)
at System.ServiceModel.Channels.InputQueue`1.Dispatch()
at System.ServiceModel.Channels.InputQueueChannel`1.Dispatch()
at System.ServiceModel.Channels.ReliableDuplexSessionChannel.ProcessDuplexMessage(WsrmMessageInfo info)
at System.ServiceModel.Channels.ClientReliableDuplexSessionChannel.ProcessMessage(WsrmMessageInfo info)
at System.ServiceModel.Channels.ReliableDuplexSessionChannel.HandleReceiveComplete(IAsyncResult result)
at System.ServiceModel.Channels.ReliableDuplexSessionChannel.OnReceiveCompletedStatic(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Channels.ReliableChannelBinder`1.InputAsyncResult`1.OnInputComplete(IAsyncResult result)
at System.ServiceModel.Channels.ReliableChannelBinder`1.InputAsyncResult`1.OnInputCompleteStatic(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Channels.InputQueue`1.AsyncQueueReader.Set(Item item)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(T item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(T item, ItemDequeuedCallback dequeuedCallback)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(T item)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecurityDuplexSessionChannel.CompleteReceive(IAsyncResult result)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecurityDuplexSessionChannel.OnReceive(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.ReceiveAsyncResult.OnReceive(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Channels.ReliableChannelBinder`1.InputAsyncResult`1.OnInputComplete(IAsyncResult result)
at System.ServiceModel.Channels.ReliableChannelBinder`1.InputAsyncResult`1.OnInputCompleteStatic(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously, Exception exception)
at System.ServiceModel.Channels.InputQueue`1.AsyncQueueReader.Set(Item item)
at System.ServiceModel.Channels.InputQueue`1.Dispatch()
at System.ServiceModel.Channels.InputQueue`1.OnDispatchCallback(Object state)
at System.ServiceModel.Channels.IOThreadScheduler.WorkItem.Invoke()
at System.ServiceModel.Channels.IOThreadScheduler.ProcessCallbacks()
at System.ServiceModel.Channels.IOThreadScheduler.CompletionCallback(Object state)
at System.ServiceModel.Channels.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

Obvious? Not so much.

Interestingly, if I removed the lock around the hashtable insertion in the signon method

            lock (correlationLock)

            {

                correlationIdToWaitHandle.Add(request.CorrelationId, h);

            }

everything worked just fine.  Hmmmm. 

The problem, it turned out, was that ASP.NET uses (by default) a little thing called the SynchronizationContext.  As near as I can tell (I haven't researched this thoroughly, to be honest) one of it's jobs it to make sure that any callbacks get run on the UI thread, thereby obviating the need to call Control.Invoke like you do in WinForms.  In my case, that additional lock was giving something fits, and it was trying to clean stuff up on a thread that wasn't around any more, hence to NullReferenceException. 

The solution was provided by Steve Maine (thanks Steve!).  Unbeknownst to me (since I didn't know there was a SynchronizationContext) you can turn it off.  Just add a CallbackBehavior attribute to your client side code, with UseSynchronizationContext=false.  So now my wrapper class looks like this

[CallbackBehavior(UseSynchronizationContext=false)]

public class Wrapper : ITestServiceCallback

and everything works fine. 

I'm open to other ways of making this work, so I'd love to hear from people, but right now everything is at least functioning the way I expect.  Several people have wondered why we don't just make the server-side interface synchronous, but since we are calling WF internally, we'd essentially have to pull the same trick, only on the server side.  I'd rather burn the thread on the client then tie up one-per-client on the server.  Again, I'm open to suggestions.

CodeGen | Indigo | Work
Thursday, 28 September 2006 10:02:46 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  | 
# Wednesday, 02 August 2006

I’ve been doing some exploration of the Peer Channel in WCF over the last week or so.  It’s a pretty cool idea.  Basically, the peer channel provides a way to do multi-cast messages with WCF, where all parties involved get a call at (essentially) the same time.  Better still, it’s not just a simple broadcast, but a “mesh” with some pretty complex algorithms for maximizing network resources, etc. 

The hard part is in the bootstrapping.  When you want to join the “mesh”, you have to have at least one other machine to talk to so that you can get started.  Where does that one machine live?  Tricky.  The best case solution is to use what MS calls PNRP, or the Peer Name Resolution Protocol.  There’s a well known address at microsoft.com that will be the bootstrapping server to get you going.  Alternatively, you can set up your own bootstrap servers, and change local machine configurations to go there instead.  All this depends on the Peer Networking system in XP SP2 and up, so some things have to be configured at the Win32 level to get everything working.  The drawback (and it’s a big one) to PNRP is that it depends on IPv6.  It took me quite a while to ferret out that bit of information, since it’s not called out in the WCF docs.  I finally found it in the Win32 docs for the Peer Networking system. 

This poses a problem.  IPv6 is super cool and everything, but NOBODY uses it.  I’m sure there are a few hearty souls out there embracing it fully, but it’s just not there in the average corporate environment.  Apparently, our routers don’t route IPv6, so PNRP just doesn’t work. 

The way to solve this little problem with WCF is to write a Custom Peer Resolver.  You implement your own class, derived from PeerResolver, and it provides some way to register with a mesh, and get a list of the other machines in the mesh you want to talk to.  There’s a sample peer resolver that ships with the WCF samples, which works great.  Unfortunately, it stores all the lists of machines-per-mesh in memory, which suddenly makes it a single point of failure in an enterprise system, which makes me sad…

That said, I’ve been working on a custom resolver that is DB backed instead of memory backed.  This should allow us to run it across a bunch of machines, and have it not be a bottleneck.  I’m guessing that once everyone has joined the mesh, there won’t be all that much traffic, so I don’t think performance should be a big deal. 

The next step will be WS-Discovery over PeerChannel.  I’ve seen a couple of vague rumors of this being “worked on” but I haven’t seen anything released anywhere.  If someone knows different I’d love to hear about it.

Indigo | Work
Wednesday, 02 August 2006 14:10:43 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  | 
# Wednesday, 08 February 2006

I'm doing some WCF (Indigo) training this week, and one of the hands on labs went through an example of a federated trust scenario, with two STS's involved in the process.  I've got to say, I'm really impressed with how easy it was.  Granted, the configuration is pretty hairy, but it's just that, configuration.  You can set up a whole federated trust system using config files.  And it worked.  Not too shabby.  I would never have contemplated attempting something like that in WSE 2, although I think in WSE 3 it's supposed to be a bit easier. 

One thing to note, if you want to do federated trust, is that the WCF team is not shipping an STS.  Presumably for liability reasons, but that's anyone's guess.  They are, however, providing some very complete samples, which could be fairly quickly adapted for use inside one's organization.  There's also a good example STS for WSE 3 up on gotdotnet as of a few weeks ago. 

Overall, my impression is that security in WCF is very thought out, and WAY easier to bend to your will than ever before.  Check it out.

Wednesday, 08 February 2006 09:15:28 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, 23 January 2006

I’ve been playing with the January CTP of WCF, and I’ve encountered what seems like a pretty major setback.  I’ve got an interface that takes a MessageContract and returns a MessageContract.  All well and good.  But then I want to use the AsyncPattern on the service side, so that my routine will get called on a different thread from the one that’s listening on the network.  So I decorate the interface like so:

    [ServiceContract()]

    public interface IThingy

    {

        [OperationContract(AsyncPattern=true,Action="Signon",IsInitiating=true)]

        IAsyncResult BeginSignon(ThingyRequestMessage<SignOnRequest> request, AsyncCallback cb, object state);

 

        [OperationContract]

        ThingyResponseMessage<SignOnResponse> EndSignon(IAsyncResult ar);

    }

 

Now I get an exception at runtime, which says that I can’t mix parameters and messages for the method “EndSignon”.  What it means is that if I return a MessageContract instead of a primitive type, my method has to take a MessageContract and not one or more primitive types.  OK, I get that.  But my EndSignon method is getting flagged because it takes an IAsyncResult (as it must according to the AsyncPattern) and returns a ThingyResponseMessage. 

Does this mean I can’t use MessageContracts with the AsnycPattern?  If so, LAME.  If not, then what am I missing?

SOAP | Web Services | XML | Indigo
Monday, 23 January 2006 15:05:23 (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [2]  |