Monday, July 30, 2007

I've spent a number of the last posts mostly griping about what's broken in Orcas Beta2. I figured I'd change the tone a bit and talk about some of the new goodies that I like!

- WebHttpBinding - This thing is going to rock hard when the bugs get squeezed out. To be fair, it hasn't had nearly the soak time of the other WCF bindings, either. In general, I'm really impressed with the combination of WCF + WebHttpBinding for POX and REST-style messaging. I love using the same service implementation to handle server-to-server and client-server communication, without having to compromise performance or code to a "least common denominator". Well, I'm almost there- I still end up building AJAX-friendly versions of most of my service contracts, at least the ones that accept HTTP GET. I don't think serializing a DataContract instance as JSON on the query string was what the REST-afarians had in mind, and I'm in the "explicit MessageContract" minority, so I don't have much choice.

- New IDE goodies, refactors, cleanups

"Organize Using"- nicely handles one of my little pet peeves: sloppy/dead "using" statements. I see this as a long-needed tool for cleaning up after the oh-so-useful-but-mess-making "Resolve Using" command.

Multi-targeting- so I can be sure at compile-time that I haven't inadvertently introduced 3.5 dependencies in our 2.0/3.0 only projects

Intellisense improvements- Ctrl-for-transparency, showing both member docs and intellisense at once, Javascript intellisense(!)

- Extension methods. This one's gonna be tough not to abuse, but lemme tell you: hanging .HasContent() on every string, null or not (wrapper over ! String.IsNullOrEmpty()) is one really nice bit o' syntactic sugar...

- LINQ- I'm in love. LINQ to SQL is coming along nicely on the march to RTM, and does just about everything I need right now. Sad to see DataShape get hidden back away under the LINQ to SQL umbrella as DataLoadOptions in B2 (could be useful for other stuff, which I'm sure was the initial intent before it was locked down to SQL Server only), but oh well. LINQ to Objects is another one that'll be tough not to abuse- I already have a couple of places where we're joining LINQ to SQL and LINQ to Objects-based queries together to present a unified log and history view across multiple service tiers. I'm excited to see where LINQ takes us in the next few years- both in MS-built query providers and third party stuff. I really hope to see LINQ-enabled providers over WMI and LDAP from Microsoft soon. The lack of the latter (and yes, I'm aware of Bart DeSmet's work in this area, but it's not ready for prime-time) was the final nail in the coffin for ADAM on our project. Implementing our user store from scratch in SQL Server was much more palatable since LINQ to SQL removed enough of the impedance mismatch- it was just going to be too much double-definition and code->directory sync with ADAM- we're in a bit of a hurry. If I had a lot more time on my hands, I'd jump in and pick up the ball on LINQ to LDAP- I think it's a very interesting project, and Bart's got a great start on it.

Anyway, so far, so good- I haven't hit any issues I wasn't able to work around, which is good- we're planning on going live (or at least beta) with the Orcas Beta2 bits.

Yikes. The following statement from the .NET 3.5 B2 "known issues" made me a little nervous:

For WCF HTTP streaming, the stream that is returned to the client is not the same size as the stream size requested in some stress situations. The service can send extra bytes to the client side due to a known racing issue in the Visual Studio 2008 product code.To resolve this issue:Avoid using the HTTP streaming feature for WCF. Um, I need HTTP streaming- my stress tests max out my server memory if I do things buffered, and the stress tests don't even simulate slow clients yet. I set up a test to try and reproduce the behavior (just to see how bad it is). I wasn't able to see that behavior, but I did see another nasty one. If a client disconnects from a webHttpBinding service while a response is being streamed, the HttpListener throws an unhandled exception on an I/O completion thread. In a self-hosted scenario (like mine), unhandled exceptions take the process down, which effectively means part of my app is down until someone brings it back up!

Luckily, the legacy .NET 1.1 unhandled exception backstop is still available via configuration, so that will keep my process alive until they get the problem fixed.

Saturday, July 28, 2007

This is mentioned in the "known issues", but in a backhanded way that I didn't notice 'til I'd already filed a bug ("Running some WCF-based project templates results in a crash of svcutil.exe crashing due to a signing issue"). If you need to use svcutil to generate client proxy code in VS2008 Beta2, you'll have to apply a little hack. Seems they built it for delay signing, but never actually signed it- if you run it, you get the error quoted at the bottom of the page. You can work around this by preventing strong name verification for the svcutil assembly. Run:

Minor issue- .sln files created by Orcas Beta1 do nothing when double-clicked in Explorer. Hovering over the icon in Explorer shows "Version: (unrecognized version)". Everything works fine if you just open the solution file in Visual Studio.

Turns out the trick is in the comment on the third line of the solution file. For solutions created by Beta1 (and I'm assuming, anything older), the third line looks like:

# Visual Studio Codename Orcas

In Beta2+ solutions, it is:

# Visual Studio 2008

Change the line in notepad (or whatever), and all is well- you can open the solution by double-clicking in Explorer again.

UPDATE: Figured this one out! I was always using a fully-qualified type name on my enums- for this to work, you have to prepend the global:: prefix. Unqualified type names work fine, as long as the type is visible from the generated LINQ entity class namespace. I filed this as a bug...

-----

Has anyone successfully managed to use the Orcas Beta2 (sorry, VS2008) bits to do first-class enum mapping onto an int column? My Beta1 code is doing the "define another property that does the mapping from int->enum and back" trick, but I'd like to let the mapper do it for me now that it's supposedly supported. I've got my enum defined, but when I set the 'Type' field on the column properties in the O/R designer to the FQ name of the enum, I get the following error:

DBML1005: Mapping between DbType 'Int' and Type 'AnApp.AnEnum' in Column 'EnumDBColumn' of Type 'ATable' is not supported.I know it's finding the type- I get a different error if I use a bogus type name. Grr. I'm hoping I'm just doing something dumb- I really want this to work!

Friday, July 27, 2007

I'm loving the new WebGet and WebInvoke programming model in WCF- the URITemplate binding style is so natural. Just poke up a request URI with some placeholders in it, and WCF magically maps it to the right operation on my contract and plugs in the arg values... It'd be even cooler if it supported something akin to WPF binding paths (allowing you to bind querystring values to any property on any object), but I'm still thrilled with what we have, especially how much better it is than the Beta1 stuff!

This being beta software, however, there are a few rough edges. Unfortunately, WebGet operations don't support nullables. That means I have to botch up my existing contracts that have "optional" int typed args to take strings (so I can distinguish missing values from default values- I get this with my enums already because I always define a 0 "Undefined" value). If you try to use a nullable in a WebGet OperationContract, you'll end up with:

Operation 'Op' in contract 'Service' has a query variable named 'nv' of type 'System.Nullable`1[System.Int32]', but type 'System.Nullable`1[System.Int32]' is not convertible by 'QueryStringConverter'. Variables for UriTemplate query values must have types that can be converted by 'QueryStringConverter'.

I've filed a suggestion to see if they can add support for nullables. Go vote for it! I guess I'm the Nullable b*tch- I went and counted. Over the years, I've filed six different bugs on various .NET APIs support for nullables, and blogged about at least one of them.

UPDATE:

The situation is worse when using enableWebScript with the JSON bits. The JsonQueryStringConverter says it supports Nullables (eg, its CanConvert method returns true when a Nullable comes in), but it barfs if the value is actually null when you call ConvertStringToValue. This is even worse- my service starts up fine, but fails with a NullReferenceException in the JSON deserializer the first time I omit a nullable value from my query string. Ick. Bug filed.

Been playing around (actually, trying to get real work done) with shiny new Orcas Beta2 bits. I've been working on Orcas since the day Beta1 came out, and I've been eagerly awaiting the integration of WebGet/WebInvoke and full-fledged JSON serialization that Steve Maine's been talking about for Beta2. There are a couple of weirdnesses:

Steve suggests the use of WebServiceHost when hosting a WCF service that uses the new JSON stuff and WebGet/WebInvoke, because it wires up the right behaviors for you and checks a few other constraints (like only supporting one contract per endpoint address)... Turns out it also creates some new problems.

The enableWebScript endpoint behavior is what turns on all the JSON goodies. By default, WebServiceHost wires up a POX-emitting WebHttpBehavior (of which WebScriptEnablingBehavior is a subclass) on every webHttpBinding endpoint. It doesn't check first to see if there's already a webHttp behavior there. Looks like this means that if you want JSON serialization in B2, you have to use a vanilla ServiceHost and wire up the enableWebScripting behavior yourself. Otherwise, if you try to use a DataContract on a WebGet operation, you'll get the following InvalidOperationException on the .Open() call:

"Operation 'Bla' in contract 'Service' has a query variable named 'v' of type 'BlaDataContract', but type 'BlaDataContract' is not convertible by 'QueryStringConverter'. Variables for UriTemplate query values must have types that can be converted by 'QueryStringConverter'. Unfortunately, WebServiceHost hardwires webHttpBehavior on all webHttpBinding endpoints."

It looks like this won't be a problem forever: Reflector shows a WebScriptServiceHost that automatically plugs in enableWebScript (thanks, Lutz!), but it's marked internal. IIS users can probably use the WebScriptServiceHostFactory in a .svc file (since that's public), but I'll stick to the standard ServiceHost for now and just make sure I follow all the rules.

Friday, July 6, 2007

Part of the application I'm developing at work right now will need to serve a metric buttload of images and metadata to a lot of clients concurrently. I'm using Orcas Beta1 + WCF to build everything (gotta love that bleeding edge), and I'm blundering into a whole host of different security issues. The big one is XmlHttpRequest just plain not supporting the "same host, different port" scenario, regardless of the zone security settings. We're currently doing our development on Windows XP, though the app will deploy on Server 2003. For performance reasons, the mailbox servers won't run in IIS with the rest of the app- they run in their own service processes. That's fine on Server 2003- HTTP.SYS lets us divvy up port 80 between IIS and our app. The problem comes on our XP development machines. Neither the built-in VS webserver nor IIS 5.1 support HTTP.SYS- they hog their target ports all to themselves, which means the mailbox services have to run on different ports from the "main" webserver. One of those services handles JSON-encoded AJAX requests via XmlHttpRequest- this causes us problems since all the page code comes from localhost:80 (or whatever port VS chooses), but we're trying to fetch data from the JSON service at localhost:8081. XmlHttpRequest just plain doesn't support this (and is documented as such). Bummer.

So I'm stuck with one of the following solutions:

1. Set up Apache to listen as a reverse-proxy on yet another port and "merge" the URL space of the different services together (ie, Apache maps localhost:8082/Web to Cassini at localhost:8080, localhost:8082/MailData to my JSON service, and localhost:8082/MailImages to the image service (both listening on 8081- WCF uses HTTP.sys to share ports if it can). This takes care of the cross-port problems, but it's a hassle to show a mere mortal dev how to set up multiple instances of the app and actually have them work.

2. Upgrade all our dev boxes to Vista and develop against IIS7 instead of Cassini. Tempting, but I don't have time to repave right now, and doing an "upgrade" on the OS feels dirty.

3. Upgrade our dev boxes to Server 2003. Again, tempting, but not very cost-effective, and the same hurdles exist from #2.

4. Just host the services in IIS/Cassini for development (via WCF .svc files). I don't really like this option, especially since the production servers will likely be redirecting to a different host (same domain) for images and JSON mail data- too many things can go wrong when you develop in the same URL space and don't deploy that way (though we are contemplating hiding the different hosts behind a content load balancer, which would make this option a little more palatable).

5. Try and pull a switcheroo on the VS webserver to a version that supports HTTP.SYS (I swear that an HTTP.SYS-based WebDev.Webserver2 used to ship with VS2005- what happened to it?). This project looks like it allows exactly that, though I had to make a change to it, since it was always binding to '/', regardless of the vroot passed at startup- not very conducive to sharing ports... Oh well- at least he's kind enough to provide the source!

I'm currently leaning toward this last one. It's a one-time setup on every dev's box to "replace" the built-in VS webserver- then we just set up our project defaults to use the same port as our default configs. If someone wants to set up another instance, they can change that instance's ports in WCF config and VS debug config.