Friday, January 7, 2011

MSBuild, Visual Studio and 64-bit Windows == Huh?

So this is kind of lame. Let's say you've got a web app in VS on a 64-bit version of Windows. Everything's all good and you decide that instead of building from VS you now want to build the project directly from MSBuild, either by pointing at the .sln file or the .csproj. Should just work, right? Wrong.

Your build will fail with this:
"The imported project "C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" was not found."

You see, there's an element inside of your .csproj that looks like this:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" />

This apparently pulls in some additional targets that MSBuild needs to do its thing on web apps. Unfortunately, when running MSBuild from the command line on a 64-bit maching the $(MSBuildExtensionsPath) variable refers to:

C:\Program Files\MSBuild

but the stuff that it's looking for only exists in the 32-bit version of this directory at:

C:\Program Files (x86)\MSBuild\

Let me just say right now that:
1. I have no idea why this isn't an issue when building in VS. I believe it's still using MSBuild to execute, but maybe the variable is being correctly set to the 32-bit program files directory in this case. Or maybe it's running a 32-bit version of MSBuild?!
2. Why this stuff only lives in the 32-bit program files directory. The other directory exists but it only appears to house some WF stuff.

Anyway, I found an MS Connect report (from 2006!) on it but they're claiming that it's "by design" and not a bug. Their proposed work-around is simple, but really lame - copy the files over into the x64 program files directory. Like I said simple, but lame.

Here's the link: http://connect.microsoft.com/VisualStudio/feedback/details/109232/msbuild-msbuildextensionspath-returns-invalid-path-on-x64

I copied the stuff over and it did seem to solve the problem, but we'll see what sort of downstream issues come up later when some installer isn't expecting 32-bit versions of that stuff to be sitting in there...

Not impressed on this one MS.

Add to del.icio.usDiggIt!RedditStumble ThisAdd to Google BookmarksAdd to Yahoo MyWebAdd to Technorati FavesSlashdot it

Tuesday, December 14, 2010

Invasion of the Response Body Snatchers or How I Learned to Stop Worrying and Love IIS

Today I got burned for the second time on this issue. And you know what our former Commander in Chief said about that: "Fool me once, shame on you. Fool me twice - uhh - won't get fooled again."

So in the interest of not getting fooled again. Here's the deal, all laid out in plain English for the future me to read the next time I have this problem, well, in the future.

I have a lovely little RESTful web service (ASP.NET MVC) that has some (IMHO) elegant exception handling built in. The controller action is wrapped in a nice big try-catch and when an exception is thrown the catch block passes the exception to a class that logs the failure and then creates a "Fault" message for the caller (basically an XML representation of the exception details). The response status code is set to 400 - Bad Request and the response body is set to the aforementioned XML "fault".

This code all works fine, that is, until it doesn't. When hitting the service locally everything is fine and the correct XML "fault" message gets returned in the response body, but from a remote client the response body is simply the 11-byte string "Bad Request". Pretty baffling behavior, until you figure out what's going on and understand the rationale behind it. Turns out that this behavior is by design and built into IIS.

At first blush this looks Just like remoteOnly behavior in web.config. The idea there being to prevent gifting sensitive details (like DB connection strings and the like) to potential hackers and foreign spies in the guts of an error response. All well and good. Only I couldn't fix this problem by setting customErrors mode="off". It turns out that this basic functionality is now built into IIS as well, downstream of the handler processing the request. That is, *after* the request has been processed by my code and the response created. It's like IIS sees my 400 and says to itself "Uh oh, trouble in paradise. And look, this is some shifty outsider causing this mess. I better wipe out the response body and replace it with something innocuous. Oooh, I know! How about the text 'Bad Request'? Makes me sound tough! Perfect!"

[N.B. I know, I've broken one of the cardinal rules - "Never anthropomorphize computing equipment" - but I think it's pretty innocent in this case. It's a web server after all. It's not like it's going to take over our nuclear power plants and enslave humanity or anything.]

Anyway, it turns out there's now an httpErrors element in web.config and applicationHost.config that supersedes what's in customErrors. So if you want to display the real response body on anything that has a response code of 400 or greater you now have to set this to allow detailed error messages to be displayed to remote clients (either directly in the file, or via IIS Admin).

(As a side note - I believe this is new to IIS 7.0, I haven't found any historical info on this, but I'm pretty sure that prior to IIS 7.0 things didn't work this way. Either that or I'm getting a little bit senile in my old age. Anyone know which it is?)

Why would you want to override this behavior and make your site vulnerable to terrorists? Remember, you're either against us or with us! Well in my case I was doing integration testing with a new partner deployment on QA server and needed the detailed info troubleshooting. Actually, I can think of several scenarios where you don't want this behavior, but I don't fault MS for making it the default. In fact I definitely think they made the right choice. I just wish someone over there would've picked up the phone and called me. Come on guys, one quick call to let me know about this.

Anyway, here are a couple of links that get into more detail.



Or if you just want the guts...

In web.config, comment out customErrors in system.web like so:


<!-- customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors -->


and then add to system.webServer the following:


<httpErrors errorMode="Detailed"/>


If anybody is thinking to themselves "Duh. It's always worked this way." Please let me know. I'll accept this assertion with minimal evidence, I just really don't remember ever having this problem prior to IIS 7.

Add to del.icio.usDiggIt!RedditStumble ThisAdd to Google BookmarksAdd to Yahoo MyWebAdd to Technorati FavesSlashdot it

Sunday, December 5, 2010

Disk Defrag Tools

Had to crunch down the physical size of my HDD in order to resize the partition so it would fit onto a smaller SSD. PerfectDisk worked better than any of the other defrag tools hands down (including the built-in Windows' defrag which was basically useless.)

http://www.raxco.com/

PS - SSD is a OCZ Vertex 2. Amazing speed.

Add to del.icio.usDiggIt!RedditStumble ThisAdd to Google BookmarksAdd to Yahoo MyWebAdd to Technorati FavesSlashdot it

Wednesday, November 10, 2010

Enable Tracing in ASP.NET MVC

Not as obvious as I had first thought.

First, in your MVC app's web.config add this (as a child of the root
<configuration> node):


<system.diagnostics>
<trace>
<listeners>
<add name="WebPageTraceListener"
type="System.Web.WebPageTraceListener, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</listeners>
</trace>
</system.diagnostics>


Next, turn on tracing. Again in web.config add the following (as a child of the <system.web> node):


<trace enabled="true" localOnly="false" mostRecent="true" pageOutput="false" />


(I won't get into thew details on this element but MSDN has all the info here: http://msdn.microsoft.com/en-us/library/6915t83k.aspx)

Now add some Trace.WriteLine() type calls to your controller action, build and run it.

Then you should be able to go to http://localhost/myapp/Trace.axd (or wherever your app resides) and see the list of recent requests and all their trace details, just like in old-skool ASP.NET web forms apps.

Of course, you could also skip all of the above and just use the built-in Controller.HttpContext.Trace class in ASP.NET MVC. It's already wired up and just basically works, but if you've got a bunch of plumbing built on top of System.Diagnostics.Trace (like NCore's Spy class, for example) then that's not going to do you any good.

PS - Yes, I know it's been a *long* time since my last post, but that's just what happens with a baby around the house. It'll get better when he's old enough for me to pay him to write my blog posts.

Add to del.icio.usDiggIt!RedditStumble ThisAdd to Google BookmarksAdd to Yahoo MyWebAdd to Technorati FavesSlashdot it

Saturday, June 26, 2010

Is that a transaction log file in your pocket, or are you just happy to see me?

So after much research and muddling around I’ve found that the following works consistently for truncating *and* shrinking the transaction log file in SQL 2008:

Do a full backup and then a transaction log only back-up, then run this (where “MyDb” is the name of your DB and “MyDb_Log” is the name of your DB transaction log file):

USE MyDb
GO
ALTER DATABASE MyDb SET RECOVERY SIMPLE
GO
ALTER DATABASE MyDb SET RECOVERY FULL
GO
DBCC SHRINKFILE(MyDb_log, 1)
GO


The DBCC SHRINKFILE should display results indicating a 1m log file at this point.

I know this seems a bit weird but I was just able to shrink the a transaction log from 1.6 gig (!) down to the default minimum size of 1 meg.

Add to del.icio.usDiggIt!RedditStumble ThisAdd to Google BookmarksAdd to Yahoo MyWebAdd to Technorati FavesSlashdot it

Thursday, February 25, 2010

LINQ + .ToList() + .ForEach() + Anonymous Methods = Serious Mojo

Just stumbled onto this combination.


XElement root = XElement.Parse(
@"<myxml>
<a/>
<b/>
<c/>
</myxml>");

root.Descendants().ToList().ForEach( delegate(XElement element){ element.Add( new XAttribute( "moo", "car" ) ); } );

Console.WriteLine( root );

Accepting the implications of .ToList'ing your LINQ query, this is some powerful stuff.

PS - If you don't know what I'm talking about with the whole "implications of .ToList'ing your LINQ query" you should really buy the LINQ Pocket Reference. And if you want to easily try the example you should download LINQPad. It's free, excellent, and written by the authors of the aforementioned book.

Add to del.icio.usDiggIt!RedditStumble ThisAdd to Google BookmarksAdd to Yahoo MyWebAdd to Technorati FavesSlashdot it