Friday, July 24, 2009

Seek, and Ye Shall Find - Visual Studio Find & Replace Tips

Today, I want to share with you a few tips to find your way through the real jungle, my code!

Our project is huge. 2500+ files and growing. Those aren't all image files either, wise guy!

Because of the size of our project I have always experienced a extremely slow response when I use the "Find" feature, [ctrl]+[f], in Visual Studio when I did an "Entire Solution" or "Current Project" search. This pause became unbearable. It was like standing in the middle of a jungle just waiting to get eaten. The suspense drove me nuts! Would it find anything? Would I die before I found out?! I couldn't take it.

So, as most adventurers do, I started experimenting.

The first thing I tried was "incremental search" [ctrl]+[i]. This is really cool because you don't get a pop up window, you just type [ctrl]+[i]+{whatever} and Visual Studio will start to search for "w", then "wh", then "wha", then "what", etc... I liked searching without the pop up. It was very convenient. If you find what you are looking for but you need the next match just type [ctrl]+[i] again and it will jump to the next match. [ctrl]+[shift]+[i] will allow you to search backwards through the file. Pretty cool and pretty handy. Another benefit of this is that if you always use incremental search to search within the current file that allows you to leave the traditional "Find" dialog set to "All Open Files." I believe "All Open Files" is the best use for the traditional "Find" dialog.

But wait, there's more. Order within the next 10 minutes and get free shipping.

Incremental search is nice for small jobs, sort of like an explorer's pocket knife but what should you use when you need a machete?

The answer you seek is "Find in Files." [ctrl]+[shift]+[f].

"Find in Files" is the real secret that ties my whole search strategy together! First of all, loosely speaking it is 4000 times faster than regular find because it runs asynchronously. As soon as it starts to find matches the matches appear where you specify (generally the "Find Results 1" toolbar/window). Once items appear in the "Find Results 1" window you can start double clicking on them to be transported to that spot in the code.

Here's another secret though that makes this even more powerful. That list is persisted in that window even if you do a regular "Find". So, you can have two sets of find results at the same time. There is also a "Find Results 2" pane if you need a 3rd. Now, it gets even more cool!! [Suspense Builds.... sort of like when you wait for the regular find to return a result] Because the next match of a regular find can be accessed using F3 you can move through those regular find results while moving through your list of "Find in Files" results using F8. The 2 lists can really be worked on simultaneously!

Keep in mind that any time you have a code helper window (e.g. the error list, the bookmark window, the build output window, etc...) with code line markers in it you can double click to navigate to that spot in the code AND you can use F8 to move to the next item in the list. This is a huge timesaver for me.

Just as there is a "Replace" [ctrl]+[h] command, there is also a "Replace in Files"[ctrl]+[shift]+[h] command. One thing to keep in mind is to UNCHECK the "Keep modified files open after Replace All" if you are going to modify a large set of files. Generally, loading all of those files into the IDE will slow the operation down tremendously and may cause your IDE to lock up.

So, the next time you are lost in a really big jungle, remember [ctrl]+[shift]+[f].

By the way, when a machete is not enough I like to use a bulldozer. I am currently developing a bulldozer that will do multi-file, multi-line, regular expression enabled find and replace. Keep checking the blog (or subscribe) to see when it becomes available.

Web Services - Now, that's an adventure

Recently we got this wonderful exception at work:

WebHost failed to process a request.

Sender Information: System.ServiceModel.ServiceHostingEnvironment+HostingManager/4110503

Exception: System.ServiceModel.ServiceActivationException: The service '{ServiceName}.svc' cannot be activated due to an exception during compilation. The exception message is: Operation '{MethodName}' of contract '{InterfaceName}' specifies multiple request body parameters to be serialized without any wrapper elements. At most one body parameter can be serialized without wrapper elements. Either remove the extra body parameters or set the BodyStyle property on the WebGetAttribute/WebInvokeAttribute to Wrapped.. ---> System.InvalidOperationException: Operation '{MethodName}' of contract '{InterfaceName}' specifies multiple request body parameters to be serialized without any wrapper elements. At most one body parameter can be serialized without wrapper elements. Either remove the extra body parameters or set the BodyStyle property on the WebGetAttribute/WebInvokeAttribute to Wrapped.

I don't usually blog about exceptions because I cause so many of them! I'd never have time to make more if I wrote about each one! Well, actually, usually someone else has already thoroughly blogged about it. In this case, I did not stumble upon a blog for this exception. It was nearly as exciting as the birth of another child. So, I'll name it... "Targort".

So, where did Targort come from and how was Targort corrected?

I cannot answer that without putting a little adventure into the story first. So, we had a huge demo on the horizon and we'd been testing and tweaking for weeks. The development machines were smoking! The build server was sweating and gasping for air and the demo server looked to be coming online nicely. Then with 48 hours to go before the demo we found this one service was not running on the demo server. Of course, "everything was the same on the 2 boxes." So, it couldn't be the boxes. Finally, my boss, Roberto the Wise, was able to conclusively eliminate the boxes. We had other interfaces within the same service that would work, everything else checked out and all the evidence pointed to the code (AKA me, the meek).

Time was running short but we really suspected it was something in the code even though the code in the .svc.cs file was pretty vanilla because it was essentially just a facade in front of the real logic in the system.

Finally, my colleague, Marco the Magnificent, looked at the .svc file. You know, it is one of those files that you never look at because it has "nothing in it." Well, there lay the treasure we were searching for. The file, which had never been altered since it was created and had worked/compiled on numerous other servers, had quite a bit of information in it that didn't seem to correspond to anything we were doing:

This is what it looked like:

ServiceHost Language="C#" Factory="System.Data.Services.DataServiceHostFactory, System.Data.Services, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089" Service="SharedServicesService.DocManInfo.DocManInfoService"

True, our service was in the namespace "SharedServicesService.DocManInfo.DocManInfoService" but what did "System.Data.Services.DataServiceHostFactory, System.Data.Services, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089" have to do with anything?

Once we changed the file to this:

ServiceHost Language="C#" Debug="true" Service="SharedServicesService.DocManInfo.DocManInfoService" CodeBehind="DocManInfoService.svc.cs"

(I was forced to omit the starting and ending characters in these files due to the limitation/restriction within blogger on certain character combinations)

Everything came together perfectly and we had 11 minutes left before the demo!

The server wasn't the only thing sweating during this adventure. I hope you enjoyed it as much as we did. Or at least, I hope this will solve your problem if you ever run into Targort the Terrible in the jungle.

Just a good piece of troubleshooting advice on web services, always make sure the WSDL file can be viewed in the browser. If the WSDL won't work, that narrows things down to a compile time issue.