Showing posts with label c#. Show all posts
Showing posts with label c#. Show all posts

Thursday, July 15, 2010

The Right Way to Version Your Assemblies

No, I'm not talking about whether you have Major, Minor, Patch, and Build numbers.  I'm not talking about when you increment which number.  My suggestion is to do whatever works for you in those cases, though I reserve the right to blog about that in the future.
What I am talking about is how you can structure your solution and project files in Visual Studio to make updating your assemblies' version number much easier.
This blog post makes some assumptions:
  • You are using version numbers in your assemblies
  • You are using a recent version of Visual Studio (I know these steps work with both Visual Studio 2008 and 2010)
  • You have multiple assemblies in one Visual Studio solution
  • You want to update all of your assemblies' version numbers at the same time and to the same version number
Those assumptions cover a vast majority of the projects I've worked on, but I'll admit that there may be projects out there that might not fit into the mold I've set forth.  I'm OK with that.  This blog post isn't for you if your mold is different.
So let's get started.
I created a solution file which contains several 1-OriginalSolutionExplorerprojects.  There's an MVC2 web application project, its associated test project, a business objects project, and a data access layer project.  These are all empty projects for this example—only the structure of the solution and projects is important for my purposes.  Solution explorer for this project is shown here.
The first step is to separate out the version information (and any other information that is common from assembly to assembly if you wish, like company name, product name, and so on) into its own file.  Open up one of the AssemblyInfo.cs files to see what I'm talking about.
2-OriginalAssemblyInfo
Notice that the settings are assembly attributes.  Some people don't realize that you don't have to put all the assembly attributes in one file.  I'm going to take advantage of that fact to accomplish what I set out to do: change the version in one place and have all projects updated at once.  The other key is using a solution-level file and linking to is from each project.
Visual Studio supports files at the solution level as well as at the project level.  You can put anything you want at the solution level and it will be carried along with the solution, though it won't get included in any output unless you write some custom build rules to grab the file and do something with it.  On some projects I've included a Word document about the solution and its projects as a solution level file.
For this example, we're going to create a new C# source file and include it at the solution level.  I'm going to call this new file, "VersionInfo.cs".
Step 1: Create a new file and call it "VersionInfo.cs".  Do this by choosing "File | New | File…" or press Ctrl-N in Visual Studio.  Choose "Visual C# Class" as the file type.
3-CreateVersionInfoCsStep 2: Erase everything in this file.  Save it as VersionInfo.cs in the same folder as the solution file.
Step 3: Highlight the version information lines from the AssemblyInfo.cs file from earlier, cut it from that file (Ctrl-X) and paste it in VersionInfo.cs (Ctrl-V).  Add "using System.Reflection;" to the top of VersionInfo.cs to avoid compilation errors.
4-SolutionItemsFolder Step 4: Add the VersionInfo.cs file to the solution by right-clicking on the solution file, choosing "Add | Existing Item…", then selecting "VersionInfo.cs" (find it in the solution's folder where you just saved it), and then click "Add".  You should see a new "Solution Items" folder under your solution name with the VersionInfo.cs file in it (see screenshot).
Step 5: Add the VersionInfo.cs file to each project as a linked file. See below for instructions on how to do this.
Step 6: Remove the version information from the other projects' AssemblyInfo.cs files.
At this point you should be able to build your solution successfully, and each assembly should have the same version information—the information found in VersionInfo.cs.
So what is a "Linked File" and how do I create one?  Read on.
Right-click on the project and choose "Add | Existing Item…", navigate up one folder level, and click once (don't double-click!) on the VersionInfo.cs file.  [This assumes that your solution folder is one level up from your project 5-LinkedFilefolder!]  Now instead of clicking "Add", click the little down-arrow next to the "Add" button.  Choose "Add As Link".  You should now see VersionInfo.cs in your project, but with a "shortcut" icon overlaid on top of the file's icon.
Once you have completed this step for each project, you can build your solution without errors.
Now to test it out.  Build the solution and look in the bin folder (or "folders" if you haven't added the other projects as references in the web project) for the DLLs.  Look at the version numbers.  They should all read "1.0.0.0".
Now change the version number in the VersionInfo.cs file information.  Rebuild the solution then check the version numbers.  All of the assemblies should now have the new version number.
6-DLLProperties Notice, however, that it is the "File Version" that you see when hovering over the DLL in Windows Explorer, so if you change only the "Assembly Version" the you'll have to go to the DLL's properties to see your change.
[I must admit, I don't recall which version number is used when putting DLLs into the Global Assembly Cache.  Please leave a comment if you have that information handy!]
So that is the procedure.  If you have an automated build system then you can make use of the fact that the version number is in its own file and update it automatically.  Then each build will produce DLLs with distinct version numbers.  That is a great way to identify the build that a DLL came from if you happen to get a report from the field that there is an error in a specific DLL.
But I'm sure that's never happened to you…right?
[Update: adding to ]

Friday, April 30, 2010

ReaderWriterLock FTW!

Recently I was encountering an issue on a SharePoint site where some site properties were getting cached in the HttpContext.Current.Cache.  The logic went roughly like this:
  1. Read values from a SharePoint list
  2. Put the values into the cache
I'm oversimplifying, of course, but those are the important steps as they relate to this blog post.
I had implemented proper locking, or so I thought, by using the C# lock statement.  The pattern I learned long ago when dealing with creating a shared cache that multiple threads can access is to do something like this:
  1. Try accessing the object.
  2. If you get it, great! Go forth and use it.
  3. If you don't get it, try obtaining a lock (i.e. you're about to enter a critical section).
  4. Once you obtain the lock, try accessing the object again, just in case another thread beat you to it.
  5. If you get it, great! Release the lock, go forth and use it.
  6. If you still don't get it, create it, then put it in the cache.
  7. Exit the critical section.
This pattern prevents unnecessary locking if the object already exists in the cache.  It also prevents the object from being created a second time.
As it turns out, the HttpContext.Current.Cache object isn't entirely thread-safe.  Even though I was locking and such, I was unable to retrieve the site properties I was creating.  To the second thread, it appeared that the properties were not in the cache, and the logic would get invoked to create it again.
That's when I did some research and discovered the System.Threading.ReaderWriterLock class.
This gem allows multiple threads to access an object, but only allows one thread at a time to write to that object.  The object has both a reader lock and a writer lock.  Many reader locks are granted as long as there are no writer locks granted (or pending).  If there is a writer lock that is being requested, it blocks until all the reader locks are released.
Another cool feature is that, once you have a reader lock, you can call "UpgradeToWriterLock" if you discover you need to write to the object.  Once you're done, you then "DowngradeFromWriterLock" and then "ReleaseReaderLock".  Or you can simply call "ReleaseLock", I believe, to release all locks obtained.
Best practice for using this object is to wrap it in a try…finally block of code. In the "try" part, acquire the lock and then do your stuff. In the "finally" part, release the lock.  In this manner you ensure that you always release the locks you acquire, and the system never gets "out of balance".
Here's how I used it to solve my problem:
  1. Get a reader lock (will block only if another thread is writing)
  2. Try to get the properties
  3. If obtained, exit and use the properties (the finally kicks in to release the lock)
  4. If not obtained, upgrade to a writer lock (will block if any other reader or writer locks are being held).
  5. Try to get the properties again (nested try block)
  6. If obtained, exit and use the properties (the finally kicks in to downgrade the lock, and then the outer finally kicks in to release the reader lock)
  7. If not obtained, create it and return it (the finally blocks kick in as described in step 6)
It's more complex than using a simple lock statement, but I found that it was necessary in this case.
One final implementation note: the ReaderWriterLock object was stored in a "private static readonly" member variable so that it was accessible from any method that needed it, and so that all threads were accessing the same object.

[Update: adding to ]