tag:blogger.com,1999:blog-78157506626265402502024-03-05T21:18:44.898-05:00SwearPoint"the point at which you are swearing at technology is when you have the most interesting things to share" - F. McGeoughStevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.comBlogger21125tag:blogger.com,1999:blog-7815750662626540250.post-39847313570966900292011-10-04T08:49:00.004-04:002011-10-04T08:56:36.362-04:00Happy Birthday to MeToday is my birthday.<div><br /></div><div>I was surprised when I brought up Google this morning and the "Google Doodle" showed a cupcake, a present, and ribbon. I thought, "What a coincidence. I wonder which famous person also has my birthday?" After all, Google is known for celebrating birthdays of celebrities.</div><div><br /></div><div>When I hovered over the doodle, I was taken aback! It said, "Happy Birthday Steve!"<br /></div><div><br /></div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxfdLdc4U6YWD9XCnJY1b_YcVIPGBZLXiN9hUQx5x-sdnxz4Nh_F0wNNuVHHZiF1KMzFTKyew0C8l9nCKPW0S6mZR-LC89p5p7l98Emru483ZwwtyUrIjL4c3Y6pTze0NC2fG4ISWs0AI/s1600/GoogleBirthday.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 229px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxfdLdc4U6YWD9XCnJY1b_YcVIPGBZLXiN9hUQx5x-sdnxz4Nh_F0wNNuVHHZiF1KMzFTKyew0C8l9nCKPW0S6mZR-LC89p5p7l98Emru483ZwwtyUrIjL4c3Y6pTze0NC2fG4ISWs0AI/s320/GoogleBirthday.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5659619352253023634" /></a><br /><div><br /></div><div>Google knew that it was my birthday and changed my Google homepage in celebration of me!</div><div><br /></div><div>I have no clue what you other less-important people are seeing on your Google homepages, but for me, I'll take my cupcake and present! :-)</div><div><br /></div><div>[By the way, if I click on the doodle I'm taken to my <a href="https://plus.google.com/103493431731656629266/posts">Google profile page</a>.]</div>Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-38905524781102278692010-07-15T08:21:00.001-04:002012-02-10T16:28:28.491-05:00The Right Way to Version Your AssembliesNo, I'm not talking about whether you have Major, Minor, Patch, and Build numbers. I'm not talking about <em>when </em>you increment <em>which</em> 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.<br />
What I <em>am</em> talking about is how you can structure your solution and project files in Visual Studio to make updating your assemblies' version number much easier.<br />
This blog post makes some assumptions:<br />
<ul>
<li>You are using version numbers in your assemblies </li>
<li>You are using a recent version of Visual Studio (I know these steps work with both Visual Studio 2008 and 2010) </li>
<li>You have multiple assemblies in one Visual Studio solution </li>
<li>You want to update all of your assemblies' version numbers at the same time and to the same version number </li>
</ul>
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.<br />
So let's get started.<br />
I created a solution file which contains several <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibjkW-wD7c8KlNvOErU_Z1sgG_GwuNXggKdGh8KXvskNdli49Hq728m-lJFm6XLVk7OxK7nHzzjjrVLQAGwod3ar5YfiOmcunN-OcsfxSlbaxrcyh8d-Ib0SEFMybyQXTzk6rTz9OQ1_c/s1600-h/1OriginalSolutionExplorer3.png"><img align="right" alt="1-OriginalSolutionExplorer" border="0" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPpRzuAqUDVf8WaHy3uL2znADM6h6lWPnrZBE6eU9rDVPyy-Q4kbRG7XETf1JCzIEqiuIYQuBzCiDCmsqJDvvcduiKte6LV25SlyTBxuTc84E3iyni5Y3B3JgNQMa-QvmeXNOQ3YN-ZqE/?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; margin-left: 0px; margin-right: 0px;" title="1-OriginalSolutionExplorer" width="122" /></a>projects. 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. <br />
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.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk_uh61LOeC_39_El3j7I2I9tYxAmBdYng48W-3gHZ2YgjjLM3itNT78WHwc6Jwy2-fGRHRaAiIFtY11vse_obEPpzkPrjXnsjAEnK459Awr73JH3DIlskbdCDtgHGZ4V8GjYQt6vLP0E/s1600-h/2OriginalAssemblyInfo4.png"><img alt="2-OriginalAssemblyInfo" border="0" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVera8sEoOFDtx8ngFUYu_2s0OD0yUDFjTjpTbINZ9jex7ehUWzTLp1zP5c_t0HxgR45wIJi8KNJBMDqLwlCRLncLbD2M5_hj6hhOqINisYWHC3wxP-ZxsuQED2fN_t1l8xzO5_mKYD_s/?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline;" title="2-OriginalAssemblyInfo" width="244" /></a><br />
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.<br />
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.<br />
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".<br />
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.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRzOuerOjL8VXleZuDrQxumf0UCBrK0pGRrVlfPe3blFPxdFVcVegmT_vAhRhOsZvFbQvNbt5lyE0g_cTkwUAjTfOafko535nSyBbdhZJUT93vklaqYhodJRcAc1J6Wqz2LHPUeamJrx0/s1600-h/3CreateVersionInfoCs4.png"><img align="right" alt="3-CreateVersionInfoCs" border="0" height="169" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNYwUonQ3IJuKvgwi6NO9D6TBMPk4xmftn4AnZv4SSSdcdczRnqQ4FVViUVUIwfop114tHII3Ds333kJDM-vJw9EQgBvqQBC6SS8BHYHumxwKo8Qzqg1MGhjFmJn8GCZXNrpOcZPuI7YQ/?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; margin-left: 0px; margin-right: 0px;" title="3-CreateVersionInfoCs" width="244" /></a>Step 2: Erase everything in this file. Save it as VersionInfo.cs <em>in the same folder as the solution file</em>.<br />
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.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuFFucVunhlxkv0_2eCQM2l-ZIJ6yvWsdzVyyP2XyKlJb9Z8TJmrMI2DROsdVPC5f7xqMp1nJZ_3DTwFU3TbMn83YOswd_SO8YsE2VIJ-IZrlWUBwETtQFU8OQlQvi_RxxhN2RKS19ZTo/s1600-h/4SolutionItemsFolder9.png"><img align="left" alt="4-SolutionItemsFolder" border="0" height="129" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_2dKLR3t81_f_32y8nLyoqjSgKVXzpWp7YRbM7pbpmikMIC-QKW4krwWbezM7y9vGuJK1DM-3tSm7RZaGDooI2Ich5Nj23moinGUlrBFj6Iu0CQW7Z4mP0xCbGB1h2mBjrpBzE5DXClg/?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; margin-left: 0px; margin-right: 0px;" title="4-SolutionItemsFolder" width="244" /></a> 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).<br />
Step 5: Add the VersionInfo.cs file to each project as a linked file. See below for instructions on how to do this.<br />
Step 6: Remove the version information from the other projects' AssemblyInfo.cs files.<br />
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.<br />
So what is a "Linked File" and how do I create one? Read on.<br />
Right-click on the project and choose "Add | Existing Item…", navigate up one folder level, and click <em>once</em> (don't double-click!) on the VersionInfo.cs file. [This assumes that your solution folder is one level up from your project <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF2hbSVVke8SvhMw79IJEH_FCllv5PcO9CcawStkr_Lz7Xm4FrRVXTWqSyu_1XpkFPCW6QqmfVhND05-ediQ6jeRu7FRxPccOf0Z7Om04dDqJyFBD4bvWV6B72NfVZwSaeBBG4nwwjGtM/s1600-h/5LinkedFile15.png"><img align="right" alt="5-LinkedFile" border="0" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg19ioHt7VYuINNWIc8XXkUuKw_-mx3eoAnwarbdkX5izRUbXKNoJiJklrYTEu68jGxarI8lj3ayucxHs14M5dr07GsKcc43qQK33L-4e22XRfRjXHZQ-krXaF4pwAr5aHBlUJqoT-H-MY/?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; margin-left: 0px; margin-right: 0px;" title="5-LinkedFile" width="196" /></a>folder!] 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.<br />
Once you have completed this step for each project, you can build your solution without errors.<br />
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".<br />
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.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhONl91-Y3pllVmjYJlqu0FAsro-0tjtFFjmYqLRt8jKcaAvUSHlDuAwl1obj0gVDk-nzjcAOj6C45uBCkYMIWMX49r7TCcpabhCYR4ebOZLeTG52Nx3wcl6elZGsjVVu2S2HM_nUBn6fE/s1600-h/6DLLProperties3.png"><img align="left" alt="6-DLLProperties" border="0" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi87f5byIPoFxJxRutKjydxUJJmFAJ2srMAqvZ0Fzwx2iv2c8Amw7aSaluJYKPPeXJPPIwpPtCXl6bZtHt8HaFkpmeBF024K70KZXC34IDvfaInyxb27RA3FUpL2yVCSkj6-I1nEwZ00Cc/?imgmax=800" style="border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; margin-left: 0px; margin-right: 0px;" title="6-DLLProperties" width="191" /></a> 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.<br />
<span style="font-size: xx-small;">[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!]</span><br />
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.<br />
But I'm sure that's never happened to you…right?<br />
<i>[Update: adding to <a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=8636865" rel="tag">CodeProject</a>]</i>Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-25620698226812389472010-05-03T18:28:00.001-04:002010-05-03T18:28:41.059-04:00Completely Empty the SharePoint Recycle Bin<p>If you have <b><i>LOTS</i></b> of stuff in your SharePoint Recycle Bin and want to empty it all, here’s a quick tip:</p> <p>1. Navigate to the recycle bin page (e.g. <a href="http://localhost/_layouts/recyclebin.aspx">http://localhost/_layouts/recyclebin.aspx</a>)</p> <p>2. In the address bar, type “javascript:emptyItems()” (minus the quotes)</p> <p>3. Press enter. You will be asked if you really want to empty the recycle bin.</p> <p>4. Click OK to empty it; click Cancel to leave it alone.</p> <p>It’s <b><i>much</i></b> faster to empty it this way if you have multiple pages of “stuff” in your recycle bin.</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-5949747433451066412010-04-30T12:00:00.000-04:002012-02-20T10:13:02.484-05:00ReaderWriterLock 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:<br />
<ol>
<li>Read values from a SharePoint list</li>
<li>Put the values into the cache</li>
</ol>
I'm oversimplifying, of course, but those are the important steps as they relate to this blog post.<br />
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:<br />
<ol>
<li>Try accessing the object.</li>
<li>If you get it, great! Go forth and use it.</li>
<li>If you don't get it, try obtaining a lock (i.e. you're about to enter a critical section).</li>
<li>Once you obtain the lock, try accessing the object again, just in case another thread beat you to it.</li>
<li>If you get it, great! Release the lock, go forth and use it.</li>
<li>If you still don't get it, create it, then put it in the cache.</li>
<li>Exit the critical section.</li>
</ol>
This pattern prevents unnecessary locking if the object already exists in the cache. It also prevents the object from being created a second time.<br />
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.<br />
That's when I did some research and discovered the System.Threading.ReaderWriterLock class.<br />
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.<br />
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.<br />
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".<br />
Here's how I used it to solve my problem:<br />
<ol>
<li>Get a reader lock (will block only if another thread is writing)</li>
<li>Try to get the properties</li>
<li>If obtained, exit and use the properties (the finally kicks in to release the lock)</li>
<li>If not obtained, upgrade to a writer lock (will block if any other reader or writer locks are being held).</li>
<li>Try to get the properties again (nested try block)</li>
<li>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)</li>
<li>If not obtained, create it and return it (the finally blocks kick in as described in step 6)</li>
</ol>
It's more complex than using a simple lock statement, but I found that it was necessary in this case.<br />
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.<br />
<br />
<i>[Update: adding to <a href="http://www.codeproject.com/script/Articles/BlogFeedList.aspx?amid=8636865" rel="tag">CodeProject</a>]</i>Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-185886970908150292010-01-27T14:02:00.001-05:002010-01-27T14:02:50.562-05:00Error While Refreshing External Data Source in Excel<p>I have an Excel 2007 workbook that pulls its data from an Access 2007 database to give users some feedback via charts and graphs.  [For this discussion it doesn't matter that Access actually gets the data via links to lists on a SharePoint site—the error I encountered deals with the connection between Excel and Access.]</p> <p>I added an "Update" button that the user presses to ensure that all of the data is freshly pulled from Access.  Initially I just called</p> <blockquote> <p>ThisWorkbook.RefreshAll</p> </blockquote> <p>However, I received an error at some point that I couldn't pinpoint.  I have about 150 different queries that pull data from Access, and trying to find the one query that was failing was like finding the proverbial needle in a haystack.</p> <p>So I changed the code to loop though all of the named ranges in the workbook.  I had given all of the data tables names when developing the spreadsheet, and so the new code looks like:</p> <blockquote> <p>for each name in ThisWorkbook.Names <br />   name.RefersToRange.ListObject.QueryTable.Refresh <br />next</p> </blockquote> <p>[Yes, I check for whether the name object actually refers to a range, and if so, whether that range has a List Object, and if so, whether that List Object has a Query Table to refresh!]</p> <p>This way I could tell which range was being refreshed and could inform the developer (me) of which range was causing the problem. I could also calculate a percent complete value to display on the status bar, since I knew how many names there were and which name I was currently working on (via a "currentName" variable).</p> <p>This looked promising, but I again got an error.  Looking at the named range that the code was refreshing when the error occurred, I didn't see anything wrong with it.  I could manually refresh it with no problem.</p> <p>So I started looking at the properties of the QueryTable object to see if there were any clues in there.  That's when I stumbled upon the "MaintainConnection" property.</p> <p>I changed the code to this, to see if it made any difference:</p> <blockquote> <p>for each n in ThisWorkbook.Names <br />n.RefersToRange.ListObject.QueryTable.MaintainConnection = False <br />n.RefersToRange.ListObject.QueryTable.Refresh <br />next</p> </blockquote> <p>This time when I ran it, it ran to completion without any errors.</p> <p>So it seems that if you try to maintain your connection to the database, you'll eventually run out of available connections.  What's strange is that the searches I did came up with information about Excel handling up to 255 connections, and I seemed to bump into this problem at about connection number 60 or so.  I'm not sure of the exact count—it could have been more or less than 60, but it was pretty close, I believe.</p> <p>So if you're attempting this on your own, take it from me: set the MaintainConnection property to False!</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-20534920709790408272010-01-14T18:16:00.001-05:002010-01-14T18:16:12.117-05:00Don't Forget to "DoEvents"<p>I'm writing a reporting package which uses Microsoft Access and Microsoft Excel to gather and chart data collected from a set of custom SharePoint lists.  I have some VBA code which does some calculations on all of the list items before the reports are generated to ensure that the data is the most up-to-date that it can be.</p> <p>The VBA code lives in Access.  It basically does a lookup on each row in one of my lists and sets values in another list.  The business rules for this update had enough conditions on it that I couldn't implement it in one complex Update statement, thus the looping.</p> <p>I also discovered that you can very easily add a progress bar to the Access status bar by calling "SysCmd acSysCmdInitMeter, 'message', maximum value" to initialize it, and "SysCmd acSysCmdUpdateMeter, currentValue" to increment the counter.  (To reset the status bar when I'm done, I call "SysCmd acSysCmdRemoveMeter".)  So I added this bit of functionality to the VBA code as a bonus for my users to give them some feedback.  After all, some of the lists I'm working with have a few thousand items in them (don't get me started about best practices stating that 2000 items is the workable maximum—I've already fought that battle and lost!) and it can take some time for the VBA code to complete.</p> <p>Well wouldn't you know it, but the update code continually ran, and the thread blocked on the SQL command, so the progress bar just sat there, no progressing.  Furthermore, the whole UI appeared to be locked up while the update was running.  I couldn't even pause execution of the VBA code because the IDE wouldn't recognize my mouse clicks on the pause button.</p> <p>And then I remembered something from my old VB6 days: the DoEvents method.</p> <p>The DoEvents method relinquishes control of the processor by the VBA engine to allow other processes to do things. Or more accurately in my case, other threads. It was more important to call it when a background thread was doing heavy processing and you wanted to allow the UI thread a chance to handle the mouse clicks, keyboard presses, and other events which had queued up since the last time it got a time slice from the operating system.</p> <p>I remembered one main warning about using it, though:</p> <ul> <li>You don't want to call it too often, since an expensive context switch most likely would take place</li> </ul> <p>So I decided to see if it worked in VBA by adding this block of code:</p> <p>If currentRow Mod 10 = 0 Then <br />    DoEvents <br />End If</p> <p>In this block of code, currentRow is an integer which gets incremented each time through the loop. It is the variable that I pass to the method which updates the progress bar. You can see that I don't call DoEvents each time through the loop—I want the main processing code to actually finish in a reasonable amount of time, after all!</p> <p>The IDE didn't complain about the method call, so it at least recognized it as valid VBA code.  So far, so good. But will it accomplish what I want it to?</p> <p>The short answer: YES!</p> <p>So now, in the latest version of the code, the DoEvents method is called every 10 rows.  This allows the UI to get refreshed, so I can now pause execution and scroll through the source code (albeit in bursts).</p> <p>But more importantly, the progress bar now shows progress as the code runs!</p> <p>DoEvents FTW!</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-24632790246941911382010-01-08T12:00:00.000-05:002010-01-08T12:00:03.487-05:00SharePoint Exception Occurred 0x80020009 (DISP_E_EXCEPTION) – UPDATE<p>A colleague pointed me to the following article: <a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;970946&sd=rss&spid=12200" target="_blank">Description of the Windows SharePoint Services 3.0 Cumulative Update Server Hotfix Package (Sts.msp): June 30, 2009</a>.  In it, it makes reference to the DISP_E_EXCEPTION error.  Specifically, it states:</p> <blockquote> <p>SPList.GetItems(SPQuery) fails when the item count reaches the threshold value 2000, and you will receive the following error message: </p> <p>Exception occurred. (Exception from HRESULT: 0x80020009 (DISP_E_EXCEPTION))</p> </blockquote> <p>Presumably, if you have a custom query on a list, and that query returns more than 2000 items (which is the case on the pages where I'm encountering this error), you'll get this exception.  The fix for this, supposedly, is in the June 30, 2009 hotfix package.</p> <p>I'm in the process of requesting that this hotfix be applied to our SharePoint server.  I'll post again once that happens and let you all know whether it fixes this problem as advertised.</p> <p>While searching for possible causes for this exception, the blog posts I found mentioned improperly-configured load balancers and other maladies, but not this 2000-item limitation nor the hotfix.  Hopefully this post will help someone else out there who is having a similar problem on their system.  Please comment if that person is you!</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-51823847378091367712010-01-07T12:44:00.001-05:002010-01-07T12:44:31.054-05:00Best Practices for Building SharePoint Solutions<p>A friend forwarded this link to me: <a href="http://msdn.microsoft.com/en-us/magazine/dd458798.aspx" target="_blank">10 Best Practices For Building SharePoint Solutions</a>.  Most are fairly obvious, but sometimes it's good to state the obvious things, lest we forget them.</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-27115689997880830682010-01-04T12:00:00.000-05:002010-01-04T12:00:01.806-05:00Performing Outer Joins on a SharePoint List from Access<p>I'm currently creating reports from the custom SharePoint lists I have been working on over the past few months.  The way I'm accessing the data in my SharePoint lists is through Access 2007, and then I have an Excel 2007 spreadsheet which uses the Access database to get the data. In this manner, I can use the power of Access' database engine to perform queries against my lists, and then just import the data to Excel.  It sounds cumbersome, and perhaps I'm showing some of my ignorance of Access and Excel development, but it runs much faster than the earlier version of the report which uses VBA only to generate similar reports.</p> <p>That being said, I ran into an issue with one query.  I have a list which tracks FCC filings.  There can be a major filing and a minor filing, both tied to the same physical location.  So there can be one or two filings (items) per location stored in this list.  I needed to report on whether the filings were "complete".  They are considered complete if the major filing is complete, or if the minor filing is complete in the case where a location does not have a major filing.</p> <p>My SQL statement was structured like this:</p> <blockquote> <p>SELECT * FROM <br />( <br />    SELECT <<completed major filings>> <br />    FROM Filings <br />    WHERE Filings.Type = 'Major' <br />UNION <br />    SELECT <<completed minor filings>> <br />    FROM Filings Minor <br />    LEFT JOIN Filings Major ON Minor.Location = Major.Location <br />    WHERE Minor.Type = 'Minor' <br />    AND Major.Type = 'Major' <br />)</p> </blockquote> <p>However, this didn't work.  The second part of the query kept behaving like an inner join.  I did some proofs-of-concept to discover that, yes, outer joins <strong><em>do</em></strong> work in Access on actual tables within Access, but they just weren't working on linked tables where the source is SharePoint.</p> <p>What to do?</p> <p>After beating my head against the wall for a while, and possibly pulling some hair out, I finally had the bright idea to break things into smaller chunks.  Thinking that Access had a problem joining a linked table with itself, I created an Access query which only returned items representing minor filings, and another Access query which only returned items representing major filings.  Then I removed the where clauses from my original SQL statement above and replaced direct access to the linked table with the queries I had created.</p> <p>It worked beautifully!</p> <p>The new SQL Statement looks like this:</p> <blockquote> <p>SELECT * FROM <br />( <br />    SELECT <<completed major filings>> <br />    FROM AllMajorFilings <br />UNION <br />    SELECT <<completed minor filings>> <br />    FROM AllMinorFilings Minor <br />    LEFT JOIN AllMajorFilings Major ON Minor.Location = Major.Location <br />)</p> </blockquote> <p>Lesson learned: when working with Access, break things into smaller components to avoid confusing Access.</p> <p>I can't wait to get back to "normal" C# development!</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-29362966131964677032009-12-21T12:00:00.000-05:002009-12-21T12:00:01.496-05:00Slow Going<p>As you know, it's the end of the year.  Holidays are only days away.  So is vacation time.  I'm frantically finishing up 2009 work so that I can take some time off and return fresh in the new year.</p> <p>So here's to a good start in 2009 on this blog.  I'm aiming to make 2010 even better.  Perhaps I'll even get a new URL to boot.</p> <p>I have lots of things planned, just need to implement the plan now.  I'll be posting my learning experiences here.</p> <p>As always, I look forward to your feedback.</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-11124177419113422772009-12-18T12:00:00.001-05:002009-12-21T11:43:49.541-05:00Strange Column Names<p>In SharePoint 2007, you <em>can</em> have a column named "!".</p> <p>I'm sure there are other characters you can use, as well, but this one surprised me. I suppose it is because I'm a C# developer…the "!" (or "bang") is an operator, not an identifier.</p> <p>But in SharePoint (at least the 2007 version) it is a valid column name.</p><p><span style="font-weight: bold; color: rgb(102, 102, 102);font-size:85%;" >[Update: It appears that the column named "!" appears in SharePoint Designer as "_". I haven't tried including this column in any of my workflows, so I'm not sure how it would behave. If you have the opportunity to do so, please leave a note letting me know how it works for you. -sj]</span><br /></p>Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-37626966997191067922009-12-17T12:00:00.000-05:002009-12-17T12:00:00.969-05:00SharePoint “Lookup” Columns<p>As you may know, you can include a “Lookup” column in your SharePoint 2007 lists which, in effect, creates a reference to another SharePoint list.  This can be used, for example, if you have an address list and want to ensure that your states or provinces are all spelled correctly—just create a “StatesProvinces” list, fill it with the properly-named states and provinces, then add a reference to it in your address list. The user experience is a combo box containing the items in the “StatesProvinces” list, and they can select the one they want without having to type it themselves. [Note that you can configure SharePoint to allow multiple items from the referenced list to be selected, but that’s not what I’m focusing on in this post.]</p> <p>In order to add a lookup column to your list, you first must have created the other list that you are going to reference.  You don’t actually need data in it, yet, but it does need to be created.  Fair enough.</p> <p>Next, after choosing the list, you must choose which column in that other list you want to display to the user.  So in this example, you may want to have the “Title” column display to the user.</p> <p>Once you save everything and add data to the “StatesProvinces” list, you have a workable solution.</p> <p>But what if you want to have a workflow run against this address list which has the reference to the “StatesProvinces” list?  This is where you need to know a little bit about the inner workings of lookup columns in SharePoint.</p> <p>If you know anything about database development, you probably know about the concept of a “foreign key”. In short, a foreign key resides in a table that is related to another table.  Let’s call the first table the “Child” table, and the second table the “Parent” table.  So the Child table would hold the foreign key to the Parent table.</p> <p>What does a foreign key look like? Simple: it is the primary key of the Parent. [A primary key uniquely identifies a row in the database table.] It is usually a numeric type.</p> <p>The same concept applies to a lookup column in SharePoint, but the execution is slightly different. You see, the actual <em>value</em> stored in the lookup column is the ID of the referenced list, but what is <em>displayed</em> to the user is the configured column from that list.</p> <p>Back to my workflow scenario: if you want to manipulate lookup columns from a SharePoint workflow, and I’m talking specifically about using SharePoint Designer to create the workflows, you need to keep the foreign key concept in mind. As a test of this, do the following:</p> <ol> <li>Create a workflow and attach it to a SharePoint list which contains a lookup column.</li> <li>Create two variables in the workflow, one of type “String” and one of type “List Item ID”.</li> <li>Set both variables to the value of the lookup column</li> </ol> <p>If you examine the contents of each variable (by logging it to the workflow history) you will see that the String variable contains the value of the configured column to display (in our example, the “Title” column), and the List Item ID variable would have a number (which corresponds to the ID column of the referenced list).</p> <p><strong>If you are going to change the value in the Lookup column via a workflow, you must set its List Item ID (number), not the displayed value(text).</strong>  You will wonder why your workflow isn’t working correctly if you don’t follow this advice, especially if you are new to creating SharePoint workflows using SharePoint Designer.</p> <p>I hope I have helped someone out there with this advice.</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-6149618921387435932009-12-16T12:00:00.000-05:002009-12-16T12:00:03.854-05:00Mentoring<p><img style="display: inline" height="284" src="http://www.freeclipartnow.com/d/19290-1/apple-chalkboard.jpg" width="235" align="right" /> I got my start in programming from typing lines of BASIC in the backs of magazines into my Commodore 64.  I didn’t really understand what each line did at first, but I dutifully typed in the lines, just to see the finished product in action.</p> <p>I got my <em>professional</em> start in programming when I was a student at Georgia Tech.  When I enrolled at Tech, I indicated that I wanted to be part of the cooperative education plan, which meant that I would attend classes for one quarter, then work at a  company for one quarter, then repeat.  It took me a little over a year to find a company that would hire me as a “co-op student”, but once I did I stayed with that company until I graduated from college.  In fact, that company hired me when I graduated, and I went to work for them full-time.</p> <p>But while I was still a co-op student, I often felt that I learned more while I was on the job rather than when I was in school.  I think the reason for this is that I was very fortunate to have found a job where there were many people eager to share their knowledge of programming with me.</p> <p>In a nutshell, I was surrounded by mentors.</p> <p>I have very fond memories of those days—people smarter than myself showing me how things got done in the “real world”.  As a result, I often take the time to teach others things that I have learned, now that I’ve been a professional developer for nearly 20 years.</p> <p>I guess that’s where this blog is coming from.</p> <p>In my current role as a consultant, my coworkers are all very knowledgeable about software development, so I don’t often get to mentor them.  But on the rare occasion that I get to explain something, or show something that I know, it really gives me a sense of satisfaction when I can see that I’ve made a difference—when the other person has that “a-ha” moment and has learned something.</p> <p>I think we can all learn from each other, and I think that there is great value in continually learning new things.  My company doesn’t currently have a mentoring program, but I’m considering suggesting that they start one.</p> <p>What about your company?  Do you have a mentoring program of sorts?  If so, what’s it like?  Can you quantify the value of it?  Is it company-supported?</p> <p>I’d be willing to collaborate with you (Google Wave, anyone?) on a “Mentoring Charter” of sorts if you want to start a similar program at your company.</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-52773350006724308992009-12-15T12:00:00.000-05:002009-12-15T12:00:03.675-05:00What Do You Want From Me, Anyway?<p>I started this blog a week or so ago, and I have filled the first few entries with mainly SharePoint-related information.  Mostly from my current project.</p> <p>But I know lots more than just SharePoint.</p> <p>I can write about patterns.  .NET, C# in particular.  Middle-tier development.  Consulting.  Project management.</p> <p>But I don’t want to just spout things out without some feedback from you, my dear reader.  [And, yes, at this point <em>I</em> may be the only reader!]</p> <p>Please leave me a comment to let me know what you’re interested in reading about.  I know that this blog will take shape over time, and will most likely track what I’ve been working on, but there’s nothing stopping me from deviating from that pattern and posting something that you’re actually interested in.</p> <p>Like how I got laid off twice within one year and each time landed a new job within 1 1/2 weeks.</p> <p>Or perhaps you’d like to know how to make money by blogging?  [Sorry, but I haven’t figured that one out, yet.]</p> <p>In any case, do leave a comment, no matter <em>when you are reading this.</em>  Meaning, if the year is now 2012 and you’ve stumbled upon this post because you’re preparing for the world to end on December 21st, please leave me a message.  Note that I don’t think it will end, but the <a href="http://www.2012truth.org/crustal-displacement" target="_blank">crustal displacement theory</a> sounds plausible.</p> <p>You won’t find what you’re looking for here unless you ask!  So ask away!</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-66201614227775985272009-12-14T12:00:00.000-05:002009-12-14T12:00:02.028-05:00Let Me Introduce Myself<p>I’ve been considering starting a technical blog for some time—the technical posts on my personal blog just felt out of place.</p> <p>And so “SwearPoint” was formed.</p> <p>My plan for this blog is to share some knowledge I’ve gained about developing software professionally for over 20 years.  I am a web developer who got his start way back in the ‘80s on my Commodore 64.  I actually still have that computer down in the basement.  Haven’t turned it on it several years.  Perhaps a decade or more!  Not sure if it still works, even…</p> <p>I would spend hours typing in BASIC programs in the back of magazines such as <em>Home Computing</em> (at least I <em>think</em> that’s the name…it’s been quite a while!), only to find that they didn’t work.  Then the next month they would publish the corrections to the code. Anyone else remember doing that?</p> <p>I didn’t necessarily learn “good programming skills”, but I at least fostered my love for programming.</p> <p>After graduating from high school, I attended the Georgia Institute of Technology, lovingly referred to as Georgia Tech.  I pursued a bachelor’s degree in Information and Computer Science, which I obtained in 1992.  During that time, I was involved with the cooperative education plan, in which I would attend classes for a  quarter and then go to work at a company for a quarter.  In hind sight, this was a very smart thing to do.  Not only did it introduce me to what working at a company is like, but I met many people who had a profound influence on my professional life.</p> <p>Plus, that was the only job offer I received when graduating from college.  If you’ll recall, that was right before the .com boom, and programming jobs weren’t quite as easy to come by, so I felt blessed to have landed a job at that time.</p> <p>Fast forward a few years.  One of the managers at that company contacted me a couple years after I left. He told me that he was starting a company and asked if I would be interested in working for him again.  I said, “Yes”, and thus started my 5 years at an upstart company (or “start-up”, if you prefer).  We weren’t exactly doing “web stuff”, which was the big focus at the time (1995), but we had good funding, and I had the chance to work with some very smart people again (most of the staff came from my first job).</p> <p>Since then, I have mainly been a consultant working with Microsoft technologies such as (you guessed it) SharePoint.  I’ve also worked with BizTalk Server, Content Management Server (anyone remember that one?), and SQL Server.  I am quite proficient with .NET and the C# language (and to a lesser degree VB.NET).  My current passion is Silverlight 3.  I attended a “Firestarter Event” back in August for Silverlight 3, and there was definitely a fire started.</p> <p>I’m only working on Silverlight in my spare time, thus the lack of blog posts about it so far.  Most of my time these days are spent dealing with SharePoint.</p> <p>I foresee this blog being a reflection of my current experiences mostly, with occasional forays into software development practices in general, consulting tips, and possibly some plain ol’ nostalgia (especially after I get that Commodore 64 hooked up again—I’ll be interested to see if any of the 5 1/4” floppies are still readable).</p> <p>So there we are—that’s my introduction.  If you happen across this post, please let me a comment.  Please click the advertising links!  :-)  After all, I have 4 kids to feed (and to put through college some day!).</p> <p>Thank you for reading.</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com2tag:blogger.com,1999:blog-7815750662626540250.post-23873526594765282642009-12-11T12:00:00.002-05:002009-12-11T12:00:00.309-05:00SharePoint Exception Occurred 0x80020009 (DISP_E_EXCEPTION)<p>My, oh my, will wonders never cease?</p> <p>Have I mentioned how much I love SharePoint recently? Let me count the ways. And believe me, there are not 0x80020009 ways. In fact, I can count the ways on one hand. Sometimes no hands! :-)</p> <p>For some reason, one of my SharePoint lists will not display in the customized Datasheet view that I created. The others custom Datasheet views on my other lists are working perfectly.</p> <p>So I “Googled with Bing” for a solution and found that, lo and behold, I am not alone in the universe. Many, many more people share my woes with this error.</p> <p>Some people mention that all that needs to happen is an IIS reset. Well that won’t work for me, since I’m at a very large corporation and I don’t know who the admins are nor do I even know <em>where</em> they are.</p> <p>Some people mentioned problems with their custom web parts. No, no web parts here. The SharePoint site I’m working on does not allow custom code to be deployed to it. All I have to work with is SharePoint Designer.</p> <p>Other people mentioned that it was the load balancer which was not configured properly.</p> <p>Are you kidding me? One error has so many different causes? Do you think someone on the SharePoint team would think to provide us, the lowly end-users, with more of an explanation of where to look for the problem? (Before you mention reading log files or anything similar, keep in mind what I mentioned above: I don’t have access to the servers. I don’t even know where they are!)</p> <p>Nice. Even though I initially found comfort in knowing that other people have encountered this error and have, in fact, found solutions, it seems that I may be all alone in this particular case.</p> <p>I tried re-creating the view, just in case something mucked it up. You know, like those stray gamma rays that knock various electrons out of their paths inside your PC’s CPU which cause programs to run amuck. As you probably guessed, it didn’t work.</p> <p>In fact, as of this writing, I still do not have a solution for this problem. I apologize if you have read this far expecting a solution. Or perhaps my prose caused you to skip ahead, which is good for you. The prose was good for me—it allowed me to vent some steam.</p> <p>[I ended up deleting all items from the list, and the view started working. Or at least I no longer get the exception. Of course, there were no items in the list for it to display! Once I loaded it with data again, I started getting the same error. Other similar views don't give me this error. Perhaps it's a data issue.]</p>Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-74281341150188578522009-12-10T12:00:00.000-05:002009-12-10T12:00:01.616-05:00How Can You Tell if You Found a Valid List ID?<p>I’m currently working with several different custom SharePoint lists.  Some of the lists have references to other lists, via a “Lookup” column.  For example, a book might have a reference back to the library it came from.</p> <p>Sometimes I only know the name of the library, and I want to be able to set a reference to it on the book’s list.  So I retrieve the library’s ID using the name of the library.  SharePoint Designer dutifully warns me that I’m not looking up the value using a unique ID, and that it will just give me the first one it finds.  I’m OK with that, since I control the names and I know that they’re all unique.</p> <p>But I’m only human, and sometimes I don’t have a library name that exists in my library list.</p> <p>In those cases, when I log the contents of the list ID variable into which I placed the data, it shows me “0”.  [That’s the number zero, not to be confused with a lower-case “o”.  I’m considering changing the font for this blog… -sj]</p> <p>As it turns out, this is the way you can tell if you have a valid list ID.  Valid IDs start at 1, I presume, and invalid IDs are all zero.</p> <p>[It’s easy to tell if you have a bad reference to another list when you retrieve string values, since they show up in log messages as “????”.]</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-407338485386101862009-12-09T12:00:00.000-05:002009-12-09T12:00:09.126-05:00Workflow Comparisons Aren’t Always Accurate<p>I have a SharePoint workflow, developed with SharePoint Designer, which is supposed to compare values in the list it is attached to against values in another list, and copy the other list’s values if they are different.  I kept noticing, via a workflow history log message, that the workflow was copying values every time it ran, even though in another log message for the same workflow instance it showed me that all the values were the same.</p> <p>As it turned out, I was comparing a string value to a list ID.  Even though they both printed in the log message exactly the same (and so were “equal” in my eyes), SharePoint saw them as different values.</p> <p>The fix was to copy the list ID value to a string variable, and then compare the two values as strings.</p> <p>[As a side note, what made this worse was that I had a second workflow which would run when anything was changed on the list.  It would then update a value in the list, which would then cause the buggy workflow described above to run again.  For the astute readers, this meant that the two workflows attached to the same list would cause each other to run infinitely.  It pretty much brought the site to a halt!]</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-43028028373425358472009-12-08T12:00:00.000-05:002009-12-08T12:00:03.644-05:00Creating a Custom SharePoint Permission Level<p>In yesterday’s post, I mentioned custom SharePoint permission levels.  Here is how to create one.</p> <p>From the landing page of your site (default.aspx), click on “People and Groups”, then click on “Site Permissions” under “All People”. Click on the “Settings” menu, then select “Permission Levels”.</p> <p>Now click “Add a Permission Level”.  Give your custom permission level a name, an optional description (I find it useful to describe the permissions that this permission level grants, and possibly which assumed permissions that this level <em>does not</em> grant).</p> <p>Now is the fun part.  You get to choose which permissions to grant, and which to exclude, for this permission level.  There are many different permissions you can choose from.  They are described in this <a href="http://office.microsoft.com/en-us/help/HA101001491033.aspx" target="_blank">Office Online page</a>.</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-29961049203311922282009-12-07T12:00:00.001-05:002009-12-07T19:10:29.823-05:00SharePoint Permission Tip<p>If you use the “Collect Information from User” action in a SharePoint Designer workflow, and you have your default site permissions set only to “read” for the user you’re planning on collecting information from, you probably won’t get very far.</p> <p>You’ll want to grant at least “Edit” permission to the user (or group that the user is in—that would be a better practice) on the Task list.  This allows the user to access the task that gets assigned to her so that she can complete her work.</p> <p>Here’s how to accomplish this:</p> <ol> <li>Click on the “Tasks” list on the Quick Launch pane if it is available.  Otherwise, click on “Lists” first, and then click on the “Tasks” list.</li> <li>Click on “Settings | List Settings”</li> <li>Click on “Permissions for this list”</li> <li>Click on “Actions | Edit Permissions”</li> <li>Click “OK” on the resulting dialog box (“You are about to create unique permissions for this list…”)</li> </ol> <p>Now you can modify the permissions for the users and groups listed.  If you don’t see the users or groups you want to configure for the Tasks list, you can add them here.</p> <p>I must note that I created more granular permission levels on the site I’m currently working on, so I can choose to grant “Read” access or “Edit” access individually.  If you only have the out of the box permission levels, you’ll want to ensure that your users have at least “Contribute” access to the list. Of course, if you only have the out-of-the-box permission levels and you haven’t created custom levels then you wouldn’t have the problem I had which prompted me to write this post.</p> <p>I’ll cover creating custom permission levels in a future post.</p> <div class="wlWriterEditableSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:e9123177-4d7d-416d-bf44-1cc0136ed850" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">del.icio.us Tags: <a href="http://del.icio.us/popular/sharepoint" rel="tag">sharepoint</a>,<a href="http://del.icio.us/popular/tip" rel="tag">tip</a>,<a href="http://del.icio.us/popular/permission+level" rel="tag">permission level</a>,<a href="http://del.icio.us/popular/tasks" rel="tag">tasks</a></div>Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0tag:blogger.com,1999:blog-7815750662626540250.post-90837847082206190822009-12-04T16:26:00.001-05:002009-12-04T16:26:11.714-05:00SharePoint? No, SwearPoint!<p>[Cross posted from <a href="http://thejacksonfamily.blogspot.com/2009/12/sharepoint-no-swearpoint.html">my personal blog</a>. -sj]</p> <div class="wlWriterEditableSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:a6653682-848d-4998-952f-ab23eb9dbe15" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">del.icio.us Tags: <a href="http://del.icio.us/popular/sharepoint" rel="tag">sharepoint</a>,<a href="http://del.icio.us/popular/development" rel="tag">development</a>,<a href="http://del.icio.us/popular/rant" rel="tag">rant</a></div> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhivsrJ1y7-S4DDmoanFTot2GqSiCviIuTVbTML350ntI1vCBFKXn-iFph54365UVbI71LwUFnuoxTwJwT1kdxGedK0qWkREGA5iDeDvbmXLX2K-OeAgMqQTTfJudBQoCxzCciaSXz2yR8/s1600-h/Product-SharePoint2007%5B3%5D.jpg"><img title="Product-SharePoint2007" style="border-right: 0px; border-top: 0px; display: inline; margin-left: 0px; border-left: 0px; margin-right: 0px; border-bottom: 0px" height="202" alt="Product-SharePoint2007" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnbz_3P_aBadw9DDkw46jrLivWYSiUT-854SXkQtE1yZT6-FIAmGlWSKKOrRY31XsfisXXXnXyb2AM11m3-ruj4zpow-eXTxqz1OsqPWHuIAUKuUoM4U-Vmrg2e3El-aL5lqyZNqVFDs4/?imgmax=800" width="175" align="left" border="0" /></a> I’ve been working on a SharePoint project for work for the past few months, and I’m getting close to the “go-live” date.  Actually, it’ll go live whenever I’m “done” (whenever that will be…).</p> <p>But that’s beside the point.</p> <p>The point is that I’m becoming less and less impressed with SharePoint as a development platform.  And I didn’t start out with a great love for it in the first place.</p> <p>Perhaps it’s because I’m constrained to only using SharePoint Designer.  No .NET code allowed on the site I’m working on.  That bums me out right there, since I consider myself a great .NET developer.</p> <p>Most of my work has revolved around customizing SharePoint lists, and using SharePoint Designer to create custom workflows to support the operations of the team I’m developing this for.  Not necessarily hard, but the requirements have been challenging to implement, to put it nicely.</p> <p>One main requirement, as it turns out, isn’t really possible to implement fully in SharePoint, at least in a nice way.  There are two groups of people who are responsible for different items (a.k.a. “fields” or “columns”) in a list.  One group, let’s call them “Group A”,  doesn’t want the other group (“Group B”) to be able to modify Group A’s fields.</p> <p>Now let me stop right there!  I understand that SharePoint is a “collaboration” tool, so preventing users from collaborating just doesn’t jibe with the core of SharePoint.  That’s pretty much the crux of the problem.</p> <p>If it were possible to set permissions on the field level, this blog entry would not be needed.  But it is not possible to restrict access to only certain fields in a list to a certain group of people, so I had to think of ways to enable this functionality, and the way I came up with is sub-par at best.</p> <p>I ended up creating 3 lists: one for Group A, one for Group B, and one which shadows Group B’s list.  Then there are workflows which copy Group A’s fields in Group A’s list to Group B’s list, and Group B’s fields in Group B’s list to Group A’s list.  The “shadow copy” list is there to prevent the workflows from running in an infinite loop—the items are only copied back to Group A’s list if they are different between Group B’s list and the shadow copy list.</p> <p>As it turns out, this is not without problems, since if someone in Group A modifies one of Group B’s fields in Group A’s list (did you follow that?) then it will remain out of sync with Group B’s list, since Group B’s fields aren’t copied from Group A’s list to Group B’s list.</p> <p>As it turned out, thankfully, this wasn’t as big of an issue for this particular solution (whew!).</p> <p>It was my intent to set permissions on the lists such that people in Group A only had read access to Group B’s list, and vice versa.  However, as it turns out, the workflows will fail to copy data since they run as the user who initiates the change to the list.  So when a Group A person changes an item in Group A’s list, the workflow tries to copy that change to Group B’s list but can’t because Group A only has read-only access to Group B’s list.</p> <p>So I thought I could change the permission to allow Group A to edit Group B’s list, but then just set the “Hide from browsers” property on Group B’s list so that Group A couldn’t find it when browsing the site.  So instead of preventing a modification of Group B’s list through the use of SharePoint Permissions, I would be preventing the modification of Group B’s list by making it very hard to find the list.  Problem solved!</p> <p>Not.</p> <p>Once again SwearPoint, er, SharePoint worked against me, and here’s how:</p> <p>If you set the “Hide from browsers” property on the list, it does, in fact, hide the list from site when you click on the “Lists” or “All Site Content” links in SharePoint.  <strike>HOWEVER, it also hides the list from workflows!</strike></p> <p><font color="#0000ff">[UPDATE: The list’s <em>name</em> is the only thing hidden from the workflow.  The error I was seeing which led me to the previous conclusion was due to a user-error (and, no, I wasn’t the user!).  The workflow continues to operate successfully if the referenced list is hidden.]</font></p> <p>So my final solution still has three lists, one for each group and a third to ensure that the workflows eventually stop, and each group can see the other group’s lists.  Not at all what I set out to accomplish!</p> <p>It’ll just have to be a training issue to keep each group out of the other’s list.</p> <p>So much for collaboration (at least in this project).</p> Stevehttp://www.blogger.com/profile/09549787502414774213noreply@blogger.com0