MSBuild and Private Accessors of Unit Tests

If you use a MSBuild on a build server to compile your visual studio solutions, you might know this problem: MSBuild won’t be able to compile your unit-test project, because it can’t find the private accessor classes, which are usually generated dynamically. The error looks like this:

The type or namespace name ‘MyClass_Accessor’ could not be found (are you missing a using directive or an assembly reference?

So basically, MSBuild does not generate those classes for you for some reason. However it works nice on your dev machine. Why? On your dev machine, Visual Studio uses the tool publicize.exe to create the private accessors in the background. This component is not present on your build server though.

Step 1: Deploy publicize component to the build server

Copy the following folder from your dev machine to your build server.
C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\TeamTest
If your build-server is 64bit, also copy the folder to
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v9.0\TeamTest

Now if you run msbuild again, it should finally attempt to generate the private accessors. Howeve you will run into this error:

Could not load file or assembly ‘file:///C:\BuildServer\MySolution\MyProject\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll’ or one of its dependencies. The system cannot find the file specified.

This means we need to deploy an additional component..

Step 2: Deploy unit test framework to the build server

On your dev server, take the files Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll and Microsoft.VisualStudio.QualityTools.Resource.dll from the GAC (c:\windows\assembly) and copy them into the project folder of the unit test, according to the error message.

This worked for me (VS 2008, .NET 3.5). Note that the path names might differ if you have a different setup.

Sharepoint Error: Save Conflict Your changes conflict with those made concurrently by another user

If you develop Sharepoint components that modify list items, you might know this error:

Save Conflict

Your changes conflict with those made concurrently by another user. If you want your changes to be applied, click Back in your Web browser, refresh the page, and resubmit your changes.

This happens whenever you try to save a SPListItem which has been modified since you opened it. Review your code to ensure that you don’t open the same list item multiple times, work with a single SPListItem object instead.

Sometimes however it is necessary to open the same list item in parallel, as example if you want to perform an operation with elevated privileges on the item. If you take this approach you need to ensure that if you save one of the SPListItem objects, you need to reload all other SPListItems that point to the same actual item. Otherwise you’ll get the error above whenever you perform a change on the outdated items.

You can use the following utility method to ease the reloading of items:

        /// <summary>
        /// Reload a list item from scratch
        /// </summary>
        /// <param name="item">The list item</param>
        /// <remarks>
        /// Useful to avoid save conflicts
        /// </remarks>
        /// <returns></returns>
        public static SPListItem ReloadListItem(SPListItem item)
        {
            if (item == null)
                return null;

            return item.ParentList.GetItemByUniqueId(item.UniqueId);
        }

Custom redirect after creating a new Sharepoint Item

When creating a new Item in a Sharepoint list, the redirection works according to these rules:

  • If the URL contains a valid “Source” parameter, the user user will be redirected to this URL
  • Otherwise the user will be redirected to the default view of the list, such as AllItems.aspx

There are different approaches to change this behavior:

Via URL Parameter

One appraoch is to override the redirection by changing the Source parameter of the URL. This means the incoming link to the NewForm.aspx contains the redirection URL already. This is pretty static, sometimes you want to set the redirection on the fly. Also the appraoch enforces the same redirection for both Safe and Close buttons on the form.

Via Event Receiver

It is possible to redirect within an event receiver. This means placing a redirect command within a synchrounous event which aborts the normal event flow in a way, that asynchrounous events and other attached event receivers won’t work anymore.

Via Custom Safe Buttons

It is possible to replace the default buttons on the NewForm.aspx page by custom controls. So basically you develop a custom button that inherits from the Sharepoint “SaveButton” class. In there you can apply custom logic for redirection.

Via JavaScript

This is a novel approach I developed to get more flexibility and stability for the redirection behavior. It allows to set the redirection on the clientside.

$(document).ready(function() {

    var button = $("input[id$=SaveItem]");
    // change redirection behavior
        button.removeAttr("onclick");
        button.click(function() {
            var elementName = $(this).attr("name");
            var aspForm = $("form[name=aspnetForm]");
            var oldPostbackUrl = aspForm.get(0).action;
            var currentSourceValue = GetUrlKeyValue("Source", true, oldPostbackUrl);
            var newPostbackUrl = oldPostbackUrl.replace(currentSourceValue, "MyRedirectionDestination.aspx");

            if (!PreSaveItem()) return false;
            WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions(elementName, "", true, "", newPostbackUrl, false, true));
        });
    
});

So basically we modify the click behavior of the create button on the NewForm.aspx to pass in a new “Source” parameter value just before the form is submitted.

The “Column Element” in Sharepoint Render pattern is empty sometimes

According to the Sharepoint SDK:

Column Element (View)
Returns the raw data value for a particular field, as opposed to the formatted version provided by the Field element. In the case of a Lookup field, Column returns the local value (an ID number in the list that references another list), while the LookupColumn element returns the data from the foreign table.

They failed to mention that the output will be empty if the specified column is not included in the containing view already! Seems like a bug / undocumented issue to me.

The “show quoted messages” link breaks other controls

In Sharepoint discussion boards, there is the “show quoted messages” button within each post. It allows to collapse/expand the quoted message.

However this button causes a serious problem:
If you place additional postback sensitive controls on the discussion page, they might break whenever the “show quoted messages” is clicked. This happens, because this button does not perform a valid postback.

Here is a JS based fix:



// workaround for broken show-quoted-text button in discussion board
// see core.js for the original function

function ShowQuotedText(guid, anchor) {
    var frm = document.forms[MSOWebPartPageFormName];
    frm.CAML_ShowOriginalEmailBody.value = frm.CAML_ShowOriginalEmailBody.value.concat(guid);
    if (frm.action.indexOf("#") > 0) {
        frm.action = frm.action.substr(0, frm.action.indexOf("#"));
    }
    frm.action = frm.action.concat("#" + anchor);
    
    // a raw form submit will cause the people editor to break
    //frm.submit();
    // perform a proper postback instead
    __doPostBack('__Page', ''); 
    
    return false;
}

Simply include this snipped in the header of the affected page, or in an external JS file. Just make sure that it is being loaded after core.js.

Sharepoint SPGridView, expand specific group using Javascript

Sometimes you want to expand a specific group within a SPGridView using Javascript.

The following function achieves just that:

function spGridViewExpandViewGroup(gridviewId, groupIndex) {

    var gridview = $("[id$='" + gridviewId + "']");
    var rows = gridview.find("tr[isexp]");

    if (rows.length >= groupIndex) {
        var row = rows.get(groupIndex);
        
        if ($(row).attr('isexp') == 'false') {
            var link = $(row).find("a[title='Expand/Collapse']");
            link.click();
        }
    }
}

Basically you pass in the (serverside) ID of the SPGridView control and the index of the group that should be expanded.

As example, the function call being contructed on the asp.net side:

string javaScript = string.Format("spGridViewExpandViewGroup('{0}',{1});", gridView.ID, 0);
Page.ClientScript.RegisterStartupScript(typeof(string), "expandviewgroup", javaScript, true);

Note: The script requires the jQuery library to be included.

Sharepoint SPGridView control is expanded by default

The Sharepoint SPGridView control seems to be rendered as expanded sometimes, although it should be collapsed.

Here is a javascript function to fix this issue:

function spGridViewFixExpandedGroups(gridviewId) {
    var gridview = $("[id$='" + gridviewId + "']");
    var links = gridview.find("tr[isexp='true'] a[title='Expand/Collapse']");
    links.click();
}

Simply call the script upon page load. Note: The script requires the jQuery library to be included.



Follow

Get every new post delivered to your Inbox.