Note to Self: Don't Let WCF (SvcUtil) Reuse All Libraries

Friday, July 23, 2010 / Posted by Luke Puplett / comments (2)

Another short post to remind myself for when I inevitably forget some of the nuances of my nemesis, WCF. This one regards the error below:

Warning 1 Custom tool warning: Cannot import wsdl:portType Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter Error: List of referenced types contains more than one type with data contract name 'Recorder' in namespace 'http://schemas.vuplan.tv/2008/11/vuserv/entities/core/user'. Need to exclude all but one of the following types. Only matching types can be valid references: "S26.Vuplan.Core.User.ClientRecorder, S26.Vuplan.Client.MediaCenter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" (not matching) "S26.Vuplan.Core.User.Recorder, S26.Vuplan.Core, Version=1.0.2.0, Culture=neutral, PublicKeyToken=80726da9f797f65e" (matching) XPath to Error Source: //wsdl:definitions[@targetNamespace='http://schemas.vuplan.tv/2008/11/vuserv/user']/wsdl:portType[@name='IUserService']etc. yeah yeah.

I don't know why the WCF team didn't add a proper UI to this. To see and configure the mappings between a service and your client app, as well as a more wizardy interface and perhaps a more verbose "here's what I'm doing now" style feedback system would save hours.

I have a core library in which a Recorder object lives. This core library is used on server and client and defines many entities and other pan-application helpers and types. The problem comes when I define a derived type of Recorder for the client to use, which has more client specific logic in it. I want to new up a ClientRecorder and then pass the thing back up to the cloud, so I add its Recorder DataContract, and that's when it all falls apart.

The SvcUtil has been instructed to reuse types in all referenced assemblies, so when it tries to map a Recorder contract to one of my types, it can't tell if I want to use the one in the core lib or the new client one.

I could rem out the DataContract line and reimport, this'd work, but the fix is to make sure that I don't try and reuse types in my client assembly.

The config page for the Service Reference doesn't allow excludes, so I have to tick almost all assemblies except the ones I definately know don't contain serializable classes. See pic:

Service Reference Reuse

By doing it this way, I should be able to hit Update at any point in the future and not suddenly be thrown out by the error and the massive knock on effect it has to compilation.

Labels: , ,

Enum DataContract Breaks Type Reuse

Wednesday, May 13, 2009 / Posted by Luke Puplett / comments (1)

Problem and Background

A few weeks ago I got a message stating that I needed to add DataContractAttributes to my enums. I can't remember the details but I know I thought it was strange as it was all working without them. I added the attribute and got another message about not having EnumMemberAttributes on the actual enums values. I obliged and the errors went and I thought that was the end of it.

Today, while coding in the client app, I discovered that my types had properties with Specified appended. The suffix is apparently only added when you reference an old ASMX from WCF. But I'm not, so my suspicions were aroused. Furthermore, my setup is that I reference a shared assembly so I shouldn't have generated types at all..!

Solution and Bug

Had I setup some decent tests then I should have spotted the problem as soon as the change was made three weeks ago because - and I really dislike WCF for its ability to throw a curveball in your face every day - the root of the problem became clear when I rolled back and forward my enums.

It appears that the codebase was alright again (no errors) with the version that didn't include the DataContracts on my enums. While in the source, I noticed that I'd missed decorating EnumMember on some of the values.

I then rolled back again to the working snapshot and added DataContract to the enum but did not add EnumMember to anything. The result was that my working codebase broke - that is, when I updated my client service reference, types were generated and not re-used from the shared DLL.

You guessed it: adding EnumMember to all members of the enum with the DataContractAttribute and recompiling, re-updating the reference, fixed it again so I was re-using my shared lib.

This has got to be a bug. The compiler or the WSDL page should be spitting feathers and not letting me continue to code with the corrupted service state.

Update 1

This morning, after settling on the conclusion that I needed to add EnumMember to all my enum values I found that this actually didn't solve the problem and the solution was to remove all DataContractAttributes from all enums which meant foregoing my service having corporate domain namespaces for every single type - although I will look into using the assembly-level ContractNamespaceAttribute attribute.

Update 2

Further tests show that svcutil making a mess of things might only occur when FlagsAttribute is also applied.

Labels: , ,

One Service + Two Client Platforms = Misery

Friday, May 08, 2009 / Posted by Luke Puplett / comments (0)

Aim: I want to be able to transmit a username and password hash and see it easily from within the service.

My idea of Hell is WCF. I liked .NET Remoting and I got on with Web Services, but the unified Windows Communication Foundation is a quagmire of configuration and complexity, not least because its half-merged with IIS, which is sort of merged but not merged with ASP.NET.

Today, I am tackling the issue of using the same WCF Service that my Silverlight client uses, for a fat desktop client.

Making a Meal of WCF and Cookies

The scenario to the uninitiated might look straight-forward, after all I’m connecting two Microsoft .NET-based products to a Microsoft .NET-based SOAP service, all running in the same VS solution on one box.

The problem is that Silverlight runs in the browser and has an authentication scheme which uses its host browser’s HTTP stack and cookies, while the other client, despite being fat, has never even seen a cookie.

To be clear, my WCF services end uses Microsoft’s AuthenticationService to provide Forms authentication behaviour over the HTTP connection between SL client and service, which basically does the cookie creation and response so I don’t have to. This works great but I what about my non-Silverlight clients?

While Jonas Folleso has one solution to the problem, by detaching the authentication cookie and sticking it back on to all outgoing headers, I wanted to take the ‘easier’ route: exposing another service end-point that supports some kind of authentication.

I want to do this because I want my client to sign-in using the SSL secured AuthenticationService, set-up a session with some shared secret key and then in all subsequent requests to my other services, send over a hash of something using the shared secret.

I’m actually writing this as I work on it, so as these words are being typed, I don’t know what to expect.

Step One: Add a new EndPoint and connect to it

I add a new EndPoint to the web.config of my IIS service host, using the same basic binding as the working service, just for simplicity and testing.

<endpoint address="/vpr"
binding="basicHttpBinding"
contract="IUserService"
/>

Lesson - While doing this, I learn that the <baseAddresses> section is pointless on IIS because its IIS and the base address will be wherever the .svc files are.

I copied the chunk of XML that defines the mex endpoint, and like mex and according to MSDN, my service endpoint address will be relative to the base URL which is the svc file, I presume.


Adding a Service Reference to any of the URLs above fails with error saying:


The request failed with HTTP status 400: Bad Request. Metadata contains a reference that cannot be resolved: 'blah'.

Lesson – You only need to point the Add Reference box to the .svc file and the other endpoints are read from the WSDL (you can see this because the client app.config will fill with the server endpoints).

mex is an exception to the rule and can be referenced by putting /mex after .svc, also the discover option doesn’t see the endpoint as being a different service.

This is all how it should be and I think endpoint addresses are actually just ID strings which help WCF route messages into the right worker queue for processing on the server but are structured as URLs to confuse you.

Step Two: Choose a Binding

The aim is simply to choose a binding which puts the plumbing in place for me to transmit credentials. So first up, I try just setting ClientCredentials.UserName to something useful and checking it on the server via ServiceSecurityContext.Current, but get a null ref exception.

I also try disabling anonymous logon from the IIS console in case this is causing my username to be lost, but this causes everything to break – I can’t even connect to get the service description.

Lesson – Ignore the bit about disabling anonymous access.

I persist with the trusty basicHttpBinding because MSDN says:

By default, the BasicHttpBinding class does not provide security. This binding is designed for interoperability with Web service providers that do not implement security. However, you can switch on security by setting the Mode property to any value except None. To enable transport security, set the property to Transport.

I do as it says but I get this error upon updating my Service Reference:

Could not find a base address that matches scheme https for the endpoint with binding BasicHttpBinding. Registered base address schemes are [http].

The problem is that I don’t want to use HTTPS. Time to try another tack: Message Security, which MSDN tells me

means that every message includes the necessary headers and data to keep the message secure. Because the composition of the headers varies, you can include any number of credentials. This becomes a factor if you are interoperating with other services that demand a specific credential type that a transport mechanism can't supply, or if the message must be used with more than one service, where each service demands a different credential type.

Here goes nothing.

        <binding name="vpr">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>

I refresh my Service References and receive:

BasicHttp binding requires that BasicHttpBinding.Security.Message.ClientCredentialType be equivalent to the BasicHttpMessageCredentialType.Certificate credential type for secure messages. Select Transport or TransportWithMessageCredential security for UserName credentials.

Right then. TransportWithMessageCredential it is, or at least it would be but that of course leads me right back to:

Could not find a base address that matches scheme https for the endpoint with binding BasicHttpBinding. Registered base address schemes are [http].

An MSDN article on using the ASP.NET Membership Provider reckons I can use WSHttpBinding set to Message Security to transmit a user name, but goes on to talk about adding service behaviours for the membership provider. I will try most of it (as I don’t want to use the ASP.NET MemProv). I put

    <serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"/>
</serviceCredentials>

..in the behaviours element with this in the binding element:

    <security mode="Message">
<message clientCredentialType="UserName"/>
</security>

But this gives this error when adding/updating the Service Reference:


UserNamePasswordValidationMode.Custom requires a CustomUserNamePasswordValidator. Specify the CustomUserNamePasswordValidator property.


I may be on to something... searching microsoft.com reveals an article entitled User Name Password Validator.

So I’ve written a test class called CookielessCredentialValidator which extends System.IdentityModel.Selectors.UserNamePasswordValidator and just throws a NotImplementedException on the only overridable method is has, Validate() and changed my web.config:

<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="App.Web.CookielessCredentialValidator"/>

Upon updating my service reference I get:


Could not load type ‘App.Web.CookielessCredentialValidator’ from assembly ‘System.ServiceModel, Version=3.0.0.0, Cul... etc.

Looking back at the MSDN article I realised that I need to format the type name differently, prefixing my derived class with the name of my service class and maybe the word service after it? Not really clear and the article on customUserNamePasswordValidator provides no help:

Looking further into this, it seems that I must create an X509 Certificate.

Lesson – You can’t send credentials without HTTPS or some other encryption method in the .NET Framework, even if you do have you own method for encrypting the data you wish to put in the username and password properties.

Step Three: Go back to Jonas Folleso’s Blog and do what he did

...and pray that Microsoft changes this nannying part of the framework before I get customers trying to use my service from a platform that can’t fiddle around with cookies.

The End.

P.S. Sorry about the extra space around blockquotes, the Blogger system seems to want to add breaks each and every time I place a carriage return in pure HTML - bizarre and annoying in equal measure.

Labels: , , , ,

Silverlight to Service Layer: Sharing a CoreLib

Monday, April 27, 2009 / Posted by Luke Puplett / comments (0)

Technorati Tags: ,,

We all know that sharing is caring but when it comes to Microsoft’s RIA client you may find sharing is tearing, your hair out. In this post I will explain how you can code a class once but use it in both your WCF service, and the Silverlight codebase that consumes it.

While architecting a cross-browser web app, I made a big assumption: that Microsoft’s RIA platform would be CLR-based and so my types could be passed (via WCF) though all my tiers; with a simple Add Reference from Silverlight, I’d have access to all my business entities. As it turned out, I couldn’t.

True, Silverlight has the CLR we all know and love, but it ain’t the same CLR. While the Add Reference dialog will lead you up the garden path to your full-fat assembly, the sucker punch goes like this...

“You can’t add a reference to AmazingLibrary.IspentAgesWriting.dll as it was not built against the Silverlight runtime. Silverlight projects will only work with Silverlight assemblies.”

So where to go from here? The answer is to share your code files. The VS 2008 IDE (I haven’t looked in 2005) allows you to link a project to source files with the click of its often overlooked combo-button - I know this is often overlooked because I’ve seen others hacking around with their solution files to achieve the same thing.

 
Right click on the Silverlight project or a folder you have within it and choose this option:

01.Add Existing Item Menu

 
Then take the Add dialog to where you store the code files for your full-fat assembly or types but instead of hastily stabbing at the Add button, drop it down and choose Add As Link:

02.Add As Link Menu

 

You can highlight and link multiple files using CTRL, although once linked you will need to be aware of a few more things.

  1. All edits are shared - because its now linked - but the VS editor will be ‘wearing the context’ of whichever type of project the file was opened from.

    If you double-click the file under your Silverlight project tree it will impose the Silverlight context, such as intellisense, references from the Silverlight project and stuff that just isn’t included in the skinny SL CLR.

  2. You may also need to use the new SILVERLIGHT compiler keyword to section-off lumps of code that don’t compile in SL and section-in alternatives that do.

    #if
    !SILVERLIGHT
        using FullCLR.Types
    #endif

  3. If, like me, you’re sharing entity types for marshalling between a WCF service and a SL client, you may need to add data contract names and namespaces and then update your service references. Of course, you only need to add this once on each shared class.

    /// <summary>

    /// Represents a finite period of time with a start and end.
    /// </summary>
    [DataContract(
        Name = "TimeRange"
        Namespace = "http://schemas.co.com/2008/11/entities")]
    public sealed class TimeR...

    When you hit build, the compiler’s going to ensure that the same data contract metadata ends up in both the service and the client libraries and so when you add a service references, the proxy should map these types rather than generate classes.

     

Note that sharing entities in this way may not be suited to your design philosophy or coupling strategy and that code in the classes that forms the Silverlight client will be publicly available for scrutiny. In my own project I use this technique for ‘hollow’ data contract entities that otherwise contain no business logic.

Luke

Labels: , , ,