Thursday, April 16, 2009

Several tips for MOSS developing & testing with less iisreset-s

THE PROBLEM

OK, so the problem in hand are the iisreset-s (or application pool's recycling if we choose the economical approach), probably one of the most frustrating things one encounters while developing stuff with SharePoint. So I complained about that with SharePoint 2003, when it used to take several seconds and I am almost used to it now, when it can take up to half a minute and more on those slow virtual PC-s (a little less for WSS).
So basically we have two major cases when recycling of the IIS worker process is needed:
  1. when we change (recompile) assemblies that are in the GAC
  2. when we change SharePoint artifacts - site definitions, features, custom field types' definitions, control templates, etc.
Let's start with a few words about these two. The recycling of the IIS worker process to reload an assembly in the GAC is actually an issue that stems from the design of asp.net itself. The case is this: assemblies in the "bin" subfolder of the web application root get reloaded with recycling of the application domain of the web application (remember each asp.net application runs in a separate application domain). Basically an assembly can be loaded in an application domain at any moment, but can be unloaded only when the application domain is terminated - and this is what happens when you have a web application project and recompile the assembly - asp.net just detects the change in the "bin" subfolder, copies the newly recompiled assembly to a temporary folder under the "asp.net temporary files" (the so called shadow copy) and recycles the application domain of the web application so that the updated assembly can be loaded. So far, so good - but there is a big difference with assemblies which are in the GAC (or more precisely with all strongly named assemblies) - these are loaded as "domain neutral" assemblies by asp.net - this means that they are loaded once in the process and are shared by all application domains in the process (there are separate versions of the static data in the assembly for every application domain though). And this is the core of the problem with assemblies in the GAC - the recycling of the application domain just doesn't help to get the new version of the assembly reloaded, the whole process should be restarted - and in the case of IIS this is the IIS worker process of the given application pool (in our case for a SharePoint web application).
OK, so after we know that the process recycling is unavoidable for assemblies in the GAC, we may be tempted to resolve the problem a bit more radically - i.e. by not using assemblies in the GAC at all. Unfortunately, many customizations in SharePoint should be placed just in assemblies that are in the GAC - e.g. feature receivers, list/list item receivers, classes for workflows, custom field types, etc. So it turns out that we are stuck with GAC assemblies. The other "radical" approach here is to just not use the web UI (IIS) when developing and testing these customizations - well, this is one of the first tips of course, which I think most of us utilize most of the time - it is not applicable in all cases of course, especially when we want to see how something appears or behaves on the site itself - but more on that later.


And about the SharePoint artifacts - well, it seems that it is them and their caching in the first load that causes (or at least contributes significantly for it) so much delay after iisrest. And the thing is that an application domain recycling doesn't help to refresh them (you can force it if you put something in the "bin" subfolder or change the web.config for instance) - so I guess that these definitions are cached not in the asp.net cache or in the HttpApplication instance or any other managed object but in the COM core of SharePoint somehow - owssvr.dll (which among other things is also an ISAPI extension).


THE SOLUTIONS

  1. Use custom tools and the stsadm tool to debug and test if possible - the idea is simple - just stay away from the IIS worker processes and use lightweight console or WinForm tools or the nice standard stsadm tool. With console tools including stsadm you will normally set the executable as a start program for you class library assembly that you want to debug/test and with WinForm tools you can either do that or just start the tool first and then attach the Visual Studio's debugger to it. And guess what, there is a nice small bonus here - you have the "child spoiling" "edit and continue" feature here - so you can just break at the beginning of the method and then start adding code on the fly and possibly execute it many times over and over with just dragging the yellow arrow indicating the debugger current row up and down as you like. One reminder about WinForm tools - when you change something in the 12/template folder of SharePoint you should restart the tool (the same thing as with iisreset and IIS, but much much faster) so that the tool gets a fresh snapshot of the artifacts. This is not the case with console tools because they just end by themselves after completing the task they were run for.
    Here is a small example of how you can set the stsadm tool as a start program for testing and debugging a feature receiver:



    We assume here that you've copied the latest changes of the feature files to the 12\template folder and installed the feature before that (use the -force parameter - it just saves a lot of time).
    If you want to test the feature when it is activated on site creation (some things are just a little bit different when the site is being created) you can replace the -activatefeature command above with the -createsite one and debug the receiver while the stsadm tool is creating a new site. And basically using stsadm or some custom tool is best for testing site definitions - you just change the site definition in the template\SiteTemplates folder and run the stsadm tool or a batch file calling several different stsadm commands in a row. I can't really remember when was the last time I used the central administration site or the create web page to create a site collection or site.
    The same approach is quite suitable for testing list item event receivers too - you should use a custom tool here though, stsadm hasn't got a command for adding/updating list items (unless you've added a custom command to it for that matter). One small thing here with synchronous receivers (ItemAdding, ItemUpdating) - if you change only one or two fields in the SPListItem object before you call the Update method on it, only these fields will appear in the AfterProperties property that you'll get in the receiver's methods. This is somewhat different from the normal behaviour that you expect when you debug the receiver having attached to the IIS worker processes - then you have all relevant fields set (all fields in the item's content type) just because the edit form page just sets all these fields. So you'll need to update all fields in the list item (the generic solution) or just the few which you'll make use of in the receiver.
  2. Use inline code in aspx/ascx files instead of in code-behind classes - this may not sound as the best of coding practices but can save really a lot of time. Actually I am seriously addicted to using it. And after the page or the control is implemented and tested the code can be moved to a code-behind file. This way you can create a web page (SharePoint application pages in the layouts folder are easiest to create), put the script runat="server" tag and add some code, then just browse the page and it works. Then some more changes, an F5 in the browser and that's it. You can also debug that code and even change the code while debugging, though not the same way as with the "edit and continue" - here you can change the code only when you are not on a break point or stepping over the code - so the opposite to the "edit and continue" thing.
    A little word about custom user controls - I used several of these for custom field types' editing controls - a little code behind class is also necessary in this case, since the control should implement a .NET interface as well. The next thing is to transfer control from the code behind to the ascx file - this is easy - just create several virtual methods in the code behind, which then get overridden in the ascx file and this is it.
    A little bit off-topic, but if you are interested and didn't know - how does it come that the page is recompiled every time and the application domain doesn't get recycled - the answer is simple - asp.net creates a different assembly each time the page is compiled. This also means that the class of the page is different after each change - that is - its fully qualified name - it's in different assemblies after all. You can easily check this if you define a simple class in the page code then serialize it and put it in the view state/session/http cache and then try to deserialize it after an update of the page. There is yet another small thing here - after a certain number of recompiles of this sort asp.net forces an application domain recycling - so after vigorous code changing and F5-s you'll notice that once in a while the page loads much more slowly - this is when the application domain gets recycled. The logic behind that is obviously to get all those assemblies loaded in the application domain just cleared away.
  3. Tips for web parts - several suggestions here:
    - use user controls in web parts - this one is great for one other reason - you just neatly separate the presentation logic and put it in a user control where using the asp.net declarative syntax you can easily change the HTML code, add web controls, etc. Compare this with the code you would use if you need to create all your controls with code in the CreateChildControl method and|or hard-code the html in the Render method of the web part. And then remember that you can also use inline code in the ascx file and see the changes enacted after a simple refresh in the browser.
    - use XSL for rendering and place the XSLT code in a web part property - this is an alternative to the above one - it actually also nicely separates the html generation from the rest of the code - you just need to fetch some XML with the data that should be rendered (from a web service, after you convert a DataTable to XML, etc) and use an XSL transformation on it to generate the HTML output. If the XSL is stored in a web part property you can easily change it using the web UI after you open the properties pane of the web part on the page . Still this is a little bit awkward - the SharePoint UI provides you with one quite small multiline text field, which also eats all new lines in the text. Alternatively you can use a separate file to store the XSL and use a web part property just to keep its location - pretty much the same as it is in the standard content query web part (how to quickly change XSL files for the CQWP see below). One drawback here compared to the user control approach - it is somewhat more complicated to use web controls if you generate your HTML with XSLT, so the XSL approach is better suited for scenarios when you just display things and don't have much user interaction in the part.
    - place the web part assembly in the "bin" folder - this is not too much of a gain actually. You won't need to recycle the application pool after recompiling, but the delay of the next load is still considerable. Bear in mind that you should not sign the assembly in this case and you will
    probably need to change the trust level in your web.config - to a standard or custom level if you have prepared a custom security policy file to grant some extra permissions to your web part code. If you are used to having your web parts in GAC assemblies in which case they run with full trust, you'll see some quite different behaviour in the "bin" assembly case, especially if the trust level in your web.config is too low. Also remember that if you make calls to other assemblies of yours which reside in the GAC, then the latter should be decorated with the AllowPartiallyTrustedCallers attribute.
  4. Tips for testing the XSLT of content query web parts - I suppose that most guys who work with the SharePoint designer should know this simple trick. The idea here is to modify the unghosted version of the ItemStyle.xsl file or the custom xsl file that you use in you CQWP-s (I normally avoid changing standard SharePoint files, so I normally set my CQWP-s to use a custom xsl file which is just a copy of the ItemStyle.xsl with some extra templates added). OK, so as I said, the trick doesn't work if the item styles xsl file is ghosted which is the case when you first create a publishing site. Then SharePoint gets the contents of the file from a file on the hard-drive (and not from the content database), in our specific case from here: C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\PublishingResources\ItemStyle.xsl (or from the folder of the feature that provisions your custom xsl file if you use one). You can change the file on the hard drive but you'll see the changes on the site only after an iisreset. Fortunately changes to unghosted files are visible immediately. With the SharePoint designer it is quite easy to modify any file in a SharePoint site - and when you do so the file gets unghosted (if it was ghosted in the first place) with the new version stored in the content database (actually this is one of the reasons why I don't like the SharePoint designer - I don't like my aspx files to get unghosted). But what about those who don't use or don't like using the SharePoint designer - well, there is an equally simple way to edit unghosted files in SharePoint - you can use WebDav for that. Just create a network place in your windows explorer and you will be able to see and edit the folder structure and files of your SharePoint site, pretty much the same way as in the SharePoint designer.
    A
    nd a small but crucial detail just to get the whole thing working - this is how you should test the web part on the page after you make changes to the xsl file - so you need to have the page open in design view plus the property pane of the web part open - then after each change in the xsl you just hit the "apply" button (or an F5 + Retry on the prompt that appears on a page with previous post-back) and you see the changes immediately.
  5. Tips for testing the display render pattern CAML of custom field types - my approach here is to use computed fields. The idea is simple - in a custom field type definition (in a fldtypes_nnn.xml file) you have an RenderPattern Name='DisplayPattern' element and in a computed field definition you have a DisplayPattern element - both containing the CAML for the visualization of the fields. And the fact is that the syntax in both is almost identical. Also with computed fields you can use a simple tool (a WinForm for example) to create them in a SharePoint list and modify their definitions' XML, while you keep refreshing a list view page to see the result - so without recycling the application pool you can make changes and test the CAML of the display pattern of computed fields, which then with minor modifications can be transfered to the render display pattern of a custom field type definition. You can create a custom computed field using the SPFieldCollection.AddFieldAsXml (you can "steal" a sample computed field definition from the standard SharePoint "fields" feature) and then modify its definition with setting the SPField.SchemaXml property. And finally - what needs to be changed when you copy the CAML to the custom field type definition - first you should remove the Name attribute form the Field and Column elements (in the computed field definition these are needed to specify a source column, in the case of the custom field type definition, you don't have source columns, but just the field itself), then change the FieldProperty element (if you use it) to Property and remove its Name attribute.
  6. Tips for testing the CAML of custom list views - you can test this if you create an application page in the 12\TEMPLATE\layouts folder and use this code in it:

    <%@ Page language="C#" MasterPageFile="~/_layouts/application.master" %>
    <%@ Import Namespace="Microsoft.SharePoint" %>
    <%@ Import Namespace="Microsoft.SharePoint.WebPartPages" %>
    <script runat="server">
    protected void Page_Load(Object sender, EventArgs e)
    {
    SPList list = SPContext.Current.Web.Lists["Announcements"];
    SPView view = list.Views["All items"];
    string htmlView = view.HtmlSchemaXml;
    // the code up to here is just an example of how you can take the CAML view schema of a
    // list view - in a real test page you will probably want to read the CAML from a file on
    // the disk so that you can edit it and check the results after the edit - the CAML in the
    // file you can get initially either with code as above, or better still - from a View
    // element in a list template schema.xml definition file

    ListViewWebPart part = new ListViewWebPart();
    part.ListName = list.ID.ToString("B").ToUpperInvariant();
    part.ListViewXml = htmlView;

    this.phHolder.Controls.Add(part);
    }
    </script>
    <asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
    <asp:PlaceHolder runat="server" ID="phHolder"></asp:PlaceHolder>
    </asp:Content>

    Check the comments in the code snippet first. Also note that this code will work only in an application page and not on a web part page - if there is a web part manager on the page, the ListView web part, used here as a static web control won't render its contents. Another thing which may be interesting to note is the properties that I used to initialize the ListView web part - you can see that I used only the ListName and the
    ListViewXml properties and not the ViewGuid one - well, this is the subtle trick in the whole sample here.

4 comments:

  1. Stefan -- Please help. For a custom-code SharePoint Event Handler, deployed as a SharePoint Feature, will the Application Pool recycle trick shown here http://blogs.msdn.com/joelo/archive/2007/07/16/iisreset-is-bruteforce-there-are-civilized-alternatives-iisapp-vbs.aspx work and avoid having to run an iisreset?

    ReplyDelete
  2. All -- This if a follow-up to my post above. Yes, the recycle trick shown here http://blogs.msdn.com/joelo/archive/2007/07/16/iisreset-is-bruteforce-there-are-civilized-alternatives-iisapp-vbs.aspx does seem to work and it is, I think, must less heavy-handed when compared to an iisreset. Thank you. -- Mark Kamoski

    ReplyDelete
  3. yes, this is correct, the recycling of a single application pool is much more economical than the iisreset option - i've mentioned that at the beginning of my posting. As for testing event handlers (i assume that you had in mind list item event handlers) i usually don't use the web UI but a custom console or winform tool (a little advertisement for my ListItemEditor utility here) - the tool gets restarted several times faster than you get the SharePoint edit form page refreshed after an application pool recycling.

    ReplyDelete
  4. Aw, this was a really nice post. In idea I would like to put in writing like this additionally – taking time and actual effort to make a very good article… but what can I say… I procrastinate alot and by no means seem to get something done.

    ReplyDelete