tag:blogger.com,1999:blog-33403529912279799612024-03-18T02:48:29.460-07:00Marks asp.net / mvc blogWeb developer from Sydney Australia.
Currently using asp.net, mvc where possible.Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.comBlogger22125tag:blogger.com,1999:blog-3340352991227979961.post-17234546554496764202012-07-24T06:08:00.000-07:002012-07-24T06:30:09.204-07:00NuGet Package Restore with a Local Repository and Offline Access<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="background-color: white;">NuGet package restore is a great feature as it means you no longer need to check in packages to source control. The only downside is that you need to have a internet connection to "restore" the packages.</span><br />
<br />
This post is going to help address that issue by showing how you can keep your downloaded NuGet packages in a central location to save space and provide offline access.<br />
<br />
<i>Warning: this works for me but it isn't a supported use case, so YMMV + use at your own risk.</i><br />
<h3 style="text-align: left;">
<span style="background-color: white;">Firstly, enable NuGet package restore on your solution</span></h3>
<span style="background-color: white;">To enable package restore (make sure you have the NuGet.2.0 extension installed) simply <b>right click on your solution</b> and select "<b>Enable NuGet Package Restore</b>"</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-enMc25SZysM/UA6Mn8xDXmI/AAAAAAAAAxw/SIx_MPIsc5g/s1600/EnableNugetPackageRestore.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-enMc25SZysM/UA6Mn8xDXmI/AAAAAAAAAxw/SIx_MPIsc5g/s1600/EnableNugetPackageRestore.png" /></a></div>
<br />
Once this is done any packages missing from your packages folder will be automatically downloaded when you build the solution. Try it out.., delete any folder under the "packages" directory and rebuild the solution. NuGet will automatically download the missing packages.<br />
<h3 style="text-align: left;">
<span style="background-color: white;">Next, </span><span style="background-color: white;">Set up</span><span style="background-color: white;"> a central package repository</span></h3>
Now to minimize the number of items we need to download (and disk space) we are going to configure NuGet to use a single location for the packages. Normally NuGet will store the packages in a "packages" folder at the solution level but this means there is a lot of duplication of files. I have setup a global location for my packages at "C:\ddrive\dev\_nuget_repository\packages" but this can vary between computers as it's based on an environment variable.<br />
<br />
To do this, first create an environment variable called <b>PackagesDir </b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-AUgHJcdPpj0/UA6Qk29Vf2I/AAAAAAAAAyE/DiY6DNYFf9s/s1600/SetupEnvironmentVariable.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-AUgHJcdPpj0/UA6Qk29Vf2I/AAAAAAAAAyE/DiY6DNYFf9s/s1600/SetupEnvironmentVariable.png" /></a></div>
Next modify the "N<b>uGet.targets</b>" (this file is added to your solution in the first step) and add the attribute: <b>Condition=" '$(PackagesDir)' == '' " </b> to the two lines, as shown below (NuGet.targets):<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-eIvDu2C03uQ/UA6SYrj7s7I/AAAAAAAAAyU/wQWVnk5M_CQ/s1600/ModificationsToNuget.targets.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-eIvDu2C03uQ/UA6SYrj7s7I/AAAAAAAAAyU/wQWVnk5M_CQ/s1600/ModificationsToNuget.targets.png" /></a></div>
<span style="background-color: white;">NB: Commit all the files under the .nuget folder to source control.</span><br />
<span style="background-color: white;">You might need to restart visual studio but now you should have your packages stored in your preferred central location.</span><br />
<h3 style="text-align: left;">
Finally, Lets set up our own NuGet feed based on the packages in this central repository.</h3>
Notice how I set a extra "<b>packages</b>" directory to my location. This is because the packages directory will hold all the extracted packages created by NuGet and visual studio. We will use the parent directory as the NuGet Feed directory.<br />
<br />
This relies on a powershell script to copy the <b>.nupkg</b> (zipped packages) which exist in the sub directories up to the parent ( in my case its: C:\ddrive\dev\_nuget_repository\).<br />
<br />
Save the following power shell command to a file called "<a href="https://gist.github.com/3169671">_PackageCopier.ps1</a>" into the <b>_nuget_repository</b> directory :<br />
<pre><blockquote>
""
"Package Copier Starting..........."
""
if($args.length -ne 2)
{
$source = resolve-path "..\..\packages"
$destination = resolve-path "..\..\_nuget_repository\"
}
else
{
$source = $args[0]
$destination = $args[1]
}
if( -not (Test-Path -PathType Container $source))
{
throw "source directory does not exist, source: " + $source
}
if( -not (Test-Path -PathType Container $destination))
{
throw "Destination directory does not exist!! " + $destination
}
Get-ChildItem -Recurse -Filter "*.nupkg" $source `
| Where-Object { -not( Test-Path -PathType Leaf (Join-Path $destination $_.Name)) } `
| Copy-Item -Destination $destination -Verbose `
</blockquote>
</pre>
Now save the following bat file into the same directory (called "<b>_update_from_repository.bat</b>" ):<br />
<pre><blockquote>
powershell.exe .\_PackageCopier.ps1 "packages" "."
pause
</blockquote>
</pre>
If you are errors running powershell see <a href="http://stackoverflow.com/questions/4647429/powershell-on-windows-7-set-executionpolicy-for-regular-users">stackoverflow</a>. Basically I have set my execution policy to unrestricted for both 32bit and 64bit powershell prompts.<br />
<br />
Now when you double click the _update_from_repository.bat your local NuGet repository will be updated with all the NuGets you are using from all your solutions.<br />
<h3 style="text-align: left;">
Setup the local repository in Visual Studio:</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-WoLn095fLmM/UA6W_ajLpPI/AAAAAAAAAyk/TuKKCxFX-y8/s1600/SetupVisualStudioWithLocalRepository.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-WoLn095fLmM/UA6W_ajLpPI/AAAAAAAAAyk/TuKKCxFX-y8/s1600/SetupVisualStudioWithLocalRepository.png" /></a></div>
<br />
That's it. You should now have a central repository for all NuGet packages and the a local repository of all the packages you have used.<br />
<br />
If would be great if the NuGet.targets file could be modified permanently globally to respect an environment variable!<br />
<span style="background-color: white;"><br /></span><br />
<span style="background-color: white;">Below is a screen grab of what my </span><b style="background-color: white;">_nuget_repository</b><span style="background-color: white;"> directory looks like:</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-RnqnWBqvD78/UA6asJ0p5GI/AAAAAAAAAzE/tcbtB8xuySs/s1600/MyNugetDirectory.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-RnqnWBqvD78/UA6asJ0p5GI/AAAAAAAAAzE/tcbtB8xuySs/s1600/MyNugetDirectory.png" /></a></div>
<span style="background-color: white;"><br /></span><br />
<span style="background-color: white;"><br /></span><br />
<br /></div>
<a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com.au%2f2012%2f07%2fnuget-package-restore-with-local.html"><img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%253a%252f%252fmarkkemper1.blogspot.com.au%252f2012%252f07%252fnuget-package-restore-with-local.html" /></a> <span style="background-color: white;"> </span><a href="http://dotnetshoutout.com/NuGet-Package-Restore-with-a-Local-Repository-and-Offline-Access" rev="vote-for" style="background-color: white;"><img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fmarkkemper1.blogspot.com.au%2F2012%2F07%2Fnuget-package-restore-with-local.html" style="border: 0px;" /></a></div><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com3tag:blogger.com,1999:blog-3340352991227979961.post-1341406950395575542011-09-25T07:24:00.000-07:002011-09-25T07:46:16.830-07:00Project to File Reference Switcher for Visual Studio<div dir="ltr" style="text-align: left;" trbidi="on">Switching from project to file references in visual studio has always been a pain. Once you add a project to the solution you have to go find all the locations the assembly is referenced by file path and remove and update to a project reference.<br />
<br />
On top of playing hide and seek with references, the actual act of changing references is a slow and painful experience.<br />
<br />
So I decided to <b>experiment</b> and write my first visual studio extension, "<a href="http://visualstudiogallery.msdn.microsoft.com/056617a4-da39-4d7b-8ecf-933009d9b721">Reference Switcher</a>"<br />
<br />
Once installed it will sit patiently in the background and only activate when you add or remove a project from the solution.<br />
<br />
When you add a project, it gets the assembly name defined by the project and then checks to see if other projects in the solution are referencing that assembly name. If they do, it will open a alert box (sorry, send a push request) and ask if you would like to switch the file reference to a project reference. Just hit OK and it will go ahead and switch all the references for you.<br />
<br />
When you remove a project, it all happens in reverse. So any project references that it changed are restored back to the original file based reference.<br />
<br />
Here are some screen shots of the extension in action:<br />
<br />
1) Adding a existing project (existing projects have a file references to this one)<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-sNcqFdODClM/Tn81MYmF5nI/AAAAAAAAAIk/Sar2HNXQFhM/s1600/AddProject.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-sNcqFdODClM/Tn81MYmF5nI/AAAAAAAAAIk/Sar2HNXQFhM/s1600/AddProject.PNG" /></a></div><br />
2) Shows projects that can be updated to use a project reference<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-NhmBNh1JlGg/Tn81mvDRGRI/AAAAAAAAAIs/50TusKAziQE/s1600/SwitchToProjectReferenceConfirm.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-NhmBNh1JlGg/Tn81mvDRGRI/AAAAAAAAAIs/50TusKAziQE/s1600/SwitchToProjectReferenceConfirm.PNG" /></a></div><br />
3) Now the references have changed to Project References<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-EDdq0ngnqwo/Tn81zsk69nI/AAAAAAAAAI4/DHzEErGEGqY/s1600/ReferenceChangedToProject.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-EDdq0ngnqwo/Tn81zsk69nI/AAAAAAAAAI4/DHzEErGEGqY/s1600/ReferenceChangedToProject.PNG" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><br />
</div>4) When the project is removed, Reference Switcher confirms before changing the references back to there original location<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-5D7uLiBtQSU/Tn82BtfN8GI/AAAAAAAAAI8/shnc1hVl6JI/s1600/ProjectToFileReferenceConfirm.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-5D7uLiBtQSU/Tn82BtfN8GI/AAAAAAAAAI8/shnc1hVl6JI/s1600/ProjectToFileReferenceConfirm.PNG" /></a></div><br />
5)Now the reference has changed back to a file reference<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-4Eu1jwb_hbo/Tn82W5QEBoI/AAAAAAAAAJA/pz9zPReKSVQ/s1600/ReferenceChangeToFIle.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-4Eu1jwb_hbo/Tn82W5QEBoI/AAAAAAAAAJA/pz9zPReKSVQ/s1600/ReferenceChangeToFIle.PNG" /></a></div><br />
That's all there is to it. Small & simply but very handy.<br />
<br />
The extension source can be found on git hub here:<br />
<div style="text-align: left;"></div><ul style="text-align: left;"><li><a href="https://github.com/markkemper1/ReferenceSwitcher">https://github.com/markkemper1/ReferenceSwitcher</a></li>
</ul>Extension can be found in the gallery here:<br />
<div style="text-align: left;"></div><ul style="text-align: left;"><li><a href="http://visualstudiogallery.msdn.microsoft.com/056617a4-da39-4d7b-8ecf-933009d9b721">http://visualstudiogallery.msdn.microsoft.com/056617a4-da39-4d7b-8ecf-933009d9b721</a></li>
</ul><br />
<a rev="vote-for" href="http://dotnetshoutout.com/Project-to-File-Reference-Switcher-for-Visual-Studio"><img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fmarkkemper1.blogspot.com%2F2011%2F09%2Fproject-to-file-reference-switcher-for.html" style="border:0px"/></a><br />
<br />
<a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2011%2f09%2fproject-to-file-reference-switcher-for.html"><img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=url%3Dhttp%253a%252f%252fmarkkemper1.blogspot.com%252f2011%252f09%252fproject-to-file-reference-switcher-for.html" border="0" alt="kick it on DotNetKicks.com" /></a><br />
<br />
</div><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com30tag:blogger.com,1999:blog-3340352991227979961.post-40416562367960747122010-11-19T15:39:00.000-08:002010-11-21T05:19:02.024-08:00Installing Ruby on Rails 3.0.3 on Ubuntu on Virtual Box on Windows 7WARNING: This is how I got things working, it may not be the best way and it may not work for you!<br />
<br />
For a while now I have been wanting to try out ruby and rails (and Python + Django and php). Why? well I want to call myself a web developer and not a asp.net mvc developer. It’s just about keeping up on how the rest of the world is developing web applications. So I am going to make a real effort to learn other frameworks, starting with rails.<br />
<br />
Pretty much every rails guide out there says at some point ‘Don’t use windows’ as your OS. <br />
Having done a tiny bit of Unix programming in the past I already understood that using windows won’t give your the full ruby on rails experience. The command line on Linux/Unix systems is just that much better then what windows provides and ruby on rails makes full use of it. There are other benefits as well such as: ruby running faster, better support from the community (more people using Linux environments), more open source tools, experience using Linux (which comes in handy when you host on Linux) . NB: Mac's use a Linux based OS.<br />
<br />
Getting the environment setup was something of a challenge (to say the least). Given I already have basic Linux skills, the setup could be a complete show stopper for people who have only used windows for development. This post is so I can quickly and easily get it up and running in the future and in the process hopefully help one or two other people along the way.<br />
<h2>1) Getting an Ubuntu ISO</h2>You can download the Ubuntu Desktop Edition ISO from the <a href="http://www.ubuntu.com/desktop/get-ubuntu/download">Ubuntu download page</a>. It’s around the 700Mb mark. If your running a 32bit OS then make sure you download the 32 bit version. If your unsure download the 32bit version. <br />
<br />
Just save it to disk somewhere you can find it, once we have VirtualBox up and running it will mount the ISO image directly as a CD-ROM drive on your virtual machine.<br />
<h2>2) VirtualBox</h2>Although there are many choices out there for running virtual machines I choose Virtual Box based on previous good experiences using the product. It’s also available free of charge for personal use.<br />
<ul><li><a href="http://www.virtualbox.org/wiki/Downloads">Download VirtualBox</a> </li>
</ul>The install is just a click through wizard on windows. <br />
Once VirtualBox is running click the New icon to make a new virtual machine. Make sure you select ‘Linux’ as the operation system and Ubuntu as the version (select Ubuntu 64 bit if downloaded the 64 bit ISO).<br />
<br />
<a href="http://lh6.ggpht.com/_RaIYK6kVO1A/TOcEa8Hj3fI/AAAAAAAAAHs/-bSq-NmvrEQ/s1600-h/new-virutal-machine%5B3%5D.png"><img alt="new-virutal-machine" border="0" height="394" src="http://lh3.ggpht.com/_RaIYK6kVO1A/TOcEcVzaXhI/AAAAAAAAAHw/lRPw48CRnig/new-virutal-machine_thumb%5B1%5D.png?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="new-virutal-machine" width="567" /></a><br />
<br />
For starters allocate around the 1.5 gig mark for memory (you can change this at any point and assuming you have at least 4 gig on the host).<br />
<br />
For the virtual hard disk I just left the settings as default (“Create new hard disk”) and creating a new Dynamically expanding storage drive. But feel free to tinker this as your see fit.<br />
<br />
Once your new machine has been created you can select it and click the settings button. Then select storage and set the Ubuntu ISO as the source for the CDROM drive as show below.<br />
<br />
<a href="http://lh5.ggpht.com/_RaIYK6kVO1A/TOcEddRw7HI/AAAAAAAAAH0/x0nphsu6YXg/s1600-h/set-ubuntu-iso-as-cdrom-drive%5B3%5D.png"><img alt="set-ubuntu-iso-as-cdrom-drive" border="0" height="482" src="http://lh5.ggpht.com/_RaIYK6kVO1A/TOcEeEg5-pI/AAAAAAAAAH4/ZJp5Q22X8BU/set-ubuntu-iso-as-cdrom-drive_thumb%5B1%5D.png?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="set-ubuntu-iso-as-cdrom-drive" width="656" /></a><br />
<h2>3) Ubuntu</h2>Now we have our virtual machine setup and a installation disk mounted we can fire up our new virtual machine by double clicking it inside VirtualBox. After some time you should see the Ubuntu Install screen. <br />
<br />
<a href="http://lh5.ggpht.com/_RaIYK6kVO1A/TOcEeyuSAPI/AAAAAAAAAH8/DWOBVXyCAh0/s1600-h/ubuntu-install-screen%5B3%5D.png"><img alt="ubuntu-install-screen" border="0" height="468" src="http://lh3.ggpht.com/_RaIYK6kVO1A/TOcEgJz_U1I/AAAAAAAAAIA/aTgr5C1hq7Q/ubuntu-install-screen_thumb%5B1%5D.png?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="ubuntu-install-screen" width="604" /></a><br />
<br />
Just make sure you select “Install Ubuntu 10.XX LTS” because we are running at virtual machine and we want it to install onto our virtual hard drive.<br />
<br />
From here on its a really easy (glad to say) wizard similar to the windows installation wizard. Just keep clicking next unless something catches your eye (like incorrect region or such). Make sure you remember the password you create for your account!<br />
<br />
After the installation completes we can prepare Ubuntu for our rails installation. <br />
<br />
Assuming you have remembered your password, you should be able to login. Don’t worry about any aesthetics, small window size or anything else yet just get rails working first.<br />
<br />
If you new to Linux and the terminal you may want to Google-up on that and find some nice introduction articles to get you up to speed. <a href="https://help.ubuntu.com/community/UsingTheTerminal">Here is a terminal introduction from the Ubuntu documentation</a>.<br />
<br />
First open a terminal window (under Applications –> Accessories –> Terminal) and run the following command to update any existing software packages.<br />
<blockquote>sudo apt-get update </blockquote>Explanation:<br />
<ul><li><strong>sudo</strong>: means run the next command as root (or Administrator level)<br />
</li>
<li><strong>apt-get</strong>: is the Ubuntu package manager, it downloads and installs software for you.<br />
</li>
<li><strong>update</strong>: just tells apt-get to update all existing packages. </li>
</ul>Now lets install some more packages, I have just followed the advice from this page: <a href="http://thekindofme.wordpress.com/2010/10/24/rails-3-on-ubuntu-10-10-with-rvm-passenger-and-nginx/">http://thekindofme.wordpress.com/2010/10/24/rails-3-on-ubuntu-10-10-with-rvm-passenger-and-nginx/</a>. To be specific: the command suggested by the commenter at the bottom. This post was a great help in getting my installation working.<br />
<br />
Inside the terminal run the following command:<br />
<blockquote>sudo apt-get install build-essential bison openssl libreadline5 libreadline5-dev curl git-core zlib1g zlib1g-dev libssl-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libmysqlclient-dev </blockquote>Once this is complete we are ready to move onto ruby and rails using RVM.<br />
<h2>4) Ruby on Rails 3.0.3</h2>For our ruby installation we are going to use <a href="http://rvm.beginrescueend.com/">RVM</a> (Ruby Version Manager). RVM is awesome and it can really take a lot of the pain out of getting rails up and running. RVM is also a powerful tool for managing GEMS, it gives you a single command to managing the version of ruby you are using and the GEMs you are using.<br />
<br />
Installing RVM is not straight forward (don’t be fooled by the Quick Install) on the home page. Start by running the command below (as suggested).<br />
<blockquote>bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )</blockquote>Then follow the link to the <a href="http://rvm.beginrescueend.com/rvm/install/">Installing RVM</a> page and follow the Post Install instructions <b>carefully</b>. <br />
<br />
Use the command below to fire up gedit so you can edit your .bashrc file and add the line as instructed:<br />
<blockquote>gedit .bashrc</blockquote>Now add this line to the BOTTOM of the file <br />
<blockquote>[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # This loads RVM into a shell session.</blockquote>Now follow the <b>Troubleshooting your install</b> section and read the notes about the return statement. It basically says replace:<br />
<blockquote>[ -z "$PS1" ] && return</blockquote>with:<br />
<blockquote>if [[ -n "$PS1" ]]; then</blockquote><b>and </b>indent EVERYTHING below until just before the line you have just added. <em>I simply held ctrl-shift and pressed End to select the remaining content of the file, then pressed TAB to indent.</em> Then inserted the closing:<br />
<blockquote><pre><b>fi</b>
[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # This loads RVM into a shell session.</pre></blockquote>Save and close gedit. Close your terminal window and open a new terminal window then type:<br />
<blockquote>rvm</blockquote>Which should result in the usage information for rvm being shown. Now we can proceed to install ruby version 1.9.2.<br />
<blockquote>rvm install 1.9.2</blockquote>Once this has installed successfully we need to set 1.9.2 as our default ruby installation. (NB: to be safe you might want to restart your terminal here)<br />
<blockquote>rvm use 1.9.2 --default</blockquote>We can check this worked by asking ruby it’s version:<br />
<blockquote>ruby -v</blockquote>Assuming this worked and said something about ruby 1.9.2 we can finally install rails.<br />
<blockquote><pre>rvm 1.9.2@global
gem install rails</pre></blockquote>The first command sets the gemset (think of it as like a namespace for <a href="http://rubygems.org/">gems</a>) to the global gemset. We want to install rails into the global gemset, this means all other gemsets (that we may create) will have access to the rails gem (+ dependencies).<br />
<br />
Once rails has installed, lets create a new rails app and to be super unoriginal we call it MyBlog.<br />
<blockquote><pre>mkdir sites
cd sites
rails new MyBlog
cd MyBlog
bundle install</pre><pre></pre></blockquote>So what we did here is: <br />
<ul><li>Create a new directory for called sites <br />
</li>
<li>Changed to the sites directory <br />
</li>
<li>Created a new rails app called MyBlog <br />
</li>
<li>Change to the MyBlog directory (created by rails) <br />
</li>
<li>Called <a href="http://gembundler.com/">Bundler</a> to install any necessary gems for our new project (sqllite was missing)</li>
</ul>Now we can fire up the rails server <br />
<blockquote>rails server </blockquote>If you read the output you will see the port that the web server is running. So its just a matter of opening your browser to that address. <br />
<br />
OK, that's it for now. If you read this far I really hope you managed to get rails up and running on a virtual Ubuntu machine! Proof below….<br />
<br />
<a href="http://lh6.ggpht.com/_RaIYK6kVO1A/TOcEhOm--OI/AAAAAAAAAIE/cIDQVQDzH4M/s1600-h/rails-running%5B3%5D.png"><img alt="rails-running" border="0" height="606" src="http://lh6.ggpht.com/_RaIYK6kVO1A/TOcEiKNWOgI/AAAAAAAAAII/tX8CfYYvRBA/rails-running_thumb%5B1%5D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="rails-running" width="807" /></a><br />
<br />
<h2>5) VirtualBox Additions</h2>If rails is up and running chances are you want to improve your working environment. The first step is to install the VirtualBox Additions, here is <a href="http://helpdeskgeek.com/linux-tips/install-virtualbox-guest-additions-in-ubuntu/">a good article detailing the process.</a><br />
<br />
Otherwise, Ubuntu is fully customisable but don't waste time trying to make it look like a mac :)<div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com4tag:blogger.com,1999:blog-3340352991227979961.post-23600457539783896972010-10-25T08:04:00.000-07:002010-10-25T08:07:54.795-07:00Unit Testing – MsBuild SeriesThis post is going to walk through the steps of adding unit testing to our build script. This post is assuming that you already have a MsBuild script setup that you can run from the command line. Otherwise you might want to look at my previous post on setting up a build file for Visual Studio:<br />
<ul><li><a href="http://markkemper1.blogspot.com/2010/10/create-build-file-for-visual-studio.html">Create a Build File for a Visual Studio Solution - MsBuild Series</a> </li>
</ul><h2><span style="font-weight: normal;">1) Change the output path for Test projects</span></h2>The first thing to do is to change the output path for the test project (or projects). We do this so that the testing binaries to are separated from the release binaries. Below is a screen shot of the directory being changed inside visual studio.<br />
<br />
<a href="http://lh5.ggpht.com/_RaIYK6kVO1A/TMWWw74c6NI/AAAAAAAAAHc/uk3-LI0vPrA/s1600-h/Change%20output%20Directory%20For%20Test%20Projects%5B4%5D.png"><img alt="Change output Directory For Test Projects" border="0" height="690" src="http://lh6.ggpht.com/_RaIYK6kVO1A/TMWWxeYLDJI/AAAAAAAAAHg/OSUFPpcB7zo/Change%20output%20Directory%20For%20Test%20Projects_thumb%5B2%5D.png?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="Change output Directory For Test Projects" width="749" /></a><br />
<br />
Things to note:<br />
<ul><li>We selected the “Automoated_Build” configuration that was created in the first blog post “<a href="http://markkemper1.blogspot.com/2010/10/create-build-file-for-visual-studio.html">Create a Build File for a Visual Studio Solution - MsBuild Series</a>”</li>
<li>Used a <strong>relative path </strong>the the build directory</li>
</ul><h2><span style="font-weight: normal;">2) NUnit Target</span></h2>For running our tests I am going to use the “MsBuld.Community.Tasks” NUnit Task. We have been using other Tasks from the “MsBuld.Community.Tasks” so we already have the targets and dll that are needed for these tasks (<a href="http://markkemper1.blogspot.com/2010/10/mercurial-revision-no-to-version-your.html">see this post for details on getting the Community Tasks setup</a>).<br />
<br />
So, now we just define our NUnit target as follows:<br />
<pre><Target Name="NUnit">
<ItemGroup>
<TestAssembly Include="$(BuildOutputDir)\Test\*.test.dll" />
</ItemGroup>
<Message Text="NUnit is running on: @(TestAssembly)" />
<Nunit ToolPath=".\Tools\NUnit" Assemblies="@(TestAssembly)" />
</Target>
<Target Name="Test" DependsOnTargets="NUnit">
<Message Text="Testing code" />
</Target>
</pre><br />
Things to note:<br />
<ul><li>I have created two targets “Test” & “NUnit” which is not necessary if your only using NUnit. However, I did this so that it's easy to add other targets for different testing frameworks such as MbUnit, xUnit, Mspec etc. (if you using multiple test frameworks make sure you checkout <a href="http://www.gallio.org/">gallio</a>)</li>
<li>The TestAssembly ItemGroup will grab any assembly that ends with Test. </li>
<li>The nunit console runner and lib directory have been copied to "[Project Dir]\Tools\NUnit"</li>
</ul><br />
The necessary nunit binaries have been copied into “[Project Root]\Tools\Nunit”. Normally I just copy “nunit-console.exe” , “nunit-console.exe.config” and the entire Lib directory.<br />
<h2><span class="Apple-style-span" style="font-weight: normal;">3) Incorporate the Test target into the main build file</span></h2>We now have the Test target so we all we need to do is have our default build target depend on our Test target, as follows:<br />
<blockquote><span class="Apple-style-span" style="font-family: monospace; white-space: pre;"><Target Name="Build" DependsOnTargets="Clean; VersionSolutionInfo; Compile; <b>Test</b>; Zip-Source; Zip-Binaries; NuPack"></span><br />
<pre><Message Text="Clean, VersionSolutionInfo, Compile, Test, Zip-Source, Zip-Binaries, NuPack"/>
</Target></pre></blockquote>Now when we run our build the code is tested before creating our zips or NuPack (or should I say Nuget).<br />
<br />
<img alt="Testing Code" border="0" height="577" src="http://lh5.ggpht.com/_RaIYK6kVO1A/TMWWzW8-gyI/AAAAAAAAAHo/Eff4iWgecyg/Testing%20Code_thumb%5B1%5D.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="Testing Code" width="1330" /><br />
<br />
<strong>NB: Please check the reference files for full copies of the build targets used in this series so far</strong><br />
<br />
Resources: <br />
<ul><li><a href="http://pastie.org/1247456" style="color: blue; text-decoration: none;">Main MsBulid File</a> - Main build with targets : clean, compile & build. Imports all other build files.</li>
<li><a href="http://pastie.org/1232489" style="color: blue; text-decoration: none;">Version MsBuild File</a> - Contains targets from a previous post (to auto version AssemblyInfo from mercurial)</li>
<li><a href="http://pastie.org/1235531" style="color: blue; text-decoration: none;">Zip MsBuild File</a> - Contains targets from a previous post (to create zips)</li>
<li><a href="http://pastie.org/1238285" style="color: blue; text-decoration: none;">NuPack MsBuild File</a> - Contains targets from a previous post (to create a nupack package).</li>
<li><a href="http://pastie.org/1247455">Test MsBuild File</a> - Contains the targets from this post (nunit testing)</li>
<li><a href="http://msdn.microsoft.com/en-us/library/5dy88c2e.aspx" style="color: blue; text-decoration: none;">MSBuild Project File Schema Reference </a>- Microsoft documents on MsBuild</li>
</ul><br />
This is the fifth post in the MsBuild series.<br />
<ul><li><a href="http://markkemper1.blogspot.com/2010/10/create-build-file-for-visual-studio.html" style="color: blue; text-decoration: none;">Create a Build File for a Visual Studio Solution - MsBuild Series</a> </li>
<li><a href="http://markkemper1.blogspot.com/2010/10/mercurial-revision-no-to-version-your.html" style="color: blue; text-decoration: none;">Mercurial Revision No to Version your AssemblyInfo - MsBuild Series</a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/zipping-build-outputs-using-build-file.html" style="color: blue; text-decoration: none;">Zipping Build Outputs - MsBuild Series </a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/creating-nupack-package-using-build.html" style="color: blue; text-decoration: none;">NuPack packaging - MsBuild Series</a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/unit-testing-msbuild-series.html">NUnit Testing - MsBuild Series</a></li>
<li>Web Projects and AspNetComplier - MsBuild Series (coming later)</li>
<li>Cruise Control - MsBuild Series (coming later)</li>
</ul><br />
<a href="http://dotnetshoutout.com/Unit-Testing-MsBuild-Series-Marks-aspnet-mvc-blog" rev="vote-for"><img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fmarkkemper1.blogspot.com%2F2010%2F10%2Funit-testing-msbuild-series.html" style="border: 0px;" /></a> <a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f10%2funit-testing-msbuild-series.html"><img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%253a%252f%252fmarkkemper1.blogspot.com%252f2010%252f10%252funit-testing-msbuild-series.html" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com0tag:blogger.com,1999:blog-3340352991227979961.post-8101377667016638382010-10-21T08:18:00.000-07:002010-10-23T06:14:42.779-07:00Creating a NuPack package using a Build File - MsBuild SeriesThis post is going to walk through the steps of creating a NuPack package as part of an automated build file.<br />
Previously we have created an automated build file to clean, version, compile, zip-binaries and zip-source code.<br />
If you’re now familiar with creating build files then you may want to catch up by reading these:<br />
<ul><li><a href="http://markkemper1.blogspot.com/2010/10/create-build-file-for-visual-studio.html" style="color: blue; text-decoration: none;">Create a Build File for a Visual Studio Solution - MsBuild Series</a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/mercurial-revision-no-to-version-your.html" style="color: blue; text-decoration: none;">Mercurial Revision No to Version your AssemblyInfo - MsBuild Series</a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/zipping-build-outputs-using-build-file.html">Zipping Build Outputs using a Build File - MsBuld Series</a></li>
</ul><h3><span class="Apple-style-span" style="font-weight: normal;"><span class="Apple-style-span"><span class="Apple-style-span" style="font-size: x-large;">1) NuPack Package Preparation</span></span></span></h3>Before we start we need to get a few things in place. <br />
<ol><li>We need the NuPack.exe utility</li>
<li>We need a directory to keep our NuPack stuff.</li>
<li>We need a NuPack manifest file</li>
</ol><div>(<b>NB</b>: The best way to learn how to build nuspec packages (at the moment) is to look through the examples by downloading the codeplex repository here: <a href="http://nupackpackages.codeplex.com/SourceControl/list/changesets">http://nupackpackages.codeplex.com/SourceControl/list/changesets</a>)</div><div><br />
</div>Firstly we need to add the NuPack.exe utility to our tools directory. The NuPack.exe will be used to create our NuPack package. You can download this file from: <a href="http://nupack.codeplex.com/releases/view/52016">http://nupack.codeplex.com/releases/view/52016</a><br />
After downloading the following path should be valid:<br />
<ul><li>[project root]\tools\nupack\nupack.exe</li>
</ul>Next, we will create a directory for holding the input files for the NuPack package. Create the following directory: <br />
<ul><li>[project root]\NuPack\</li>
</ul>Inside this directory create a [projectName].nuspec file, for example:<br />
<ul><li>[project root]\NuPack\StickyBeak.nuspec</li>
</ul><blockquote>This file needs to conform to the nuspec file format found here: <a href="http://nupack.codeplex.com/documentation?title=Nuspec%20Format">http://nupack.codeplex.com/documentation?title=Nuspec%20Format</a><br />
Below is a sample for the StickyBeak project:<br />
<blockquote><pre><?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>StickyBeak</id>
<version>1.0.0.0</version>
<authors>
<author>Mark Kemper</author>
<author>Jobping</author>
</authors>
<description>StickyBeak is a logging utility for asp.net websites which can log every request to your site. It provides similar features as IIS log files but provides additional logging information (which just isn’t possible with IIS logs) and easy viewing of logs via a admin page</description>
<language>en-US</language>
</metadata>
</package></pre></blockquote></blockquote>Notice for now that we have hard coded the version number, later as part of the build we will replace this version number with the real version number.<br />
<br />
Next I needed to copy some static files under the NuPack Directory. For StickyBeak I created the follow static content transforms<br />
<ul><li> [Project Root]\NuPack\Content\NLog.config.transform”</li>
<li> [Project Root]\NuPack\Content\Web.Config.transform”</li>
</ul>Any files you place in the <b>Content</b> directory will be copied to the target directory when someone installs your package.<br />
<br />
Once you have your NuPack directory setup just how you want it, its time to build the package using a build target.<br />
<h3><span class="Apple-style-span" style="font-weight: normal;"><span class="Apple-style-span"><span class="Apple-style-span" style="font-size: x-large;">2) NuPack Target</span></span></span></h3>For building the NuPack package I followed these basic steps it the build<br />
<ol><li>Copy everything from our “[Project Root]\NuPack” directory into “[Project Root]\Build\NuPack” . This sets up the static content required for the NuPack package.</li>
<li>Copy the freshly built binaries from our output directory into “[Project Root]\Build\NuPack\Lib” – these files will become references when someone installs our package.</li>
<li>Update the version number inside the Package specification file using a FileUpdate (regex) task</li>
<li>Use a exec task to call the NuPack.exe executable and perform the packaging. NB: I set the working directory so the package will be created in the correct location.</li>
</ol>The NuPack target is quite involved at the moment but hopefully dedicated task is created by Microsoft to help make this a little easier in the future.<br />
<br />
The full target is shown below:<br />
<blockquote><span class="Apple-style-span" style="font-family: monospace; white-space: pre;"><Target Name="NuPack"></span><br />
<pre><PropertyGroup>
<NuPackDestDir>$(BuildOutputDir)\NuPack</NuPackDestDir></pre><pre> <NuPackDestSource>NuPack</NuPackDestSource></pre><pre></PropertyGroup>
<ItemGroup>
<NuPackFiles Include="$(NuPackDestSource)\**" /></pre><pre> <NuPackLibFiles Include="$(BuildOutputDir)\Bin\**" /></pre><pre> <NuPackPackageFile Include="StickyBeak*nupkg" /></pre><pre></ItemGroup>
<Message Text="Setting up the $(NuPackDestDir) directory will all the necessary files to create our package"/>
<Copy SourceFiles="@(NuPackFiles)" DestinationFiles="@(NuPackFiles->'$(NuPackDestDir)\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(NuPackLibFiles)" DestinationFiles="@(NuPackLibFiles->'$(NuPackDestDir)\Lib\%(RecursiveDir)%(Filename)%(Extension)')" />
<FileUpdate Files="$(NuPackDestDir)\StickyBeak.nuspec"
Regex="version>([^<]*)</version"
ReplacementText="version>$(Major).$(Minor).$(Build).$(Revision)</version" />
<Message Text="Executing the NuPack.exe packager"/>
<Exec WorkingDirectory="$(BuildOutputDir)" Command="..\Tools\NuPack\NuPack.exe ..\$(NuPackDestDir)\StickyBeak.nuspec"/>
</Target></pre></blockquote><h3><span class="Apple-style-span" style="font-weight: normal;"><span class="Apple-style-span" style="font-size: x-large;">3) Update Our Default Build</span></span></h3>Now we can update default build task to create our NuSpec package after each successful build.<br />
<blockquote><span class="Apple-style-span" style="font-family: monospace; white-space: pre;"><Target Name="Build" DependsOnTargets="Clean; VersionSolutionInfo; Compile; Zip-Source; Zip-Binaries; NuPack"></span><br />
<pre><Message Text="Clean, VersionSolutionInfo, Compile, Zip-Source, Zip-Binaries, </pre><span class="Apple-style-span" style="font-family: monospace; white-space: pre;">NuPack</span><br />
<pre>"/>
</Target></pre></blockquote>That's it. We can now prepare our NuSpec package a the click of a button. The results of our build directory now look like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_RaIYK6kVO1A/TMBZqbWDdVI/AAAAAAAAAHY/DqjzLVuy2I8/s1600/Build+Directory+NuPack.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/_RaIYK6kVO1A/TMBZqbWDdVI/AAAAAAAAAHY/DqjzLVuy2I8/s1600/Build+Directory+NuPack.PNG" /></a></div><br />
<br />
<span class="Apple-style-span" style="font-family: arial, 'Trebuchet MS', verdana, sans-serif; font-size: 14px; line-height: 22px;">Resources:</span><span class="Apple-style-span" style="font-family: arial, 'Trebuchet MS', verdana, sans-serif; font-size: 14px; line-height: 22px;"><br />
</span><br />
<ul><li style="font-family: arial, 'Trebuchet MS', verdana, sans-serif; font-size: 14px; line-height: 22px;"><a href="http://pastie.org/1238282" style="color: blue; text-decoration: none;">Main MsBulid File</a> - Main build with targets : clean, compile & build. Imports all other build files.</li>
<li style="font-family: arial, 'Trebuchet MS', verdana, sans-serif; font-size: 14px; line-height: 22px;"><a href="http://pastie.org/1232489" style="color: blue; text-decoration: none;">Version MsBuild File</a> - Contains targets from a previous post (to auto version AssemblyInfo from mercurial)</li>
<li style="font-family: arial, 'Trebuchet MS', verdana, sans-serif; font-size: 14px; line-height: 22px;"><a href="http://pastie.org/1235531" style="color: blue; text-decoration: none;">Zip MsBuild File</a> - Contains targets from a previous post (to create zips)</li>
<li><span class="Apple-style-span" style="font-family: arial, 'Trebuchet MS', verdana, sans-serif; font-size: medium;"><span class="Apple-style-span" style="font-size: 14px; line-height: 22px;"><a href="http://pastie.org/1238285">NuPack MsBuild File</a> - Contains targets from this post (to create a nupack package).</span></span></li>
<li style="font-family: arial, 'Trebuchet MS', verdana, sans-serif; font-size: 14px; line-height: 22px;"><a href="http://msdn.microsoft.com/en-us/library/5dy88c2e.aspx" style="color: blue; text-decoration: none;">MSBuild Project File Schema Reference </a>- Microsoft documents on MsBuild</li>
</ul><span class="Apple-style-span" style="font-family: arial, 'Trebuchet MS', verdana, sans-serif; font-size: 14px; line-height: 22px;"><br />
</span><span class="Apple-style-span" style="font-family: arial, 'Trebuchet MS', verdana, sans-serif; font-size: 14px; line-height: 22px;">This is the forth post in the MsBuild series.</span><br />
<ul style="font-family: arial, 'Trebuchet MS', verdana, sans-serif; font-size: 14px; line-height: 22px;"><li><a href="http://markkemper1.blogspot.com/2010/10/create-build-file-for-visual-studio.html" style="color: blue; text-decoration: none;">Create a Build File for a Visual Studio Solution - MsBuild Series</a> </li>
<li><a href="http://markkemper1.blogspot.com/2010/10/mercurial-revision-no-to-version-your.html" style="color: blue; text-decoration: none;">Mercurial Revision No to Version your AssemblyInfo - MsBuild Series</a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/zipping-build-outputs-using-build-file.html" style="color: blue; text-decoration: none;">Zipping Build Outputs - MsBuild Series </a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/creating-nupack-package-using-build.html" style="color: blue; text-decoration: none;">NuPack packaging - MsBuild Series</a></li>
<li>NUnit Testing - MsBuild Series (coming later)</li>
<li>Web Projects and AspNetComplier - MsBuild Series (coming later)</li>
<li>Cruise Control - MsBuild Series (coming later)</li>
</ul><br />
<a href="http://dotnetshoutout.com/Creating-a-NuPack-package-using-a-Build-File-Automating-your-build-Part-4-Marks-aspnet-mvc-blog" rev="vote-for"><img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fmarkkemper1.blogspot.com%2F2010%2F10%2Fcreating-nupack-package-using-build.html" style="border: 0px;" /></a> <a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f10%2fcreating-nupack-package-using-build.html"><img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url%3Dhttp%253a%252f%252fmarkkemper1.blogspot.com%252f2010%252f10%252fcreating-nupack-package-using-build.html" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com4tag:blogger.com,1999:blog-3340352991227979961.post-57911876720924469042010-10-20T07:38:00.000-07:002010-10-23T06:13:52.878-07:00Zipping Build Outputs using a Build File - MsBuld SeriesWe are continuing ths msbuild series, this post will focus on zipping the outputs of the build. To recap so far we have setup a batch file that can build and version our project. The output of this build is placed inside a folder called "build" at the top of projects directory structure.<br />
<br />
We will zip up the outputs of the build so they can be easily distributed. To do this, the Zip build task that is part of the “MsBuld.Community.Tasks” library will be used. If you are not familiar with this library and how to setup then please read through my two previous posts to get up to speed.<br />
<ul><li><a href="http://markkemper1.blogspot.com/2010/10/create-build-file-for-visual-studio.html">Create a Build File for a Visual Studio Solution - MsBuild Series</a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/mercurial-revision-no-to-version-your.html">Mercurial Revision No to Version your AssemblyInfo - MsBuild Series</a></li>
</ul>We are going to create two zip files during our build:<br />
<ol><li>A zip of all <b>binaries</b> needed to run the application</li>
<li>A zip of all <b>source code</b> needed to compile the application.</li>
</ol>We are zipping up the source so we can provide an easy download of the source code for our application via codeplex, github etc. Similarly we can provide just a zip of the binaries for a specific version of our application.<br />
<h3><span class="Apple-style-span" style="font-weight: normal;"><span class="Apple-style-span" style="font-size: x-large;">1) Change the output directory to “build\bin”</span></span></h3>Firstly we are going to change our build output directory inside visual studio so that the binaries are placed into a new folder called bin under the build directory “[project]\Build\Bin”. Having a sub directory removes the clutter from the root of our build directory so we can place our zip files there.<br />
<br />
Below is the screen shot of the new location being set inside visual studio for our automated_build configuration.<br />
<br />
<a href="http://lh4.ggpht.com/_RaIYK6kVO1A/TLsNUMEkf_I/AAAAAAAAAHA/i-JFzax-cZQ/s1600-h/Changing%20the%20output%20directory%5B3%5D.png"><img alt="Changing the output directory" border="0" height="701" src="http://lh6.ggpht.com/_RaIYK6kVO1A/TLsNVUH9_NI/AAAAAAAAAHE/oXWRghqIKVk/Changing%20the%20output%20directory_thumb%5B1%5D.png?imgmax=800" style="border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline;" title="Changing the output directory" width="795" /></a> <br />
<h3>2) Creating the Zip-Binaries target</h3>We have already included the “MSBuild.Community.Tasks” targets in our build file, so only thing we need to do before using the zip task is to ensure that we have copied the ICSharpCode.SharpZipLib.dll binary into our “Tools\MsBuildCommunityTasks” directory. This binary is included in the MsBuildCommunityTasks download.<br />
<br />
Now declare the Zip-Binaries target as follows<br />
<blockquote><pre><Target Name="Zip-Binaries">
<ItemGroup>
<BinDirectoryFiles Include="$(BuildOutputDir)\bin\**" />
</ItemGroup>
<Zip Files="@(BinDirectoryFiles)" WorkingDirectory="$(BuildOutputDir)\bin\"
ZipFileName=".\$(BuildOutputDir)\Jobping.StickyBeak_Binaries_$(Major).$(Minor).$(Build).$(Revision).zip" />
</Target></pre></blockquote>Notes on the Zip-Binaries target<br />
<ul><li>BinDirectoryFiles – this variable references all files below the "build\bin” directory. These are the files that we will zip.</li>
<li>The WorkingDirectory attribute for the zip task specifies the root directory for the zip file. We speicify the “build\bin” directory as the root for our zip</li>
<li>We use the<b> version information</b> to <b>name </b>the resultant zip file.</li>
</ul><h3><span class="Apple-style-span" style="font-weight: normal;"><span class="Apple-style-span" style="font-size: x-large;">2) Creating the Zip-Source target</span></span></h3>Apart from the binaries we also want to distribute the source code. So, we are going to declare a Zip-Source target for our build file as follows:<br />
<blockquote><span class="Apple-style-span" style="font-family: monospace; white-space: pre;"><Target Name="Zip-Source"></span><br />
<pre><ItemGroup>
<SourceFilesExclude Include="**\.hg\**" />
<SourceFilesExclude Include="build**" />
<SourceFilesExclude Include="**\.*" />
<SourceFilesExclude Include="**\bin\**" />
<SourceFilesExclude Include="**\obj\**" />
<SourceFilesExclude Include="**\Test\**" />
<SourceFilesExclude Include="**\TestResults\**" />
<SourceFilesExclude Include="**\*.user" />
<SourceFilesExclude Include="**\*.suo" />
<SourceFilesExclude Include="**\*.cache" />
<SourceFilesExclude Include="**\*.vsmdi" />
<SourceFilesExclude Include="**\*.testsettings" />
</ItemGroup>
<ItemGroup>
<SourceFiles Include="**\*.*" Exclude="@(SourceFilesExclude)" />
</ItemGroup>
<Zip Files="@(SourceFiles)"
ZipFileName=".\$(BuildOutputDir)\Jobping.StickyBeak_Source_$(Major).$(Minor).$(Build).$(Revision).zip" />
</Target></pre></blockquote>Notes on the Zip-Source target<br />
<ul><li>We generally want to zip up everything, so we include all files by using the wildcard “**\*.*” which means all files in all directories. </li>
<li>We specify a list of excludsions, these are source control files and other files that are generated or not necessary in order to build our application.</li>
<li>Getting the exclude list correct requires some testing, you should test your source zip by extracting it and making sure you can compile the solution.</li>
</ul><h3>3) Incorporating the Zip tasks into our default build</h3>Since we can now created the two zip targets all we need to do is have our default build target depend on these targets, as follows:<br />
<blockquote><span class="Apple-style-span" style="font-family: monospace; white-space: pre;"><Target Name="Build" DependsOnTargets="Clean; VersionSolutionInfo; Compile; Zip-Source; Zip-Binaries"></span><br />
<pre><Message Text="Clean, VersionSolutionInfo, Compile, Zip-Source, Zip-Binaries"/>
</Target></pre></blockquote>That’s it. Below is a screen shot of the kicking off a build and the resultant output in the build directory.<br />
<br />
<a href="http://lh4.ggpht.com/_RaIYK6kVO1A/TLsNW04h6rI/AAAAAAAAAHI/jUR67bNducA/s1600-h/Build%20output%5B3%5D.png"><img alt="Build output" border="0" height="484" src="http://lh5.ggpht.com/_RaIYK6kVO1A/TLsNYJNh_8I/AAAAAAAAAHM/lPsHJJs8TfU/Build%20output_thumb%5B1%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline;" title="Build output" width="1135" /></a> <br />
<br />
Screen shot of our ready to ship zips:<br />
<br />
<a href="http://lh5.ggpht.com/_RaIYK6kVO1A/TLsNZLVRNyI/AAAAAAAAAHQ/ezFN3ksISvU/s1600-h/Build%20output%20directory%5B3%5D.png"><img alt="Build output directory" border="0" height="120" src="http://lh4.ggpht.com/_RaIYK6kVO1A/TLsNaAHYMnI/AAAAAAAAAHU/ziPI022acJg/Build%20output%20directory_thumb%5B1%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline;" title="Build output directory" width="634" /></a><br />
<br />
Resources:<br />
<ul><li><a href="http://pastie.org/1235528">Main MsBulid File</a> - Main build with targets : clean, compile & build. Imports all other build files.</li>
<li><a href="http://pastie.org/1232489">Version MsBuild File</a> - Contains targets from a previous post (to auto version AssemblyInfo from mercurial)</li>
<li><a href="http://pastie.org/1235531">Zip MsBuild File</a> - Contains targets from this post (to create zips)</li>
<li><a href="http://msdn.microsoft.com/en-us/library/5dy88c2e.aspx">MSBuild Project File Schema Reference </a> - Microsoft documents on MsBuild</li>
</ul>This is the third post in the MsBuild series.<br />
<ul><li><a href="http://markkemper1.blogspot.com/2010/10/create-build-file-for-visual-studio.html" style="color: blue; text-decoration: none;">Create a Build File for a Visual Studio Solution - MsBuild Series</a> </li>
<li><a href="http://markkemper1.blogspot.com/2010/10/mercurial-revision-no-to-version-your.html" style="color: blue; text-decoration: none;">Mercurial Revision No to Version your AssemblyInfo - MsBuild Series</a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/zipping-build-outputs-using-build-file.html" style="color: blue; text-decoration: none;">Zipping Build Outputs - MsBuild Series </a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/creating-nupack-package-using-build.html" style="color: blue; text-decoration: none;">NuPack packaging - MsBuild Series</a></li>
<li>NUnit Testing - MsBuild Series (coming later)</li>
<li>Web Projects and AspNetComplier - MsBuild Series (coming later)</li>
<li>Cruise Control - MsBuild Series (coming later)</li>
</ul><br />
<a href="http://dotnetshoutout.com/Marks-aspnet-mvc-blog-Zipping-Build-Outputs-using-a-Build-File-Automating-your-build-Part-3" rev="vote-for"><img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fmarkkemper1.blogspot.com%2F2010%2F10%2Fzipping-build-outputs-using-build-file.html" style="border: 0px;" /></a> <a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f10%2fzipping-build-outputs-using-build-file.html"><img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url%3Dhttp%253a%252f%252fmarkkemper1.blogspot.com%252f2010%252f10%252fzipping-build-outputs-using-build-file.html" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com1tag:blogger.com,1999:blog-3340352991227979961.post-6138580943129007092010-10-19T06:29:00.000-07:002010-10-23T06:13:22.456-07:00Mercurial Revision No to Version your AssemblyInfo - MsBuild Series<b>By now you already have a build file setup and running, if not please see <a href="http://markkemper1.blogspot.com/2010/10/create-build-file-for-visual-studio.html">my previous post</a>.</b><br />
<br />
This post is going to focus on getting our version number automatically updated during the build process. We are going to use the AssemblyInfo task from the “<strong><a href="http://msbuildtasks.tigris.org/">MSBuild.Community.Tasks</a></strong>” library and the HgVersion task from the “<strong><a href="http://msbuildhg.codeplex.com/">MSBuild.Mercurial</a>”</strong> library.<br />
<br />
The <strong>HgVersion</strong> task will get the revision number from our Mercurial source control repository so we can include it in our assemblyInfo file.<br />
<br />
The <strong>AssemblyInfo</strong> task will create our AssemblyInfo file before the compile task is executed.<br />
<br />
If you are using TFS or Subversion you will need to substitute the Mercurial task for the equivalent task for these version control systems. The MsBuild.Community.Tasks contains a TFS and Subversion Version tasks for this purpose.<br />
<h3>1) Getting the revision number from Mercurial.</h3>Firstly download and copy the MSBuild.Mercurial from <a href="http://msbuildhg.codeplex.com/releases/view/47779">http://msbuildhg.codeplex.com/releases/view/47779</a> and <b>copy </b>the following <b>two files</b> below to “[Project Root]\Tools\MSBuild.Mercurial” diretory:<br />
<ul><li>MSBuild.Mercurial.dll</li>
<li> MSBuild.Mercurial.tasks </li>
</ul>With these files in place we can now use the HgVersion Task to retrieve the version number from Hg and store it into a property. <br />
<blockquote><pre><PropertyGroup>
<MSBuildMercurialPath>.</MSBuildMercurialPath>
</PropertyGroup>
<Import Project="Tools\MSBuild.Mercurial\MSBuild.Mercurial.Tasks" />
<Target Name="Hg-Revision">
<HgVersion LocalPath="$(MSBuildProjectDirectory)" Timeout="5000">
<Output TaskParameter="Revision" PropertyName="Revision" />
</HgVersion>
<Message Text="Last revision from HG: $(Revision)"/>
</Target>
</pre></blockquote>Firstly we set “MsBuildMercurialPath” to the current directory. Then we import the Mercurial Tasks from our Tools directory. <br />
<br />
Next we define a new target called “Hg-Revision” which calls the HgVersion task. The HgVersion task will set the revision number from the repository in the "LocalPath" to a property called “Revision”. <br />
<br />
Lets test it out by running: build /t<b>:</b>hg-revision <br />
<br />
<a href="http://lh6.ggpht.com/_RaIYK6kVO1A/TLsBB7UzrKI/AAAAAAAAAGg/ECprRQKheYE/s1600-h/Output%20from%20hg-revision%20task%5B3%5D.png"><img alt="Output from hg-revision task" border="0" src="http://lh5.ggpht.com/_RaIYK6kVO1A/TLsBCwqyH1I/AAAAAAAAAGk/3YZx8PrM_z8/Output%20from%20hg-revision%20task_thumb%5B1%5D.png?imgmax=800" style="border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline;" title="Output from hg-revision task" /></a> <br />
<h3>2) Creating our Assembly Info File </h3>Now we are going to create a task to automatically generate an assembly info file. <br />
<br />
We need the <strong>MSBuild.Community.Tasks </strong>library from here: <a href="http://msbuildtasks.tigris.org/">http://msbuildtasks.tigris.org/</a> <br />
<br />
Again I will copy the necessary files under the tools folder. <br />
<ul><li> Tools\MSBuildCommunityTasks\MSBuild.Community.Tasks </li>
<li> Tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll </li>
</ul>(NB: Copying these assemblies under tools folder makes the solution much easier to setup on other developer machines and build servers.) <br />
<blockquote><pre><!-- Create an Assembly Info File -->
<PropertyGroup>
<Major>1</Major>
<Minor>0</Minor>
<Build>0</Build>
<Revision>0</Revision>
<MSBuildCommunityTasksPath>.</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project=".\Tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Target Name="SolutionInfo">
<Message Text="Creating Version File: $(Major).$(Minor).$(Build).$(Revision)"/>
<AssemblyInfo
CodeLanguage="CS"
OutputFile=".\Source\SolutionInfo.cs"
AssemblyTitle="Jobping.StickyBseak"
AssemblyDescription="StickyBeak logs requests to your website"
AssemblyCompany="http://www.jobping.com/"
AssemblyProduct="Jobping.StickyBeak"
AssemblyCopyright="Copyright © whohive"
ComVisible="false"
CLSCompliant="true"
Guid="1d4d1a0d-52f3-49d4-b2f8-3ca642f05cfe"
AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"
AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)"
/>
</Target> </pre></blockquote>Once this is in place we can now generate our AssemblyInfo.cs file. I have called the output filename <b>SolutionInfo.cs</b> as I like to use the same file for each project in the solution (I’ll show you how to do this soon). <br />
<br />
Let's test this by running: build /t<b>:</b>solutionInfo <br />
<br />
<a href="http://lh5.ggpht.com/_RaIYK6kVO1A/TLsBEJbF7WI/AAAAAAAAAGo/z81bCfqvUQU/s1600-h/Output%20from%20SolutionInfo%5B3%5D.png"> <img alt="Output from SolutionInfo" border="0" src="http://lh3.ggpht.com/_RaIYK6kVO1A/TLsBFpj5L9I/AAAAAAAAAGs/SjwHZcTWJsU/Output%20from%20SolutionInfo_thumb%5B1%5D.png?imgmax=800" style="border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline;" title="Output from SolutionInfo" /> </a> <br />
<br />
As you can see we are using the hard-coded version number of 1.0.0.0 for the moment. <br />
<br />
Now lets hook our SolutionInfo.cs file into our visual studio project and have it included in the build. <br />
<ol><li> Firstly go through your project and delete any <b>existing</b> AssemblyInfo files you may have under the “Properties” folder. </li>
<li> For each project in your solution right click and select “Add Existing Item” from the context menu. </li>
<li> Navigate to the SolutionInfo.cs file generated and<b> add as a link</b>. (See below on how to <b>add as a link</b>) </li>
</ol><a href="http://lh5.ggpht.com/_RaIYK6kVO1A/TLsBG6CSYMI/AAAAAAAAAGw/EKkktM0n9Lc/s1600-h/Add%20SolutionInfo%20as%20a%20link%5B3%5D.png"> <img alt="Add SolutionInfo as a link" border="0" src="http://lh6.ggpht.com/_RaIYK6kVO1A/TLsBH0_PIFI/AAAAAAAAAG0/a7sikNCtxT4/Add%20SolutionInfo%20as%20a%20link_thumb%5B1%5D.png?imgmax=800" style="border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline;" title="Add SolutionInfo as a link" /> </a> <br />
<br />
<h3>3) Combining the Hg-Revision and SolutionInfo Tasks</h3>So far our SolutionInfo task only uses a hard-coded revision number of "<b>0</b>". What we really want is to have this revision number linked to the source control revision number. This is really easy to achieve as follows. <br />
<blockquote><pre><Target Name="VersionSolutionInfo" DependsOnTargets="Hg-Revision;SolutionInfo">
<Message Text="Get Revision, Generate SolutionInfo"/>
</Target>
</pre></blockquote>So we have just combined the two items into a new task called <b>VersionSolutionInfo</b>. This task depends on the Hg-Revision and SolutionInfo tasks, so it will first call Hg-Revision to get the lastest revision number into our Revision property then when SolutionInfo is called it will use this revision number. <br />
<br />
Lets execute it as follows: build /t<b>:</b>VersionSolutionInfo <br />
<br />
<a href="http://lh4.ggpht.com/_RaIYK6kVO1A/TLsBI8f1BWI/AAAAAAAAAG4/InPBjlmcBps/s1600-h/Output%20from%20VersionSolutionInfo%5B3%5D.png"> <img alt="Output from VersionSolutionInfo" border="0" src="http://lh3.ggpht.com/_RaIYK6kVO1A/TLsBJouVrFI/AAAAAAAAAG8/98cmi7QGHCE/Output%20from%20VersionSolutionInfo_thumb%5B1%5D.png?imgmax=800" style="border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline;" title="Output from VersionSolutionInfo" /> </a> <br />
<br />
As you can see, the revision number <b>10</b> is retrieved from mercurial and then the solutionInfo is generated. If you inspect the SolutionInfo.cs file you will find that the version number is now 1.0.0.10 <br />
<br />
The final step is to change our default build task to depend on VersionSolutionInfo so we can have everything done in 1 step. <br />
<blockquote><pre><Target Name="Build" DependsOnTargets="Clean;VersionSolutionInfo,Compile">
<Message Text="Clean, VersionSolutionInfo, Compile"/>
</Target>
</pre></blockquote>NB: you can have any number of files containing the assembly meta data such as company, version etc. For example we could split it as follows: <br />
<ul><li> SolutionInfo.cs – contains static information and is checked into source control </li>
<li> SolutionInfo.geneated.cs – just contains version information (not checked in) </li>
</ul>You can organise it anyway you like. <br />
<br />
<b>Update: You need to TAG your repository with the full version number so you can relate the release's version no to a changeset across all repositories. I'll post a build target to do the tag automatically at some point. Thanks to @jdhard for bringing this up.</b><br />
<br />
Below I have provided two sample MsBuild files. Firstly we have our main build file that handles : clean & compile from the first post it this series. The second file contains the targets used in this post. I have split out the items so it's easy to isolate the Tasks for this post.<br />
<br />
Resources:<br />
<ul style="line-height: 22px;"><li><a href="http://pastie.org/1232479" style="color: blue; text-decoration: none;">Main MsBulid File</a> - Main build with targets : clean, compile & build. Imports all other build files. </li>
<li><a href="http://pastie.org/1232489">Version MsBuild File</a> - Contains targets from this post (to auto version AssemblyInfo from mercurial) </li>
<li><a href="http://msdn.microsoft.com/en-us/library/5dy88c2e.aspx" style="color: blue; text-decoration: none;">MSBuild Project File Schema Reference</a> - Microsoft documents on MsBuild </li>
</ul>This is the second post in the MsBuild series.<br />
<ul><li><a href="http://markkemper1.blogspot.com/2010/10/create-build-file-for-visual-studio.html" style="color: blue; text-decoration: none;">Create a Build File for a Visual Studio Solution - MsBuild Series</a> </li>
<li><a href="http://markkemper1.blogspot.com/2010/10/mercurial-revision-no-to-version-your.html" style="color: blue; text-decoration: none;">Mercurial Revision No to Version your AssemblyInfo - MsBuild Series</a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/zipping-build-outputs-using-build-file.html" style="color: blue; text-decoration: none;">Zipping Build Outputs - MsBuild Series </a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/creating-nupack-package-using-build.html" style="color: blue; text-decoration: none;">NuPack packaging - MsBuild Series</a></li>
<li style="line-height: 22px;">NUnit Testing - MsBuild Series (coming later) </li>
<li style="line-height: 22px;">Web Projects and AspNetComplier - MsBuild Series (coming later) </li>
<li style="line-height: 22px;">Cruise Control - MsBuild Series (coming later) </li>
</ul><br />
<a href="http://dotnetshoutout.com/Marks-aspnet-mvc-blog-Mercurial-Revision-No-to-Version-your-AssemblyInfo-MsBuild-Series" rev="vote-for"><img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fmarkkemper1.blogspot.com%2F2010%2F10%2Fmercurial-revision-no-to-version-your.html" style="border: 0px;" /></a> <a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f10%2fmercurial-revision-no-to-version-your.html"><img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%253a%252f%252fmarkkemper1.blogspot.com%252f2010%252f10%252fmercurial-revision-no-to-version-your.html" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com8tag:blogger.com,1999:blog-3340352991227979961.post-16532877214081085952010-10-17T13:55:00.000-07:002010-10-23T06:12:14.298-07:00Create a Build File for a Visual Studio Solution - MsBuild Series<div><div style="font-size: 14px; line-height: 1.8;"><span class="Apple-style-span" style="font-size: x-large;">Why create a build file for a Visual Studio </span></div><div style="font-size: 14px; line-height: 1.8;"><span class="Apple-style-span" style="font-size: x-large;">Solution?</span></div></div><div><span class="Apple-style-span" style="font-size: medium;"><span class="Apple-style-span" style="font-size: 14px; line-height: 1.8;">A build file automates the process of building, testing, analyzing, packaging, & deploying your project. Build files can be used to give you a</span></span><b style="font-size: 14px; line-height: 1.8;"> single click solution</b><span class="Apple-style-span" style="font-size: medium;"><span class="Apple-style-span" style="font-size: 14px; line-height: 1.8;"> to perform </span><span class="Apple-style-span" style="font-size: 14px; line-height: 25px;">mundane</span><span class="Apple-style-span" style="font-size: 14px; line-height: 1.8;"> tasks in a consistent way.</span></span><br />
<div style="font-size: 14px; line-height: 1.8;">It saves you time by <b>automating </b>all the <b>tedious </b>steps necessary to prepare your project for testing or deploying. It reduces risk because you can <b>confidently</b> <b>repeat </b>the process, there's no change you will forgot to rename a file or change to release mode etc etc.</div><div style="font-size: 14px; line-height: 1.8;"><br />
</div></div><div style="font-size: 14px; line-height: 1.8;"><span class="Apple-style-span" style="font-size: x-large;">OK but How?</span></div><div style="line-height: 1.8;">Creating a build file for the first time can be a little tricky so I have prepared a quick tutorial on creating a simple msbuild file for compiling your solution. Just follow the steps below and let me know if you have any issues.</div><h3 style="font-size: 14px; line-height: 1.8;"><span class="Apple-style-span" style="font-size: x-large;"><span class="Apple-style-span" style="font-weight: normal;">1) Folder Structure</span></span></h3><div style="line-height: 1.8;">Once we start running builds of our project we need a place to store the results. We may also need extra tools for our build process and of course we need a place for our new build file.<br />
<br />
</div><div style="line-height: 1.8;">Below is a the folder structure I will be using for this introduction. It allows us to keep all of the build files / folders out of our source tree.</div><div style="line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">Project Root:/</div><blockquote style="line-height: 1.8;">/<strong>Build</strong> – result of the build will be placed in here<br />
/<strong>Source</strong> – all source code (& libraries) for the project<br />
/<strong>Tools</strong> – collection of tools used for the build process<br />
/build.bat – simple 'double click to build' ms dos batch file<br />
/build.proj– our build file for msbuild, this is where we define the steps for our build process</blockquote><h3 style="font-size: 14px; line-height: 1.8;"><span class="Apple-style-span" style="font-size: x-large;"><span class="Apple-style-span" style="font-weight: normal;">2) Creating the Batch File</span></span></h3><div style="line-height: 1.8;">Create a new blank file called build.bat in the root directory of your project. This will be a simple ms dos batch file to kick off our build script. Simple copy the contents below into the batch file.</div><blockquote style="line-height: 1.8;">REM dont remove this line<br />
"%windir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" /nologo build.proj %* </blockquote><div style="line-height: 1.8;">Make sure you leave the first line intact, otherwise you may run into problems because of the encoding used to save the file. If you have issues with encoding then you may need to open the file using a specific encoding (850) see: <a href="http://msdn.microsoft.com/en-us/library/dxfdkfke(VS.80).aspx">http://msdn.microsoft.com/en-us/library/dxfdkfke(VS.80).aspx</a>.</div><div style="line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">The batch file simply calls msbuild.exe and passes in our build.proj file. The <b>%* </b>argument passes any command line arguments supplied to the batch file into the msbuild.exe command. This allows us to specify a target at the command prompt like this: <strong>build /t:clean</strong></div><h3 style="font-size: 14px; line-height: 1.8;"><span class="Apple-style-span" style="font-weight: normal;"><span class="Apple-style-span" style="font-size: x-large;">4) Creating the Build File</span></span></h3><div style="line-height: 1.8;">Lets start with the simplest sample possible.</div><blockquote><pre><?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildOutputDir>build</BuildOutputDir>
</PropertyGroup>
<Target Name="Clean">
<RemoveDir Directories="$(BuildOutputDir)" />
</Target>
<Target Name="Build" DependsOnTargets="Clean">
<Message Text="Clean"/>
</Target>
</Project>
</pre></blockquote><div style="line-height: 1.8;">The build file contains two targets: <b>Clean</b> & <b>Build</b>. Build is our default target so if you run build.bat without any target specified and the <b>build target </b>will be executed by default.</div><div style="line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">The default target is specified on the 2nd line with <strong>DefaultTargets="Build"</strong></div><div style="line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">The Build target <b>depends</b> on the Clean target, so before the build target is executed the Clean target is executed. The Clean target simply deletes the output directory, giving us a clean working space to preform the build.</div><div style="line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">The output directory is defined in the <b>ProperyGroup</b> near the top of the file. This simply assigns the variable: <strong>BuildOutputDir</strong> to the string “build”, which is the name of our output or build directory.</div><h5 style="font-size: 14px; line-height: 1.8;"><span class="Apple-style-span" style="font-size: x-large;"><span class="Apple-style-span" style="font-weight: normal;">5) Adding the Compile Target</span></span></h5><div style="line-height: 1.8;">To actually compile the project we need to add another target, which we are going to call Compile.</div><div style="line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">The updated proj file now looks like this:<br />
<blockquote><pre><PropertyGroup>
<BuildOutputDir>build</BuildOutputDir>
<SolutionToCompile>Source\Jobping.StickBeak.sln</SolutionToCompile>
</PropertyGroup>
<Target Name="Clean">
<RemoveDir Directories="$(BuildOutputDir)" />
</Target>
<Target Name="Compile">
<MakeDir Directories="$(OutputDirectory)" />
<MSBuild Projects="$(SolutionToCompile)"
Properties="Configuration=Automated_Build;" />
</Target>
<Target Name="Build" DependsOnTargets="Clean;Compile">
<Message Text="Clean, Compile"/>
</Target>
</pre></blockquote></div><div style="line-height: 1.8;">Things to notice:</div><ul style="line-height: 1.8;"><li>Extra variable for the solution to compile</li>
<li>Extra target called Compile</li>
<li>Compile target uses msbuild to compile the solution</li>
<li>Solution Configuration is set to <strong>Automated_Build</strong></li>
<li>The Build target now depends on Compile too.</li>
</ul><div style="line-height: 1.8;">If you run this you should receive an error stating that the Automated_Build configuration does not exists. We need to create this configuration inside visual studio.</div><h3 style="line-height: 1.8;"><span class="Apple-style-span" style="font-size: x-large;"><span class="Apple-style-span" style="font-weight: normal;">6) Creating the Configuration “Automated_Build” in VS</span></span></h3><div style="line-height: 1.8;">Open up our visual studio solution, then under the <strong>Build </strong>menu, select <strong>Configuration Manager</strong>. Create a new configuration called “Automated_Build” as show below, copy the settings from the release configuration.</div><div style="font-size: 14px; line-height: 1.8;"><br />
</div><div style="font-size: 14px; line-height: 1.8;"><div class="separator" style="clear: both; text-align: center;"><a href="http://lh3.ggpht.com/_RaIYK6kVO1A/TLml92-207I/AAAAAAAAAF8/FfzJFY80Ka0/s1600-h/Creating%20the%20Configuration%20inside%20Visual%20Studio%5B3%5D.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="Creating the Configuration inside Visual Studio" border="0" src="http://lh6.ggpht.com/_RaIYK6kVO1A/TLml-pUaK7I/AAAAAAAAAGA/2c6wfk79uCE/Creating%20the%20Configuration%20inside%20Visual%20Studio_thumb%5B1%5D.png?imgmax=800" style="border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px;" title="Creating the Configuration inside Visual Studio" /></a></div></div><div style="font-size: 14px; line-height: 1.8;"><br />
</div><div style="font-size: 14px; line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">I am going to unselect <b>SampleWeb </b>because I do not want that project compiled as part of the automated build.</div><div style="font-size: 14px; line-height: 1.8;"><br />
</div><div style="font-size: 14px; line-height: 1.8;"><a href="http://lh4.ggpht.com/_RaIYK6kVO1A/TLml_kzELcI/AAAAAAAAAGE/HPFO0Hk65Zc/s1600-h/Configuration%20Setup%20-%20Removed%20Sample%20WEb%5B3%5D.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="Configuration Setup - Removed Sample WEb" border="0" src="http://lh4.ggpht.com/_RaIYK6kVO1A/TLmmAhewIkI/AAAAAAAAAGI/2yfzxdT8LyY/Configuration%20Setup%20-%20Removed%20Sample%20WEb_thumb%5B1%5D.png?imgmax=800" style="border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px;" title="Configuration Setup - Removed Sample WEb" /></a></div><div style="font-size: 14px; line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">Now we will change the output directory for the “Jobping.StickyBeak” project under the “Automated_Build” configuration. As Show below:</div><div style="font-size: 14px; line-height: 1.8;"></div><div style="font-size: 14px; line-height: 1.8;"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_RaIYK6kVO1A/TLm0O0gQ9wI/AAAAAAAAAGc/3pMTldrfGOE/s1600/Change+the+output+directory+to+build.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/_RaIYK6kVO1A/TLm0O0gQ9wI/AAAAAAAAAGc/3pMTldrfGOE/s1600/Change+the+output+directory+to+build.PNG" /></a></div><div style="font-size: 14px; line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">With our project configuration complete we can now run our build. </div><div style="font-size: 14px; line-height: 1.8;"><br />
</div><div style="font-size: 14px; line-height: 1.8;"><a href="http://lh6.ggpht.com/_RaIYK6kVO1A/TLmmEH50YnI/AAAAAAAAAGU/DTZcdVuda_w/s1600-h/Build%20Output%5B3%5D.png"><img alt="Build Output" border="0" height="567" src="http://lh6.ggpht.com/_RaIYK6kVO1A/TLmmE-8d-7I/AAAAAAAAAGY/EfX7QoNLDHw/Build%20Output_thumb%5B1%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px;" title="Build Output" width="1241" /></a> </div><div style="font-size: 14px; line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">As you can see we now have our freshly compiled binaries in the build directory.</div><div style="line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">By simply adding more targets to your build file you can automate any tedious step that is needed to build and package your project.</div><div style="line-height: 1.8;"><br />
</div><div style="line-height: 1.8;">This is just the beginning! next we will look at </div><div style="line-height: 1.8;"></div><ul><li><a href="http://markkemper1.blogspot.com/2010/10/create-build-file-for-visual-studio.html" style="color: blue; text-decoration: none;">Create a Build File for a Visual Studio Solution - MsBuild Series</a> </li>
<li><a href="http://markkemper1.blogspot.com/2010/10/mercurial-revision-no-to-version-your.html" style="color: blue; text-decoration: none;">Mercurial Revision No to Version your AssemblyInfo - MsBuild Series</a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/zipping-build-outputs-using-build-file.html" style="color: blue; text-decoration: none;">Zipping Build Outputs - MsBuild Series </a></li>
<li><a href="http://markkemper1.blogspot.com/2010/10/creating-nupack-package-using-build.html">NuPack packaging - MsBuild Series</a></li>
<li><span class="Apple-style-span" style="line-height: 25px;">NUnit Testing - MsBuild Series</span></li>
<li><span class="Apple-style-span" style="line-height: 25px;">Web Projects and AspNetComplier - MsBuild Series</span></li>
<li><span class="Apple-style-span" style="line-height: 25px;">Cruise Control - MsBuild Series</span></li>
</ul><div><span class="Apple-style-span" style="line-height: 28px;">Resources</span><span class="Apple-style-span" style="line-height: 1.8;">:</span></div><ul><li><a href="http://pastie.org/1228411">Sample MsBulid File</a></li>
<li><span class="Apple-style-span" style="font-size: 14px; line-height: 25px;"><a href="http://msdn.microsoft.com/en-us/library/5dy88c2e.aspx">MSBuild Project File Schema Reference</a></span></li>
</ul><br />
<a href="http://dotnetshoutout.com/Marks-aspnet-mvc-blog-Create-a-Build-File-for-a-Visual-Studio-Solution-MsBuild-Series" rev="vote-for"><img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fmarkkemper1.blogspot.com%2F2010%2F10%2Fcreate-build-file-for-visual-studio.html" style="border: 0px;" /></a> <a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f10%2fcreate-build-file-for-visual-studio.html"><img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%253a%252f%252fmarkkemper1.blogspot.com%252f2010%252f10%252fcreate-build-file-for-visual-studio.html" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com5tag:blogger.com,1999:blog-3340352991227979961.post-5556533459956922972010-10-02T09:30:00.000-07:002010-10-02T09:36:25.683-07:00Jobping Url Shortener – Version 0.6We have just checked in our latest version of the Jobping Url Shortener, version 0.6. This version resolves an important issue raised by @<strong>nato24</strong>. Nato24 asked “Have you guys implemented a solution for obscenities in your short url encoder?” . Thanks nato24 & Good question!<br />
<br />
Our short urls are too short at the moment to create any four letter words(yet!) but it was just a matter of time before all possible 4 letter words were used.<br />
<br />
So, in this release we have decided to remove all vowels (‘a’, ‘e’, ‘i’, ‘o’, ‘u’, ‘A’, ‘E’, ‘I’," ‘O’, ‘U’) from the possible letters used in the Url shortener. This means that our urls will grow a little faster but we were willing to sacrifice this for wordless urls. <br />
<br />
However, we do get a major benefit also, we can now make our own short urls using words (that contain vowels). So we are free to make up short urls like <a href="http://jobp.in/example">http://jobp.in/example</a> and know that the shortener will never create a clashing url because it contains a vowel (or 3).<br />
<br />
We also added a feature that allows us to offset the urls generated. By adding this offset into the configuration we allowed our url shortener to skip over any duplicates that would have been created if we just removed the vowels from our code.<br />
<br />
The new version is on codeplex here: <a href="http://jpurlshortener.codeplex.com/">http://jpurlshortener.codeplex.com/</a><br />
<br />
The production site for our shortener is here: <a href="http://jobp.in/">http://jobp.in/</a><br />
<br />
Also be sure to visit <a href="http://www.jobping.com/">Jobping</a>, which is now Global and advertises positions based on Microsoft Technologies.<br />
<br />
<a rev="vote-for" href="http://dotnetshoutout.com/Marks-aspnet-mvc-blog-Jobping-Url-Shortener-Version-06"><img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fmarkkemper1.blogspot.com%2F2010%2F10%2Fjobping-url-shortener-version-06.html" style="border:0px"/></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com0tag:blogger.com,1999:blog-3340352991227979961.post-84341017318823523392010-10-02T05:49:00.000-07:002010-10-20T10:16:12.881-07:00StickyBeak Version 0.3 ReleasedFinally found some time to add a couple of features to StickyBeak. <br />
<br />
StickyBeak is a logging utility for asp.net websites which can log every request to your site. It provides similar features as IIS log files but provides additional logging information (which just isn’t possible with IIS logs) and easy viewing of logs via a admin page. You can also use the StickyBeak log file parser in your own code.<br />
<br />
StickyBeak records request details such as url, ip address, unique session Id, datestamp, cookies, querystring, form values, session variables etc. StickyBeak allows you to track the requests that lead up to errors/exceptions on your site. So it provides valuable context for figuring out exactly what caused an error. StickyBeak is complimentary to elmah.<br />
<br />
We use a modified version of StickyBeak on <a href="http://www.jobping.com/">Jobping</a> . <br />
<br />
Version 0.3 is now on codeplex and includes the following additional features:<br />
<br />
<strong> 1) Logs Asp.Net Session keys and values.</strong><br />
Session keys and values are now recorded alongside the existing request data (cookies, querystring, posted form values, headers etc).<br />
<br />
<strong> 2) Allows StickyBeak to be temporarily turned on or off via the admin page. </strong><br />
StickyBeak can be Enabled via the configuration file but now you can temporary override this setting on the admin page. Therefore you can have StickyBeak turned off in the configuration file (which means no logging is taking place) then temporary turn on the logging via the admin page. If the application is restarted StickyBeak will then revert to the config setting.<br />
<h4>Upcoming features for StickyBeak</h4>We would love to hear any suggestions you may have for the next version so please send them through. Currently we are planning to include features such as: <br />
<br />
<b>Database integration</b> - which will allow log files to be consolidated from multiple sources into a rational db for analysis.<br />
<br />
<b>Viewstate (logging/decoding) </b>– allow Viewstate to be logged and decoded (we are using mvc :)<br />
<br />
You can read more about StickyBeak here: <a href="http://markkemper1.blogspot.com/2010/06/introduction-to-stickybeak.html">http://markkemper1.blogspot.com/2010/06/introduction-to-stickybeak.html</a><br />
<br />
StickyBeak is hosted on Codeplex: <a href="http://stickybeak.codeplex.com/">http://stickybeak.codeplex.com/</a><br />
<br />
<a rev="vote-for" href="http://dotnetshoutout.com/Marks-aspnet-mvc-blog-StickyBeak-Version-03-Released"><img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fmarkkemper1.blogspot.com%2F2010%2F10%2Fstickybeak-version-03-released.html" style="border:0px"/></a><br />
<a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f10%2fstickybeak-version-03-released.html"><img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%253a%252f%252fmarkkemper1.blogspot.com%252f2010%252f10%252fstickybeak-version-03-released.html" border="0" alt="kick it on DotNetKicks.com" /></a><br />
<br />
StickyBeak in action below. New features highlighted with red squares.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_RaIYK6kVO1A/TKcpq94ANtI/AAAAAAAAAF4/s2TVWEY5unc/s1600/Sample.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="405" src="http://1.bp.blogspot.com/_RaIYK6kVO1A/TKcpq94ANtI/AAAAAAAAAF4/s2TVWEY5unc/s640/Sample.PNG" width="640" /></a></div><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com0tag:blogger.com,1999:blog-3340352991227979961.post-74670452844985343022010-06-22T04:07:00.000-07:002010-10-20T10:16:19.396-07:00Introduction to StickyBeakWhile working on <a href="http://www.jobping.com/">Jobping </a>we wanted a raw record of each request made to our site so IF something happens to go wrong we would have all the data necessary to recreate the event and/or the data itself. Needless to say that it has proved very useful to investigate what has happened on the site. <br />
<br />
However the code used for this logging is embed into the main project and not easily portable, I wanted to make a assembly that captured this functionality so I could easily drop it into the next project. So we created StickyBeak.<br />
<br />
StickyBeak is a logging tool for asp.net websites written in c# and currently requires the <a href="http://nlog-project.org/">NLog </a>logging library to run. StickyBeak’s purpose is to log each request to your web server and also provide a easy interface to view these requests.<br />
<br />
Looking at these logged requests is extremely useful when you are trying to find the cause of an exception or even more useful when you are trying to recover some lost data because of an exception. <br />
<br />
StickyBeak works as an HttpModule and logs the raw request data into a log file using <a href="http://nlog-project.org/">Nlog</a>. The information recorded for the requests includes, date, http method, url, User.Identity.Name, IP Address, unique session Id, unique browser Id, header values, querystring values, posted form values and cookie values.<br />
<br />
Below is a screenshot of the admin viewing tool, which lets you see the logged activity on your site.<br />
<br />
<a href="http://2.bp.blogspot.com/_RaIYK6kVO1A/TCCTop4FGII/AAAAAAAAAFQ/tc3yC3iD5Ek/s1600/AdminInterface.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img height="402" src="http://2.bp.blogspot.com/_RaIYK6kVO1A/TCCTop4FGII/AAAAAAAAAFQ/tc3yC3iD5Ek/s640/AdminInterface.PNG" style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(170, 170, 170); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(170, 170, 170); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(170, 170, 170); border-top-style: solid; border-top-width: 1px;" width="640" /></a><br />
<h3>How it works</h3>StickyBeak runs as a HttpModule, each time a request is processed by .net the module creates a new RequestLog object and populates all the data using the current request. The RequestLog object is then passed to the LogRepository which saves the Requestlog object.<br />
<br />
Currently there is only one LogRepository, this repository uses NLog. The NLogRepository writes the LogRequest object out to the log files in a custom format. The NLogRepository can also read LogRequest objects for viewing using the admin interface.<br />
<h3>Configuration Needed For StickyBeak To Work</h3><ol><li> You need a reference to the StickyBeak and NLog assemblies contained in the binary zip file distribution on <a href="http://stickybeak.codeplex.com/">CodePlex</a></li>
<li> Configure the StickyBeak HttpModule<br />
<a href="http://3.bp.blogspot.com/_RaIYK6kVO1A/TCCUfHQoPkI/AAAAAAAAAFY/lZ5X2q26-KQ/s1600/HttpModuleConfig.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="44" src="http://3.bp.blogspot.com/_RaIYK6kVO1A/TCCUfHQoPkI/AAAAAAAAAFY/lZ5X2q26-KQ/s640/HttpModuleConfig.PNG" width="640" /></a><br />
</li>
<li> Configure the handler (to display admin interface). The configuration below also secures the handler you may wish to remove this for testing.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_RaIYK6kVO1A/TCCUwce5CuI/AAAAAAAAAFg/Y6jocpgHWT0/s1600/HandlerConfig.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="140" src="http://1.bp.blogspot.com/_RaIYK6kVO1A/TCCUwce5CuI/AAAAAAAAAFg/Y6jocpgHWT0/s640/HandlerConfig.PNG" width="640" /></a></div></li>
<li>Configure NLog to record the logging information that is record by StickBeak<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_RaIYK6kVO1A/TCCWKphv7KI/AAAAAAAAAFo/3xGpAkr11fo/s1600/NLogConfig.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="116" src="http://4.bp.blogspot.com/_RaIYK6kVO1A/TCCWKphv7KI/AAAAAAAAAFo/3xGpAkr11fo/s640/NLogConfig.PNG" width="640" /></a></div></li>
<li>You can also optionally configure exclusions for StickyBeak. For example you could exclude all requests to a certain URL, exclude a querystring/form/cookie/header value by key etc. See the sample configuration for more details.<br />
</li>
</ol>You can download the source and binaries from <a href="http://stickybeak.codeplex.com/">StickyBeak on CodePlex</a>. <a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f06%2fintroduction-to-stickybeak.html"><img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f06%2fintroduction-to-stickybeak.html" /></a> <a href="http://dotnetshoutout.com/ntroduction-to-StickyBeak" rev="vote-for"><img alt="Shout it" src="http://dotnetshoutout.com/image.axd?url=http%3A%2F%2Fmarkkemper1.blogspot.com%2F2010%2F06%2Fintroduction-to-stickybeak.html" style="border: 0px;" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com29tag:blogger.com,1999:blog-3340352991227979961.post-51552345888606061732010-05-18T01:03:00.000-07:002010-05-18T01:03:08.799-07:00Quickly Trim all model's Properties - Reflection Vrs Fasterflect<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">I've had some great feedback from <b><a href="http://www.buunguyen.net/blog/">Buu Nguyen</a> </b>author of the Fasterflect library. He supplied the feedback below as well as some sample code, which I have incorporated into the code base. </div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"></div><br />
Buu Nguyen says:<br />
<blockquote>Fasterflect only speeds up invocation operations, not query (aka lookup) operations. In fact, query operations in Fasterflect are convenient wrapper for .NET reflection, so using them will cause the code to run slower.<br />
<br />
<strong>Suggested usage</strong>: avoid query operations if you don’t really need them; move the GetProperties out of each iteration because it is the same for all methods and thus makes it hard to see the performance difference when using Fasterflect.<br />
<br />
The delegates should be reused instead of being regenerated every time – the latter will make the code run even slower than the normal Fasterflect API.<br />
<strong>Suggested usage:</strong> use a dictionary to cache the generated </blockquote><br />
The main change to the code was to cache the call to <b>type.GetProperties()</b>. So instead of calling the <b>GetProperties() </b>method directly a call is made to a caching component which ensures that the <b>GetProperties() </b>call is only made once per type.<br />
<br />
Also, as Buu has suggested, the calls for generating the delegate setters and getters are now cached into a static dictionary as well.<br />
<span class="Apple-style-span" style="font-weight: bold;"><strong><br />
</strong></span><br />
<span class="Apple-style-span" style="font-weight: bold;"><strong>Test Methods </strong><span class="Apple-style-span" style="font-weight: normal;">(2-4 using cached call to type.GetProperties() )</span><strong>:</strong></span><br />
<ol><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"></div><ol><li><strong>Long Hand</strong> - No reflection.</li>
<li><strong>Initial code</strong> - normal reflection</li>
<li><strong>FasterFlect m1 - </strong>Uses the <a href="http://fasterflect.codeplex.com/">FasterFlect</a> library</li>
<li><strong>FasterFlect m2</strong> – Uses the <a href="http://fasterflect.codeplex.com/">FasterFlect</a> library’s delegates approach</li>
</ol><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"></div></ol><h4>Results</h4><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">So here are the results (over 1 million object trims):</div><table border="0" cellpadding="2" cellspacing="0" style="width: 400px;" unselectable="on"><tbody>
<tr><td valign="top" width="200"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><strong>Long Hand Trim</strong></div></td><td valign="top" width="200"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">00:00:01.4150000</div></td></tr>
<tr><td valign="top" width="200"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><strong>Initial code</strong></div></td><td valign="top" width="200"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">00:00:08.8280000</div></td></tr>
<tr><td valign="top" width="200"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"></div></td><td valign="top" width="200"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"></div></td></tr>
<tr><td valign="top" width="200"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><strong>Fasterflect m1</strong></div></td><td valign="top" width="200"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">00:00:04.8190000</div></td></tr>
<tr><td valign="top" width="200"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><strong>Fasterflect </strong><b>m2</b></div></td><td valign="top" width="200"><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">00:00:02.9920000</div></td></tr>
</tbody></table><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><br />
</div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">Ok, now we are seeing Fasterflect perform nearly twice as fast as reflection and about 3 times as fast when using the delegate method of Fasterflect.<br />
<br />
It also appears that the delegate method of Fasterflect is only about twice as slow as the long hand trim.</div><h5><a href="http://bitbucket.org/markkemper1/quicktrimperformance/downloads">Download the source code.</a></h5><h4>Long Hand code</h4><div class="separator" style="clear: both; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;"><a href="http://4.bp.blogspot.com/_RaIYK6kVO1A/S_DoyTQQOHI/AAAAAAAAAEY/C50avJRmP0I/s1600/LongHand.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="120" src="http://4.bp.blogspot.com/_RaIYK6kVO1A/S_DoyTQQOHI/AAAAAAAAAEY/C50avJRmP0I/s640/LongHand.PNG" width="640" /></a></div><div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><br />
</div></div><h4><br />
</h4><h4><br />
</h4><h4><br />
</h4><h4>Property Cache Helper</h4><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_RaIYK6kVO1A/S_GBT3ncERI/AAAAAAAAAEg/-0apWPPumW0/s1600/PropertyCache.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="322" src="http://3.bp.blogspot.com/_RaIYK6kVO1A/S_GBT3ncERI/AAAAAAAAAEg/-0apWPPumW0/s640/PropertyCache.PNG" width="640" /></a></div><div><br />
</div><h4>Initial code</h4><div class="separator" style="clear: both; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;"></div><div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_RaIYK6kVO1A/S_GBa4qIIDI/AAAAAAAAAEo/mjGktWOW5ww/s1600/InitialCode.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="266" src="http://3.bp.blogspot.com/_RaIYK6kVO1A/S_GBa4qIIDI/AAAAAAAAAEo/mjGktWOW5ww/s640/InitialCode.PNG" width="640" /></a></div><br />
</div></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><br />
</div><h4><br />
</h4><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><br />
</div><h4><span class="Apple-style-span" style="font-weight: normal;"><b><br />
</b></span></h4><h4>FasterFlect Method #1</h4><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_RaIYK6kVO1A/S_GBzjupGgI/AAAAAAAAAE4/V9vJT1CYfkg/s1600/Fasterflect1.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="242" src="http://3.bp.blogspot.com/_RaIYK6kVO1A/S_GBzjupGgI/AAAAAAAAAE4/V9vJT1CYfkg/s640/Fasterflect1.PNG" width="640" /></a></div><div><br />
</div><h4><strong>FasterFlect Method #2</strong></h4><div class="separator" style="clear: both; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;"></div><div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_RaIYK6kVO1A/S_GB7TD8hWI/AAAAAAAAAFA/k2PLNIckZnU/s1600/Fasterflect2.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="388" src="http://1.bp.blogspot.com/_RaIYK6kVO1A/S_GB7TD8hWI/AAAAAAAAAFA/k2PLNIckZnU/s640/Fasterflect2.PNG" width="640" /></a></div><strong><br />
</strong></div></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><br />
</div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"></div><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com2tag:blogger.com,1999:blog-3340352991227979961.post-1026553074360621902010-05-18T00:29:00.000-07:002010-05-18T00:34:33.212-07:00Set Default Outgoing Repository with hg (Mercurial)Firstly if you are new to Mercurial check out <a href="http://hginit.com/">http://hginit.com/</a> its a brilliant little Mercurial tutorial.<br />
<br />
When creating a hg repository locally, at some point later, I often need to set the default outgoing repository to push out my changes.<br />
<br />
Creating projects locally using hg (hg init) without cloning from anther location is ideal for playing with new projects locally and having your source code version controlled.<br />
<br />
There are so many times where I spend about 10 minutes going down a path then find out that I don't want to continue. With mercurial or git as a local version control system, you can simply revert back your changes to the last commit.<br />
<br />
When the time comes to publish your changes to the world, setting up a default outgoing repository in hg saves a lot of typing.<br />
<br />
<b>Here is how to do it</b><br />
<ol><li>Go to the .hg folder in the root of your repository</li>
<li>Create a new <b>file</b> called <b><span class="Apple-style-span" style="font-weight: normal;">"</span>hgrc</b>"</li>
<li>Enter your default outgoing repository as show below.</li>
</ol><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_RaIYK6kVO1A/S_I-SiFNAvI/AAAAAAAAAFI/ksjxBPtCV7w/s1600/hgrc.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="134" src="http://4.bp.blogspot.com/_RaIYK6kVO1A/S_I-SiFNAvI/AAAAAAAAAFI/ksjxBPtCV7w/s640/hgrc.PNG" width="640" /></a></div><br />
<div style="clear: left"></div>That's it. Now you can just enter "<b>hg outgoing</b>" to see the list of change-sets that need to be pushed to your default repository and "<b>hg push</b>" to actually push out your changes.<br />
<br />
<a href="http://www.codeplex.com/">Codeplex</a> now has <a href="http://mercurial.selenic.com/">Mercurial</a> as an option for version control.<br />
<br />
Here is a copy paste sample for the <a href="http://www.jobping.com/">Jobping</a> <a href="http://jpurlshortener.codeplex.com/">Url Shortener project on codeplex</a><br />
<blockquote>[paths]<br />
default = https://hg01.codeplex.com/jpurlshortener</blockquote><br />
<a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f05%2fset-default-outgoing-repository-with-hg.html"><img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f05%2fset-default-outgoing-repository-with-hg.html" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com1tag:blogger.com,1999:blog-3340352991227979961.post-35887554287212907922010-05-17T00:02:00.000-07:002010-05-18T22:15:19.501-07:00Quickly Trim all your model's string properties – Speed results<b>Update:</b><b><a href="http://markkemper1.blogspot.com/2010/05/quickly-trim-all-models-properties.html"> Posted another follow up with feedback on the usage of Fasterflect</a></b><br />
<br />
With my <a href="http://markkemper1.blogspot.com/2010/05/quickly-trim-all-your-models-string.html">Quickly Trim all your model's string properties</a> post causing quite a stir (2 comments), I have decided to post a follow up and actually test the speed.<br />
<br />
For the actual benchmarking I used a slightly modified version of this nice little class <a href="http://www.yoda.arachsys.com/csharp/Benchmark.cs">http://www.yoda.arachsys.com/csharp/Benchmark.cs</a>, which I found from this page <a href="http://www.yoda.arachsys.com/csharp/benchmark.html">http://www.yoda.arachsys.com/csharp/benchmark.html</a><br />
<br />
I tested 4 different methods that use reflection to trim all the string properties on an object and the 'long hand' method that does not use reflection at all.<br />
<h4><strong>Test Methods:</strong></h4><ol><ol><li><strong>Long Hand</strong> - No reflection.</li>
<li><strong>Initial code</strong> - with the GetIndexParameters call removed (not needed) </li>
<li><strong>Initial code with linq</strong> – Initial code but using linq to select the properties to process </li>
<li><strong>TypeDescriptor</strong>– used the TypeDescriptor class to get the property list </li>
<li><strong>FasterFlect m1 - </strong>Uses the <a href="http://fasterflect.codeplex.com/">FasterFlect</a> library </li>
<li><strong>FasterFlect m2</strong> – Uses the <a href="http://fasterflect.codeplex.com/">FasterFlect</a> library’s delegates approach </li>
</ol></ol><h4>Results</h4>So here are the results (over 1 million object trims):<br />
<table border="0" cellpadding="2" cellspacing="0" style="width: 400px;" unselectable="on"><tbody>
<tr> <td valign="top" width="200"><strong>Long Hand Trim</strong> </td> <td valign="top" width="200">00:00:01.4220000</td></tr>
<tr> <td valign="top" width="200"><strong>Initial code</strong> </td> <td valign="top" width="200">00:00:07.7130000</td></tr>
<tr> <td valign="top" width="200"><strong>Initial code with linq</strong></td> <td valign="top" width="200">00:00:10.7270000</td></tr>
<tr> <td valign="top" width="200"><strong>TypeDescriptor</strong></td> <td valign="top" width="200">00:00:14.2340000</td></tr>
<tr> <td valign="top" width="200"><strong>FasterFlect m1</strong></td> <td valign="top" width="200">00:00:06.1330000</td></tr>
<tr> <td valign="top" width="200"><strong>FasterFlect </strong>m2</td> <td valign="top" width="200">00:00:06.1830000</td></tr>
</tbody></table><br />
Surprised? I was. Firstly, the refection code varied from 7 - 14 times slower then the direct code, this wasn't a surprise more of a reminder that you really need to be careful when using reflection in your code base.<br />
<br />
I knew my initial code would perform better then trying to filter for properties before doing the work. Filtering the properties first using linq or any other means, basically results in another loop over the properties on the object. So the more properties you have on your object the worst this method will perform. <br />
<br />
But I was quite suprised with the TypeDescriptor. I had hopes that this would perform better then my initial code, as according to the documentation this method actually caches the meta data about objects. I am suspicious I haven’t used it to its full potential…<br />
<br />
The other surprise was that the delegate method in fasterflect is actually slower then the normal method. Again, someone may well point out how to implement this better. The other note is that fasterflect was only able to achieve an 80% reduction. I was hoping for more.<br />
<br />
Anyway interesting stuff, if someone has another fast method to achieve the same results let me know I’ll take it for a test drive. <br />
<h5><a href="http://bitbucket.org/markkemper1/quicktrimperformance/downloads">Download the source code.</a></h5><h4>Long Hand code</h4><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_RaIYK6kVO1A/S_DoyTQQOHI/AAAAAAAAAEY/C50avJRmP0I/s1600/LongHand.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="120" src="http://4.bp.blogspot.com/_RaIYK6kVO1A/S_DoyTQQOHI/AAAAAAAAAEY/C50avJRmP0I/s640/LongHand.PNG" width="640" /></a></div><div><br />
</div><h4>Initial code</h4><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_RaIYK6kVO1A/S-06R3g6w0I/AAAAAAAAADw/fgact8ns0dI/s1600/InitialCode.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="392" src="http://1.bp.blogspot.com/_RaIYK6kVO1A/S-06R3g6w0I/AAAAAAAAADw/fgact8ns0dI/s640/InitialCode.PNG" width="640" /></a></div><div><br />
</div><br />
<h4><strong>Initial code with linq</strong></h4><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_RaIYK6kVO1A/S-06Xq_5NII/AAAAAAAAAD4/HWhX0fx46dA/s1600/InitialCodeWithLinq.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="252" src="http://3.bp.blogspot.com/_RaIYK6kVO1A/S-06Xq_5NII/AAAAAAAAAD4/HWhX0fx46dA/s640/InitialCodeWithLinq.PNG" width="640" /></a></div><div><strong><br />
</strong></div><br />
<h4>TypeDescriptor</h4><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_RaIYK6kVO1A/S-06fGEf3mI/AAAAAAAAAEA/yckmdCrCo3c/s1600/TypeDescripter.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="286" src="http://3.bp.blogspot.com/_RaIYK6kVO1A/S-06fGEf3mI/AAAAAAAAAEA/yckmdCrCo3c/s640/TypeDescripter.PNG" width="640" /></a></div><div><br />
</div><br />
<h4>FasterFlect Method #1 </h4><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_RaIYK6kVO1A/S-06l2tVUxI/AAAAAAAAAEI/vXjkTE_2Zu8/s1600/FasterFlect_m1.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="358" src="http://4.bp.blogspot.com/_RaIYK6kVO1A/S-06l2tVUxI/AAAAAAAAAEI/vXjkTE_2Zu8/s640/FasterFlect_m1.PNG" width="640" /></a></div><div><br />
</div><br />
<h4><strong>FasterFlect Method #2</strong></h4><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_RaIYK6kVO1A/S-06tKcUMTI/AAAAAAAAAEQ/CiggQMA0J9c/s1600/FasterFlect_m2.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="370" src="http://4.bp.blogspot.com/_RaIYK6kVO1A/S-06tKcUMTI/AAAAAAAAAEQ/CiggQMA0J9c/s640/FasterFlect_m2.PNG" width="640" /></a></div><div><strong><br />
</strong></div><br />
<a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f05%2fquickly-trim-all-your-models-string_17.html"><img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f05%2fquickly-trim-all-your-models-string_17.html" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com4tag:blogger.com,1999:blog-3340352991227979961.post-43116059223538821122010-05-13T05:16:00.000-07:002010-05-13T05:38:27.934-07:00Jobping Open Source URL Shortener. Goes to Version 0.5We have updated our url shortener for<a href="http://www.jobping.com/"> Jobping</a>. j<a href="http://jobp.in/">obp.in</a> has moved along to Version 0.5. While maintaining as little features as possible!<br />
<br />
Since the initial version we have placed a restriction(web.configurable) on the domain names that the shortener will shorten. We did this so only domains under jobping.com would be processed by the shortener. We wanted to maintain the integrity of the jobp.in domain. With this in place you can be sure that if you click a link shortened by <a href="http://jobp.in/">http://jobp.in</a> it will always go to our site jobping.com site.<br />
<br />
A database migration to add case sensitivity to the short url column was added to the migration scripts. Previously this needed to be added manually after the install. Now it all runs out of the box, just x-copy, point it to a database and your good to go.<br />
<br />
This version also includes a new custom style, which looks awesome by the way, for the site. The interface is now slick and styled inline with our main site <a href="http://www.jobping.com/">Jobping</a>.<br />
<br />
The new version can be downloaded from codeplex here: <a href="http://jpurlshortener.codeplex.com/">http://jpurlshortener.codeplex.com/</a><br />
<br />
The introduction blog post can be found here: <a href="http://markkemper1.blogspot.com/2010/05/announcing-jp-url-shortener-open-source.html">http://markkemper1.blogspot.com/2010/05/announcing-jp-url-shortener-open-source.html</a><br />
<a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f05%2fjobping-open-source-url-shortener-goes.html"><img alt="kick it on DotNetKicks.com" border="0" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f05%2fjobping-open-source-url-shortener-goes.html" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com6tag:blogger.com,1999:blog-3340352991227979961.post-50581979277418372222010-05-11T04:06:00.000-07:002010-05-11T04:07:56.056-07:00Quickly Trim all your model's string properties<div class="post-header"></div>While developing a internal application for <a href="http://www.jobping.com/">http://www.jobping.com </a>using ASP.NET MVC 2 I had the old problem of trimming all string properties on the model before saving to the database.<br />
<br />
My first reaction was to start adding trim() everywhere but this causes problems when properties are null. Checking for null pushed me over the edge and I quickly came up with some code to do what I needed via reflection. <br />
<br />
The code is below, it will quickly trim any string properties on your model object (no promises of course) <br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/_RaIYK6kVO1A/S-gRmQh6iWI/AAAAAAAAADo/kjTipObaqVw/s1600/TrimModelStringProperties.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="456" src="http://2.bp.blogspot.com/_RaIYK6kVO1A/S-gRmQh6iWI/AAAAAAAAADo/kjTipObaqVw/s640/TrimModelStringProperties.PNG" width="640" /></a></div><br />
<br />
With this code, you can simply trim all string properties, lets say you have a blog model object called myBlog, just go myBlog.NullSafeTrimStrings(), job done.<br />
<br />
Be warned this is quite an expansive little operation. DO NOT go putting it in a for each loop<br />
<br />
<a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f05%2fquickly-trim-all-your-models-string.html"><img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f05%2fquickly-trim-all-your-models-string.html" border="0" alt="kick it on DotNetKicks.com" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com7tag:blogger.com,1999:blog-3340352991227979961.post-22680707284635387802010-05-02T23:50:00.000-07:002010-05-03T22:52:56.713-07:00Announcing: Jobping URL Shortener. Open source MVC.NET 2 C#<a href="http://www.jobping.com/">Jobping</a> is a niche job site for listing Microsoft related vacancies (Australia only for now). <a href="http://www.jobping.com/">Jobping</a> sends out <a href="http://twitter.com/jobping">tweets</a> (1 for each job posted on the site) and we wanted to brand our short URLs e.g. <a href="http://jobp.in/g">http://jobp.in/g</a>.<br />
<br />
We could have used <a href="http://bit.ly/">bit.ly</a> pro but the pro accounts are in invite only (check it out though, they offer additional reports etc). We only very basic requirements we decided just to go ahead and implement our own url shortener.<br />
<br />
Own URL shortener has a ‘less is more’ approach and was the quickest solution for us to get our own branded short URLs. <br />
<br />
The site requires IIS 7, .net 3.5, asp.net mvc 2 and a mssql database.<br />
<br />
Apart from creating our own URL shortener we also wanted to make it open source to help out other people looking for a bare bones URL shortener. You can find the source code on Codeplex here: <a href="http://jpurlshortener.codeplex.com/">http://jpurlshortener.codeplex.com/</a><br />
<br />
For anyone new to MVC or interested in how it works, I’ll go through the basic functions of the site below.<br />
<h4>Technical Description</h4>The URL shortener has 2 functions<br />
<ol><ol><li>Create a new short URL</li>
<li>Redirect anyone requesting a short URL</li>
</ol></ol><h5>Part 1 – Creating a short URL</h5>Get request for the root URL “/”<br />
<a href="http://lh5.ggpht.com/_RaIYK6kVO1A/S95w5CcW2XI/AAAAAAAAAC0/BUTV3t_99DA/s1600-h/ControllerIndexGet%5B5%5D.png"><img alt="ControllerIndexGet" border="0" height="125" src="http://lh3.ggpht.com/_RaIYK6kVO1A/S95w6C1AOdI/AAAAAAAAAC4/JlvBWRRjVIc/ControllerIndexGet_thumb%5B3%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline;" title="ControllerIndexGet" width="816" /></a> <br />
<br />
This method on the Home controller simply returns the view. The view contains a form that allows someone or something to post a long URL and have a short URL returned.<br />
<br />
The view for the GET request is shown below (This uses a common master page).<br />
<br />
<a href="http://lh6.ggpht.com/_RaIYK6kVO1A/S95w8dmubnI/AAAAAAAAAC8/JWKuBftocys/s1600-h/HomeIndexViewGet%5B7%5D.png"><img alt="HomeIndexViewGet" border="0" height="251" src="http://lh4.ggpht.com/_RaIYK6kVO1A/S95w92oiaYI/AAAAAAAAADA/1E6Ujg4uHAo/HomeIndexViewGet_thumb%5B3%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline;" title="HomeIndexViewGet" width="991" /></a> <br />
<br />
When the form is posted back to the “/” route the follow method is invoked. <br />
<br />
<a href="http://lh6.ggpht.com/_RaIYK6kVO1A/S95xA7Z8P0I/AAAAAAAAADE/ygyDohHbyZE/s1600-h/ControllerIndexPost%5B3%5D.png"><img alt="ControllerIndexPost" border="0" height="717" src="http://lh5.ggpht.com/_RaIYK6kVO1A/S95xDKra03I/AAAAAAAAADI/aRuYRXfGRyg/ControllerIndexPost_thumb%5B1%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline;" title="ControllerIndexPost" width="913" /></a> <br />
<br />
You can see that this method is attributed with Http Post, this means that this method is only applicable when a HTTP Post is issued to the server. The method first checks if a short URL already exists for the long URL requested, if so the existing short URL is used. Otherwise a new URL object is created and saved into the database. You will notice that we first create a URL object in the database then use the Identity column as the seed to generate our short URL. Once we have generated our short URL we save this into the database. These operations exist inside a transaction to make sure we do not end up with a blank Short URL column in our DB.<br />
<br />
The other point is that if the format parameters is present and contains the value “txt” a text only view is returned. This allows the same method to be used for API calls. Clients simply pass the format parameters and read the short URL from the result. <br />
<br />
The Result view for the html result is shown below (This uses a common master page).<br />
<br />
<a href="http://lh3.ggpht.com/_RaIYK6kVO1A/S95xErr-mGI/AAAAAAAAADM/IOUnYn1mwLo/s1600-h/HomeIndexViewSuccess%5B3%5D.png"><img alt="HomeIndexViewSuccess" border="0" height="169" src="http://lh6.ggpht.com/_RaIYK6kVO1A/S95xGFoLo-I/AAAAAAAAADQ/UqkHSPvpHzs/HomeIndexViewSuccess_thumb%5B1%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline;" title="HomeIndexViewSuccess" width="1387" /></a> <br />
The Result.TXT view is shown below, here no master page is used and the only the short URL is returned<br />
<a href="http://lh4.ggpht.com/_RaIYK6kVO1A/S95xIB5xWDI/AAAAAAAAADU/8Tk7W7UsdH0/s1600-h/HomeIndexViewSuccessTXT%5B7%5D.png"><img alt="HomeIndexViewSuccessTXT" border="0" height="83" src="http://lh5.ggpht.com/_RaIYK6kVO1A/S95xJf7NRRI/AAAAAAAAADY/9eHAtBmokVE/HomeIndexViewSuccessTXT_thumb%5B3%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline;" title="HomeIndexViewSuccessTXT" width="1034" /></a> <br />
<br />
I skipped over the actual creation of the short URL. The creation happens in our ShortUrlEncoder object. Basically this object base encodes the long integer value using a series of 64 characters that are suitable for URLs. <br />
<br />
Here is a sample of what our encoder does to some sample numbers (in powershell)<br />
<br />
<a href="http://lh3.ggpht.com/_RaIYK6kVO1A/S95rNRini1I/AAAAAAAAACs/1_8SNgDiX_Y/s1600-h/Encoder%5B6%5D.png"><img alt="Encoder" border="0" height="747" src="http://lh6.ggpht.com/_RaIYK6kVO1A/S95rTiERPEI/AAAAAAAAACw/xyvMhKl-awQ/Encoder_thumb%5B4%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline;" title="Encoder" width="1008" /></a> <br />
<br />
We simply just append the output of the encode to the current domain name, so “qCc” would be a short URL of “http://jobp.in/qCc” <br />
<h5>2. Redirect anyone requesting a short URL</h5>The redirection is really quite straight forward, we simple route all that are not “/” to our “Follow” method on the Home controller.<br />
<br />
<a href="http://lh4.ggpht.com/_RaIYK6kVO1A/S95xMcD04tI/AAAAAAAAADc/h5GhP3gPpjQ/s1600-h/ControllerHomeFollow%5B3%5D.png"><img alt="ControllerHomeFollow" border="0" height="430" src="http://lh3.ggpht.com/_RaIYK6kVO1A/S95xNrfWAqI/AAAAAAAAADg/AbVJSPg8Au8/ControllerHomeFollow_thumb%5B1%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline;" title="ControllerHomeFollow" width="789" /></a> <br />
<br />
As you can see, we simply retrieve the short URL from the database. If its not found, then return a 404, otherwise we redirect the user to the long URL destination. You can also see that we are using the current URL as the base when forming our short URL. <br />
<br />
That’s it for now, you can check out the code on Codeplex here: <a href="http://jpurlshortener.codeplex.com/">http://jpurlshortener.codeplex.com/</a><br />
We would welcome any feedback on the project.<br />
<br />
<p><a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f05%2fannouncing-jp-url-shortener-open-source.html"><img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fmarkkemper1.blogspot.com%2f2010%2f05%2fannouncing-jp-url-shortener-open-source.html" border="0" alt="kick it on DotNetKicks.com" /></a></p><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com4tag:blogger.com,1999:blog-3340352991227979961.post-76434464922617546172010-04-17T12:11:00.000-07:002010-04-18T08:17:41.385-07:00ASP.NET Force the Browser to Really CACHE Content<div><br /></div><div>Recently I have been optimizing the load time for our site <a href="http://www.jobping.com/">Jobping</a>. Getting the browser to really cache a file (not request that file for the duration specified) is something that always takes some time to get right. </div><div><br /></div><div>So I just wanted to share the code to achieve it and hope it helps someone else out.</div><div><br /></div><div> <span class="Apple-tab-span" style="white-space:pre"> </span>TimeSpan duration = TimeSpan.FromMinutes(cacheDurationInMinutes);</div><div> HttpCachePolicy cache = HttpContext.Current.Response.Cache;</div><div> cache.SetCacheability(HttpCacheability.Public);</div><div> cache.SetExpires(DateTime.Now.Add(duration));</div><div> cache.SetMaxAge(duration);</div><div> cache.AppendCacheExtension("must-revalidate, proxy-revalidate");</div><div><br /></div><div> FieldInfo maxAgeField = cache.GetType().GetField("_maxAge", BindingFlags.Instance | BindingFlags.NonPublic);</div><div> <span class="Apple-tab-span" style="white-space:pre"> </span>maxAgeField.SetValue(cache, duration); </div><div><br /></div><div>This will instruct the browser to store a copy of the url andfor the duration given. So be sure that this is applicable for your content before using this code (YMMV).</div><div><br /></div><br /><br /><p><br /><a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fxsation.blogspot.com%2f2010%2f04%2faspnet-force-browser-to-really-cache.html"><img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fxsation.blogspot.com%2f2010%2f04%2faspnet-force-browser-to-really-cache.html" border="0" alt="kick it on DotNetKicks.com" /></a><br /></p><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com0tag:blogger.com,1999:blog-3340352991227979961.post-254119123239678602008-06-15T06:27:00.000-07:002008-06-15T19:17:21.253-07:00Mvc.Net preview 3 validation - server and client sideWORKING EXAMPLE: <a href="http://www.xsation.net.au/Samples/MvcValidation/Comment.aspx">http://www.xsation.net.au/Samples/MvcValidation/Comment.aspx</a><br />DOWNLOAD SOURCE: <a href="http://www.xsation.net.au/Samples/MvcValidation/MvcValidationSample.zip">http://www.xsation.net.au/Samples/MvcValidation/MvcValidationSample.zip</a><br /><br />If you need some basics on MVC check out:<br /><ul><li><a href="http://weblogs.asp.net/scottgu/archive/tags/MVC/default.aspx">Scott Gu's Blog</a><br /></li><li><a href="http://www.asp.net/mvc/">Asp.Net Site</a></li><li><a href="http://blog.wekeroad.com/tags/mvc/">Rob Conery </a></li></ul><br />Over the last few weeks I have been toying with .net .mvc framework.<br /><p>While enjoying the freedom that mvc offers from the 'old school'(too early?) .net forms , there are a few sore points<br />when working with mvc:<br /></p><ol><li>No composite controllers</li><br /><li>No built in support for validation on the server and client site</li></ol><p>Scott Gu has hinted that we will see support for validation in the upcoming releases of the mvc framework, however I need something now.</p><p>The rest of the post describes the approach I am trialling at the moment using jquery </p><h2>The controller</h2><p>First on the controller I wanted my action method to simply look like the following:<br /></p><blockquote><ol><li>Is this a get request? yes -> return the form(view)..</li><br /><li>It's a post. Is the form validated? no -> return the form with errors</li><br /><li>It's a validate post. Save the entity then redirect somewhere</li></ol></blockquote><p>The code example deals with a simple scenario where we collect a comment and displaying the list of comments collection so far on the page.<br /></p><p>In my example, the c# code for the controller looks like this<br /></p><blockquote><pre><br />//Initialize the view data<br />CommentListData vd = new CommentListData();<br /><br />//add the current list of comments to the view data<br />vd.Comments = _comments;<br /><br />//1. Its is a gets , just return the list of comments and form<br />if (Request.HttpMethod == "GET")<br />return Index();<br /><br />//Check if the form is valid...<br />if (!vd.Form.Validate(Request.Form))<br />{<br />//if its not, the form will have all the errors messages populated in the viewddata (vd).<br />return View("Index", vd);<br />}<br /><br />//Form is valid, so we can add our new comment.<br />Comment comment = new Comment();<br /><br />//Set all validated properties<br />vd.Form.UpdateEntity(comment);<br />_comments.Add(comment);<br /><br />///Redirects back to the comment list of comments.<br />return RedirectToAction( "Index");<br /></pre></blockquote><p>Although a little long, it achieves the 3 goals above of how a simple form post should be handled.</p><p>The definition of the CommentListData object is show below<br /></p><blockquote><pre> public class CommentListData<br />{<br />public CommentListData() { this.Form = new CommentForm(); }<br /><br />public List<comment> Comments { get; set; }<br />public CommentForm Form { get; set; }<br />}<br /></comment></pre></blockquote><p>This is pretty self explanatory, we have a list of comments and a form. The form object deals with the collection of the 3 inputs from the<br />posted form, 1) A person's name 2) A person's email and 3) the persons comment. </p><p>The form object provides the following basic functions</p><ol><li>Holds the raw (string) data posted from the raw http form<br /></li><br /><li>Performs validation to determine if the data is valid</li><br /><li>Transfers the validated data into the real typed entity</li><br /></ol><p>The actual implementation of the CommentForm is posted below.</p><blockquote><pre><br />public class CommentForm : Form<comment><br />{<br />public FormField Name = new FormField("Comment_Name");<br />public FormField EmailAddress = new FormField("Comment_EmailAddress");<br />public FormField Comments = new FormField("Comment_Comments");<br /><br />public RequiredFieldValidator NameRequired;<br />public RequiredFieldValidator EmailAddressRequired;<br />public RequiredFieldValidator CommentsRequired;<br />public RegexFieldValidator EmailAddressRegex;<br /><br />public override void AddValidators(List<validator> validators)<br />{<br /> this.NameRequired = new RequiredFieldValidator("Please enter your name", Name);<br /> this.EmailAddressRequired = new RequiredFieldValidator("Please enter your email address", EmailAddress);<br /> this.CommentsRequired = new RequiredFieldValidator("Please enter your comments", Comments);<br /> this.EmailAddressRegex = new RegexFieldValidator("Please check the format of your email address", EmailAddress, "..*@..*\\...*");<br /><br /> validators.Add(this.NameRequired);<br /> validators.Add(this.EmailAddressRequired);<br /> validators.Add(this.CommentsRequired);<br /> validators.Add(this.EmailAddressRegex);<br />}<br />}<br /></validator></comment></pre></blockquote><p>Here we define our 3 fields that we wish to collection form the raw http form. Then we add all our validators to the form</p><p>The CommentForm inherits from a generic Form object, we pass our Comment object in as the generic. The base form object can handle the following functions now it has a Comment object<br /></p><ol><li>It can validate the posted form using the validates we have described</li><br /><li>It can set the form up with initial values from a Comment object( used for editing purposes)</li><br /><li>It can updated the Comment entity based on a valid form</li><br /></ol><p>The Form object uses reflection and some exiting MVC helpers that also use reflection to achieve these functions.</p><p>The implementation details for the form object can be browsed in the download available at the top of this article.</p><h2>The html view layer</h2><p>The full html for the comment form is posted below. Its should be pretty straight forward for those familiar with .net and html.</p><p>For those keeping up to date with mvc you will notice a lack of the Html Helper methods, this is a personal preference. I like to have my html out in open for everyone to see, not hidden in some server method.<br /></p><blockquote><pre><br /><fieldset class="comment"><br /><legend>Add your comment</legend><br /><div class="inner"><br /><form id="addCommentForm" method="post" action="<%= Url.AddCommentUrl() %>"><br /><br /><br /> <ul id="error_list" class="error" <%= Html.RenderDisplayNone(this.ViewData.Model.IsValid) %> ><br /> <h6>There were errors!</h6><br /> <% foreach(var e in this.ViewData.Model.Validators){ %><br /> <li <%= Html.RenderDisplayNone(e.IsValid ?? true) %> class=" <%= e.ClientId %>" ><%= e.Message %></span></li><br /> <% } %><br /> </ul><br /> <div class="row"><br /> <label for="Comment_Name">Name:</label><br /> <input id="Comment_Name" name="Comment_Name" class="text" value="<%= this.ViewData.Model.Name %>" type="text" /><br /> <span class="error Comment_Name_Required" style="display: none" >« <%= this.ViewData.Model.NameRequired.Message %></span><br /> </div><br /><br /> <div class="row"><br /> <label for="Comment_EmailAddress">Email:</label><br /> <input id="Comment_EmailAddress" name="Comment_EmailAddress" value="<%= this.ViewData.Model.EmailAddress %>" type="text" /><br /> <span class="error Comment_EmailAddress_Required" style="display: none" >« <%= this.ViewData.Model.EmailAddressRequired.Message%></span><br /> <span class="error Comment_EmailAddress_Regex" style="display: none" >« <%= this.ViewData.Model.EmailAddressRegex.Message%></span><br /> </div><br /><br /> <div class="row"><br /> <label for="Comment_Comments">Comments:</label><br /> <textarea id="Comment_Comments" name="Comment_Comments" rows="10" cols="60"<br /> ><%= this.ViewData.Model.Comments %></textarea><span class="error Comment_Comments_Required" style="display: none" >« !</span><br /> </div><br /><br /> <div class="row button"><br /> <input value="Add Comment" type="submit" /><br /> </div><br /><br /> <val:ClientValidators FormId="addCommentForm" Form="<%# this.ViewData.Model %>" runat="server" /><br /></form><br /></div><br /></fieldset><br /></pre></blockquote><p>There are a couple of things to note here, firstly the ClientValidators server control a the bottom. This can be viewed in the download<br />but basically it initializes a bunch of javascript objects to help with client side validation (output of the control shown below). The script attaches the validators to the actual html form DOM object<br /></p><blockquote><pre><br /><script type="text/javascript">var form = document.getElementById('addCommentForm');<br />form.validators = new Validators();<br />form.validators.array[0] = new Validator('Please enter your name', 'validate_RequiredField', 'Comment_Name_Required', 'Comment_Name' , '');<br />form.validators.array[1] = new Validator('Please enter your email address', 'validate_RequiredField', 'Comment_EmailAddress_Required', 'Comment_EmailAddress' , '');<br />form.validators.array[2] = new Validator('Please enter your comments', 'validate_RequiredField', 'Comment_Comments_Required', 'Comment_Comments' , '');<br />form.validators.array[3] = new RegexValidator('Please check the format of your email address', 'Comment_EmailAddress_Regex', 'Comment_EmailAddress' , '', '..*@..*\...*');<br /></script><br /></pre></blockquote><p>Secondly, the html inputs are initialized from the form values, this allows an invalid value to be posted to the server and then redisplayed with an error message. (it allows us to keep invalid values during a round trip)<br /></p><p>Thirdly, there is a simple Html helper, to render "style="display: none" based on a boolean value.</p><p>JQuery is used to perform the client side validation. It makes use of the array of validators to perform client side validation. A portion of the jquery is show below.<br /></p><blockquote><pre><br />//when ready<br />$(document).ready(function(){<br /><br />//get every form in this document (1 in this case)<br />$("form").each(<br /> function()<br /> {<br /> //if validators exist<br /> if(this.validators)<br /> {<br /> //attach the validates to the inputs<br /> attachValidator(this.validators);<br /><br /> //attach efferts to the inputs<br /> attachValidatorEffects(this.validators);<br /> }<br /><br /> $(this).submit(function()<br /> {<br /> if(this.validators)<br /> {<br /> var isValid = this.validators.validate();<br /> var errorList = $('#error_list');<br /> errorList.hide();<br /> <br /> for(var i=0; i < this.validators.array.length; i++)<br /> {<br /> UpdateValidatorDisplay(this.validators.array[i]);<br /> }<br /> <br /> if(!isValid)<br /> errorList.fadeIn();<br /> else<br /> $(this).css("visibility","hidden");<br /> <br /> return isValid;<br /> }<br /> });<br /> });<br />});<br /><br /><br />function attachValidatorEffects(validators)<br />{<br />//loop through each validotor<br />for(var i=0; i < input =" $(validators.array[i].targetDom);" j="0;"><br /></pre></blockquote><p>I'll be the first to admit my javascript skills are lacking but I did get the job done. The form in action can be viewed here<br /><a href="http://www.xsation.net.au/Samples/MvcValidation/Comment.aspx">http://www.xsation.net.au/Samples/MvcValidation/Comment.aspx</a>.<br /></p>This is a rough start to validation using mvc and I am sure Scott and the team will come up with something far better. But for now, I hope it either helps you out a bit or provides some discussions points moving forward.<br /><br /><a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fxsation.blogspot.com%2f2008%2f06%2fmvcnet-preview-3-validation-server-and.html"><img alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fxsation.blogspot.com%2f2008%2f06%2fmvcnet-preview-3-validation-server-and.html&fgcolor=000099&bgcolor=FFFFCC" border="0" /></a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com7tag:blogger.com,1999:blog-3340352991227979961.post-7903194242074653972007-11-26T04:22:00.000-08:002007-11-26T05:04:11.879-08:00Forget Page Methods move on to Script Services<p>This entry is to guide you through the transition from the page method to the script service. Don't worry it won't hurt a bit...</p><p><a href="http://www.xsation.net.au/samples/HelloWorldScriptService.zip">Full Sample Code</a><p>Using page methods is a great idea for getting more performance out of your ajax apps but if they have a few draw backs</p><ol><li> They don’t promote reusability, user controls methods would but sadly they missed the boat.</li><br /><li>Getting them to actually work on a hosted server is somewhat of a mistory (see: <a href="http://www.west-wind.com/WebLog/posts/152493.aspx">http://www.west-wind.com/WebLog/posts/152493.aspx</a>)</li></ol><p>The best thing about page methods is that the converted me to using Web Services or Script Services. Script services are regular web services with a few extra attributes. The good thing is that you can have a javascript proxy built for you just like a page method. Better still, the web service is actually usable from anywhere...</p><p>The hello world of the script service.</p><p>Fire up visual studio and start a new Web Project (or web site if you must) .</p><p>Add a new Web Service</p><p>The Hello world template should be already present, as below .... (I changed the method name to ‘Hello’ so it’s different from the class name)</p><blockquote>/// <summary><br />/// Summary description for HelloWorld<br />/// </summary><br />[WebService(Namespace = "http://tempuri.org/")]<br />[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]<br />[ToolboxItem(false)]<br />public class HelloWorld : System.Web.Services.WebService<br />{<br /><br />[WebMethod]<br />public string Hello()<br />{<br /> return "Hello World";<br />}<br />}<br /><br /></blockquote>The add a reference to the System.Web.Extensions library and add the script service attribute to the class as below.<br /><blockquote>using System;<br />using System.Web.Services;<br />using System.Web.Services.Protocols;<br />using System.ComponentModel;<br /><br />using System.Web.Script.Services;<br /><br />namespace ScriptServiceExample<br />{<br />/// <summary><br />/// Summary description for HelloWorld<br />/// </summary><br />[WebService(Namespace = "http://tempuri.org/")]<br />[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]<br />[ToolboxItem(false)]<br />[ScriptService]<br />public class HelloWorld : System.Web.Services.WebService<br />{<br /><br />[WebMethod]<br />public string Hello()<br />{<br /> return "Hello World";<br />}<br />}<br />}<br /></blockquote>That’s it. Done. We have converted out web service into a script service. Now we will move onto the default.aspx file and consume our script service.<br /><p>Firstly check you web.confg has the necessary handlers defined.<br /></p><blockquote><httphandlers><br /><remove verb="*" path="*.asmx"><br /><add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"><br /><add verb="*" path="*.asmx" type="System.Web.Script.Services.ScriptHandlerFactory" validate="false"><br /></add><br /><br /></add></remove></httphandlers><br /></blockquote>Now, Add a ScriptManger to the page and define the script service as follows.<br /><blockquote><asp:scriptmanager id="ScriptManager1" runat="server"><br /><services><br /> <asp:servicereference path="/HelloWorld.asmx"><br /></asp:servicereference><br /></services><br /><br /></asp:scriptmanager></blockquote>The web page will compile and run at this stage but viewing a blank page less than impressive.<br /><p>If you got firebug or other means you can run the page and check that the following javascript is actually generated and linked to the page..<br /></p><blockquote>Type.registerNamespace('ScriptServiceExample');<br />ScriptServiceExample.HelloWorld=function() {<br />ScriptServiceExample.HelloWorld.initializeBase(this);<br />this._timeout = 0;<br />this._userContext = null;<br />this._succeeded = null;<br />this._failed = null;<br />}<br />ScriptServiceExample.HelloWorld.prototype={<br />Hello:function(succeededCallback, failedCallback, userContext) {<br />return this._invoke(ScriptServiceExample.HelloWorld.get_path(), 'Hello',false,{},succeededCallback,failedCallback,userContext); }}<br />ScriptServiceExample.HelloWorld.registerClass('ScriptServiceExample.HelloWorld',Sys.Net.WebServiceProxy);<br />ScriptServiceExample.HelloWorld._staticInstance = new ScriptServiceExample.HelloWorld();<br />ScriptServiceExample.HelloWorld.set_path = function(value) {<br />var e = Function._validateParams(arguments, [{name: 'path', type: String}]); if (e) throw e; ScriptServiceExample.HelloWorld._staticInstance._path = value; }<br />ScriptServiceExample.HelloWorld.get_path = function() { return ScriptServiceExample.HelloWorld._staticInstance._path; }<br />ScriptServiceExample.HelloWorld.set_timeout = function(value) { var e = Function._validateParams(arguments, [{name: 'timeout', type: Number}]); if (e) throw e; if (value < _timeout =" value;" get_timeout =" function()" set_defaultusercontext =" function(value)" _usercontext =" value;" get_defaultusercontext =" function()" set_defaultsucceededcallback =" function(value)" e =" Function._validateParams(arguments," _succeeded =" value;" get_defaultsucceededcallback =" function()" set_defaultfailedcallback =" function(value)" e =" Function._validateParams(arguments," _failed =" value;" get_defaultfailedcallback =" function()" hello=" function(onSuccess,onFailed,userContext)" href="http://www.blogger.com/post-edit.g?blogID=3340352991227979961&postID=790319424207465397">Ping the server<br /></blockquote>This will not work yet because the function is defined as “ScriptServiceExample.HelloWorld.Hello” but just to encapsulate this call I am going to add an helper javascript file called HelloWorldHelper.js<br /><p>The final html looks like this..<br /></p><blockquote><form id="form1" runat="server"><br /><br /><br /><div><br /><asp:scriptmanager id="ScriptManager1" runat="server"><br /><services><br /> <asp:servicereference path="/HelloWorld.asmx"><br /></asp:servicereference><br /></services><br /><br /><script src="helloWorldHelper.js" type="text/javascript"></script><br /><a href="javascript: Hello()">Ping the server</a><br /><br /></asp:scriptmanager><div id="processing" style="display: none;"><br /> fetching<br /></div><br /><div id="results"><br /></div><br /><br /></div><br /></form><br /></blockquote>NB: You should move the script include into the head of your document<br /><p>The javascript file looks like this..<br /></p><blockquote>function Hello()<br />{<br />$get('processing').style.display = 'block';<br />ScriptServiceExample.HelloWorld.Hello(SuccessCallBack);<br />}<br /><br />function SuccessCallBack(result)<br />{<br />$get('results').innerHTML = result;<br />$get('processing').style.display = 'none';<br />}<br /></blockquote><p>That’s it, consuming your script service couldn’t be easier.</p><p>Sending complex results to your web page is as easy as returning an object from your web service. The object will automatically be serilised using JSON (although you can specify XML if you want).</p><p>Quick code for returning an object.<br /></p><blockquote>[WebService(Namespace = "http://tempuri.org/")]<br />[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]<br />[ToolboxItem(false)]<br />[ScriptService]<br />public class HelloWorld : System.Web.Services.WebService<br />{<br /><br />[WebMethod]<br />public HelloWorldResponse Hello()<br />{<br /> HelloWorldResponse r = new HelloWorldResponse();<br /> r.Message = "Hello World";<br /> r.Time = DateTime.Now;<br /> return r;<br />}<br />}<br /><br />[Serializable]<br />public class HelloWorldResponse<br />{<br />public string Message;<br />public DateTime Time;<br />}<br /></blockquote><p>Comsuming the object, client side<br /></p><blockquote>function Hello()<br />{<br />$get('processing').style.display = 'block';<br />ScriptServiceExample.HelloWorld.Hello(SuccessCallBack);<br />}<br /><br />function SuccessCallBack(result)<br />{<br />var resultObject = eval(result)<br />$get('results').innerHTML = "The Message is:" + resultObject.Message + " The message was created on " + resultObject.Time;<br />$get('processing').style.display = 'none';<br />}<br /></blockquote><p><a href="http://asp.net/AJAX/Documentation/Live/tutorials/ASPNETAJAXWebServicesTutorials.aspx">Visit the offical site for more info</a></p><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com4tag:blogger.com,1999:blog-3340352991227979961.post-25516822513580589022007-11-06T17:56:00.000-08:002007-11-06T17:56:05.081-08:00Xsation.Net: Emailing: An alternate file upload method<a href="http://xsation.blogspot.com/2007/11/emailing-alternate-file-upload-method.html">Xsation.Net: Emailing: An alternate file upload method</a><div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com0tag:blogger.com,1999:blog-3340352991227979961.post-13221049046749264192007-11-06T17:19:00.000-08:002007-11-06T17:37:45.497-08:00Emailing: An alternate file upload methodOn a recent project there was a requirement for an easy upload method for files. We considered a few options such as drag and drop file upload components, ftp , file synchronization etc. However since the files arrived via email the simplest method (for the user) was to have the email and attachment forwarded onto a specified email address.<br /><br />When an email is delivered to the address, the asp.net website picks up the email and saves any attachments into a POP email inbox folder on the server. Then emails are then sorted and the attachments are moved to the required directory based on the business rules of the system.<br /><br />This is the first time I have used such an approached and has been a great success in terms of ease of use. The client is very familiar with forwarding emails and the process has become second nature.<br /><br />There are a few draw backs to be aware of when using this process, mainly concerning the slow nature of the POP mail servers in use. The mail takes a about 3 to 5 minutes to arrive on the site in the correct location. This is having the web site check the emails every minute.<br /><br />Technically we had to ensure that there is only 1 web client checking the email at once. In testing, we had many emails delivered, which causes the download of the email to take longer the 1 minute. After which another client started and because the email client would delete the email after downloading the contents this caused major problems. The solution was to stop the secondary clients launching if a client was already running.<br /><br />Apart from the above the solution is up and running well. It's definitely something to keep in mind next time you have to deal with file uploads to a website<div class="blogger-post-footer"><a href="www.xsation.net.au">www.xsation.net.au</a></div>Mark Kemperhttp://www.blogger.com/profile/15632388969636292370noreply@blogger.com1