Sue Hernandez's SharePoint Blog

SharePoint and Related Stuff

CAML vs LINQ to SharePoint 2010 – Performance

Summary

LINQ to SharePoint is really cool.  There’s lots of things you can do with it that you can’t do with CAML alone.  It’s much easier to read, to work with, to maintain, and because of the strongly typed objects, you find more of your bugs at compile time than at run time like you have to with CAML.

However, let me just say LINQ to SharePoint performance STINKS big time when you’re doing relatively simple comparisons that you could just as easily do in straight CAML.

My Test Case

My test case is a program that reads in information from 2 different stored procedures in SQL, separates out the data, loads them in to 4 SharePoint Lists, and looks for invalid corresponding data in another SharePoint list.  Basically 1 list is a master Valid Codes list, 1 list is a master Invalid Codes list, and the other 2 lists are distinct lists of categories and departments that relate to those codes, coming from the denormalized SQL queries.  Basically you first put all the valid codes in to the valid list, then you go through the invalid codes and see if there are matching codes in the valid codes list, and if so delete them, then check for corresponding data in another SharePoint list that has those code values in it, then put the invalid code in the invalid list.

The results

Running the program using straight CAML queries and no LINQ took 3 hours 11 minutes.

Running the program using all LINQ to SharePoint and no CAML took 9 hours 2 minutes.

 

I’m really bummed, I liked LINQ.  Now I’m not so sure.

Advertisements

Redesign your coded InfoPath 2010 Form to handle 1000’s of records

Scenario

Let’s say you have an InfoPath form with Code Behind where you want to have the user paste in thousands of Job Codes or Organization Codes or Cost Codes into a text box, and then they would press a button and it would validate those records – the Codes – against a System of Record (like a list in SharePoint) and then add the records into a Repeating Table in your InfoPath form.  You want to do this because your Codes have metadata associated with them that you either want to display and/or have the user update, and you want to have conditional formatting and buttons on each record to make it look and function the way you want it to.

You will quickly find that doing this in a Web Browser-enabled form is near to impossible, requires too many configuration settings to change in various places, and performs really sluggishly any time you take an action on the form like clicking a checkbox or pressing a button that only runs rules.  You’ll run into InfoPath settings in Central Admin that you have to configure, and you’ll run in to the dreaded 6-minute default timeout of IIS pages waiting for responses from the server.

The first step I took, on the advice of Doug my co-worker, Architect extraordinaire, was to offload all of the individual item processing – the validation against the SOR – off to a custom SOAP web service.  This did the heavy lifting – I passed in a comma-separated string of all of the Codes (which is why it had to be a SOAP service and not a REST service – too much data to pass in a URL), and then it validated the codes.  Finally, I had the web service create an XML Document with exactly the structure of my repeating table in InfoPath, fill it in with all of the Codes and their metadata, and pass that Outer XML as a string back to the form.  I used XPathNavigator to then insert the XML directly into the form.

What I found, through constructing a timing debug string, was that my Web Service call was taking 2 – 4 seconds.  Inserting the XML and waiting for the XML to render into HTML was taking 4 – 10 minutes, depending on how many codes I had.  It was completely unacceptable as I had clients who needed to put in 2000 codes.  My form got sluggish at 300 and outright unusable over 700.

Solution

So I went back to Doug, which is what I usually do when I’m stuck on a problem.  I explained where the bottleneck was and confirmed my suspicions by reading up on Performance and InfoPath on a series of 6 Microsoft blogs.  Doug said, since the bottleneck is the size of the XML in the form, you have to get the XML out of the form, into some other storage mechanism.  Allow the form to hold no more than 100 items at a time in the repeating table, and implement Paging of sorts to get at the other 1900 records.

Wow, sounded like a great idea so I tried it, and so far it seems to be working.  Because I’m getting paid to do this, I can’t give you all of the code.  However, here’s the general steps you will need to take in order to accomplish this idea:

  1. Create a SOAP Web Service.  I had done this before and put it in ISAPI, but you have to do some extra manual work to get it to work there.  I found that if you add it directly to a subfolder under the Layouts directory, you can call it from there without any extra work.  The Web Service will need to have 4 methods or so:
    1. AddSQLDataGetPageXML(string identifier, int pageNum, string codes, string extraMetadataIfNecessary, string xmlChanges, string ns)
      This method is responsible for

      1. (a) getting existing data out of SQL for that identifier, or creating a new identifier if none exists;
      2. (b) copying the existing data, if any, into a  Generic List of custom objects representing your Code and its metadata;
      3. (c) processing  any changes that the customer has made to the repeating table of the 100 or so items that are showing (you need to capture your changes before blowing away and recreating the 100 items);
      4. (d) doing the actual Validation of the Code against your SOR;
      5. (e) adding the valid (and Invalid) codes to both the Generic List, as well as adding the record to the SQL table;
      6. (f) sorting your Generic List by RowNum or some kind of numbering identifier that you have added to the custom object;
      7. (g) iterating through the entire Generic List, gathering statistics of things like how many are invalid, how many total, etc., and taking only the ones in “that page” and turning them into an XML Element that you insert into a generic XmlDocument object that you created at the top of the method (using the ns parameter which is the namespace of the “my” prefix of your InfoPath form);
      8. (h) and finally returning a custom Return Object that holds both the statistics that you wanted to capture, as well as the XML which is the OuterXML of the xml document.
    2. GetPageXML(string identifier, int pageNum, string xmlChanges, int oldPageNum, string ns)
      This method is responsible for

      1. (a) getting existing data out of SQL for that identifier;
      2. (b) copying the existing data, if any, into a  Generic List of custom objects representing your Code and its metadata;
      3. (c) processing  any changes that the customer has made to the repeating table of the 100 or so items that are showing (you need to capture your changes before blowing away and recreating the 100 items) – NOTE HERE – this has to handle deletions too, so you need some special manipulation of comparing what is (or rather is NOT) in the XML string of changes, versus what IS in your Generic List that you read out of SQL;
      4. (d) sorting your Generic List by RowNum or some kind of numbering identifier that you have added to the custom object;
      5. (e) iterating through the entire Generic List, gathering statistics of things like how many are invalid, how many total, etc., and taking only the ones in “that page” and turning them into an XML Element that you insert into a generic XmlDocument object that you created at the top of the method (using the ns parameter which is the namespace of the “my” prefix of your InfoPath form);
      6. (f) and finally returning a custom Return Object that holds both the statistics that you wanted to capture, as well as the XML which is the OuterXML of the xml document.
    3. ProcessChanges(string identifier, string xmlChanges, string ns, int startItem, int stopItem, SqlConnection conn, List<YourCustomCodeObject> allCodesFromSQL)
      This is the method that you call from the first 2 methods to process your existing changes.  Remember, if the user changes something on the current page, and then you switch to another page, you need to make sure that you capture what has been changed on the current page.
    4. ReValidateCode(string code, string otherMetadataHere)
      This method is responsible for

      1. (a) reading in the code and finding it / validating in against your SOR;
      2. (b) creating a custom Return Class that can hold your Code’s data and metadata, and whether or not it’s valid;
      3. (c) returning the custom Return Class (your InfoPath form handles the changing of the repeating table item in the repeating table)
  2. Hook this up to your InfoPath Form
    1. There’s too much involved to go into here, but you need to basically do the following:
      1. Have a SOAP web service Data Connection, and methods to set values into and Execute that connection.
      2. Have a text box to paste the Codes into, and a method to take those codes, wrap them up and send it to the SOAP web service
      3. Have a repeating grid with all of your fields, metadata fields, buttons (such as ReValidate), and conditional formatting
      4. Have buttons for Previous and Next that have conditional formatting on them to only show if it is valid to show them (i.e. don’t show Previous if you’re on page 1)
      5. Have a MoveToPage method that gets called from the buttons and optionally some other places (like a delete all on this page kind of method)
  3. Create your Workflow (a custom Console Application probably) that handles the business processes of the form
    It will do the following:

    1. Loads in the InfoPath form, which you have created a class for using the XSD of the InfoPath Form (Export to Source Files to find the XSD)
    2. Checks to see if it’s already processed
    3. Looks for the Identifier in the Form that got stored when you were using the form
    4. Looks in SQL using the Identifier to get all the records
    5. Processes the records
    6. Deletes the records out of SQL

 

InfoPath 2010 – Registering of form templates deployed through object model is not supported

In an earlier post, https://suehernandez.wordpress.com/2013/11/22/deploy-infopath-2010-with-code-as-solution-feature/, I explained how to implement an InfoPath 2010 form as a Feature in SharePoint 2010.

As I was trying to deploy this feature, I kept getting the error:

Registering of form templates deployed through object model is not supported

 

It turns out that what happened is that up until that point I had been deploying my InfoPath forms through Central Administration.  It’s easiest and usually quickest to do it this way (although once you deploy the same form like 50 times it starts getting really slow to deploy).

But I had not changed the “Identifier” per se of the form.  Let me show you a couple of pictures that shows what I’m talking about:

Form Template Properties buttonForm Template Properties dialog

Where it says “urn:schemas-microsofr-com….” that is the identifier of the form.  If you publish the form, and then copy it and change the name of it, it does NOT change the identifier.  So if you’ve already got that ID in Central Admin under Manage Form Templates, and then you try to deploy the same form as a feature, you get this error message.

One of the ways you can fix this is to either manually change this URN ID, or just open up the form in InfoPath Designer in design mode and do a Save-As.  It will automatically change your ID to match the new name of the form.

In the mean time, delete the form from Central Admin and try your Solution Deployment again.

Deploy InfoPath 2010 with Code as Solution-Feature

This one will be somewhat long of a post.   Here we will examine deploying an InfoPath 2010 form with CodeBehind as a custom Feature to SharePoint 2010.

  1. Change the properties of your form to Full Trust

    Full Trust

    Full Trust

  2. Publish your form to a SharePoint Server

    Publish Step 1

    Publish Step 1

  3. Choose your URL to the site where you’re going to be using the file.  Note that it will put this form in the Form Templates (url is FormServerTemplates) library at the top of your Site Collection

    Publish Step 2

    Publish Step 2

  4. Make sure that it is checked off to enable the form to be filled out by a browser, and choose Administrator Approved Template

    Publish Step 3

    Publish Step 3

  5. Choose the location on your desktop or local computer or shared drive where you want to save the Published form template to

    Publish Step 4

    Publish Step 4

  6. Create your Visual Studio solution.  Create it as a SharePoint 2010 Empty SharePoint Project

    Create new Empty SP Project

    Create new Empty SP Project

  7. Choose to deploy as Farm Solution

    Deploy as Farm Solution

    Deploy as Farm Solution

  8. Add a Feature to the project

    Add Feature

    Add Feature

  9. Rename the feature to something that makes more sense, so you can more easily find it later through PowerShell or in the 14 hive

    Rename Feature

    Rename Feature

  10. Open the feature in Visual Studio into Design view and set the properties of the feature at the top.

    Set Properties of Feature

    Set Properties of Feature

  11. Add a Module to the project

    Add Module

    Add Module

  12. Rename the module and remove the Sample.txt file

    Rename Module and remove Sample txt

    Rename Module and remove Sample txt

  13. Add your Module Files.  Here you will be adding the published form that you completed in Step 5 here, as well as the dll.  (You can get the .dll by going into design view in InfoPath, pressing Publish, and pressing Export Source Files – give it a folder to put the source files in, and find your .dll in there)

    Note that I have a little naming change here versus what I have in step 5.  I am showing you a “real” project file and had to blot out part of the name.  But this will be your published file – not the file you use to make the changes to the form.

    Add Module Files

    Add Module Files

  14. Open the Elements file in the NewFormModule.  You will have to make changes.  Here’s the file as it looks before:
    Elements Before

    Elements Before

    and here’s the file as it looks after:

    Elements After

    Elements After

  15. Open back up the Feature in Design mode, and press the button to switch it to Manifest mode.  Expand Edit Options and add the 2 properties shown below, with your Feature Name and your Published InfoPath form name as the values:

    Update the Manifest

    Update the Manifest

  16. Get the Properties window up for the Feature.  You will be adding a Receiver Assembly and a Receiver Class
    ReceiverAssembly=”Microsoft.Office.InfoPath.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c”
    ReceiverClass=”Microsoft.Office.InfoPath.Server.Administration.XsnFeatureReceiver”

    Feature Props add Receiver info

    Feature Props add Receiver info

  17. OPTIONAL:  Add Activation dependency.  NOTE:  if you are going to mark your feature as Hidden=TRUE, you cannot add this dependency.  We will discuss later why you may want to make your feature Hidden.
    <ActivationDependency FeatureId=”C88C4FF1-DBF5-4649-AD9F-C6C426EBCBF5″/>

    Feature Activation Dependencies

    Feature Activation Dependencies

  18. Now in your solution explorer, you will want to highlight the Elements file and get the properties.  Clear out the Deployment Path and make sure it’s of type ElementManifest

    Elements Properties

    Elements Properties

  19. Next highlight the XSN published form and get its properties.  Clear the Deployment Path and make sure it is of type ElementFile

    Form Properties

    Form Properties

  20. Repeat the previous step for the DLL file
  21. Open up your Feature in Design mode just to look at it.  Your Manifest file should look similar to the following (if you added the Activation Dependency):

    Manifest After

    Manifest After

Making the feature Hidden

The problem with adding the published file to Central Administration – Managed Form Templates is twofold:  (1) It puts in every version you create into the manifest; and (2) It makes the feature available to EVERY site collection in your Farm.  Similarly, if you don’t make the feature Hidden, all site collections will be able to see this feature.

Depending on your project, you may opt to hide the feature so that no one can turn it on, and only activate it to the Site Collection using PowerShell.

If you choose to make your feature hidden, you cannot have the Activation Dependency of the Enterprise Features.  Just realize this means is that if you don’t have Enterprise Features turned on, this Content Type will not work.

Here’s a picture of where you’d make the feature hidden – it’s just another one of the properties on the feature:

Is Hidden is True

Is Hidden is True

Deploy the feature, Activate it, and Use it

Once you deploy the solution package (.WSP file) and activate it to the site collection you’re going to use it in, it will now be a Content Type that you add to your Form Library.  Any fields which you promoted in the Publishing process will automatically be added to your Form Library and will sync to the data in the form.

References

I got bits and pieces from a lot of places, but here’s some main ones that I got a lot of detail from:

SPSecurity.RunWithElevatedPrivileges and Console Applications

Wow – I should have recognized this a LONG time ago.  But hey, I’m human.

When you use code such as the following:

SPSecurity.RunWithElevatedPrivileges(delegate()
{

     using (SPSite site = newSPSite(urlToSite))
{

         using (SPWeb web = site.OpenWeb())
{

             // Do something
         }
}
});

if you use that code in a Web Part, Application Page, or I think Event Handler – i.e. any code that has Context inside SharePoint – the code runs as expected and will elevate your permissions to the Application Pool account.

However, if you try to use that same code in your Console application, and let’s say you update a list item, and you run it either in debug mode or just “regularly” by double-clicking the exe file, you will notice that last modified by is YOU – NOT your Application Pool account.

This is because there’s no SharePoint context.

Instead, you can create a batch file (like if you have input parameters) and use the “RunAs” method that Windows gives you (example, in Windows Server 2008 R2 you have to hold down Shift and Right-click the batch file, in order to get the Run As option).  If you’re setting up the job as a scheduled task through Windows, you simply have to set the account to the Application Pool account and specify that it should run whether or not the account is logged in.

SPD 2013 Workflows – Wait for Change booleans

The Scenario:

I have an approval process in my InfoPath form.  When the user presses a button called Approve or a button called Reject, it fills in 3 read-only fields:  ApprovalName (with currently logged in user name); ApprovalDate (with today); and Approval (Boolean with blank default – true means approved, false means rejected).

In SharePoint Designer 2010 what I would do is to use an action called “Wait for field change” (not sure if that’s the exact action name).  I would give it the ApprovalName field and say Wait until Field ApprovalName is not empty.

The reason I choose that field is because I do not wait to wait until Approval is true – because true means approved; what if it’s rejected?  I can’t wait for it to be false either for that same reason.  In 2010, it wouldn’t give you the choice for Boolean fields to say “is not empty” so I chose the text field instead, the person’s name, that gets auto-populated when the button is pressed.

The Problem:

I ran across 2 problems.  The first of which is that in SharePoint Designer 2013, with the 2013-compatible workflow, you can no longer choose things like “Contains”, “Is NOT Equal to”, “Is greater than”, “Is not Empty”, etc.  You can ONLY choose Is Equal To.  As a matter of fact, it’s not even a choice – it’s hard-coded right in there – you can’t even click “is Equal To”.

So what I figured I’d do, is to create another field in InfoPath called ApprovalCompleted as a Boolean, with false as the default.  When you press EITHER the Approve or Reject buttons, it turns the ApprovalCompleted field to true.

So I went in to SharePoint Designer and changed my Wait for Field Change to ApprovalCompleted.  For the value it wouldn’t allow me to fill anything in – it only gave me a Yes or No.  So I chose Yes.

No matter how many times I chose “True” in that blasted InfoPath Form, the Workflow would never get past that Wait for Field Change action – it never equaled.

The Solution:

I Googled around a bit and found that others had the same problem – Boolean fields just never match in a Wait for Field Change action.  I think it’s because it’s storing it as a 1 or 0 and the action expects “Yes” or “No”.

So there’s 2 ways of handling this.  One way is to go back into your InfoPath form and change ApprovalCompleted into a text field and have a textual representation of completion when they click the Approval or Rejection buttons.  That way you can set the Wait for Field Change action to equal a particular string that you’ve set in the InfoPath form.

The other way to do it is to set up a Calculated Field on the Form Library.  So to be clear, you need to promote ApprovalCompleted as a Boolean field (in this particular way of handling it) to the Form Library.  Then you go into the Form Library and create a new field, and call it something like ApprovalCompletedCalc.  Set its formula as just outputting the ApprovalCompleted field, and set its output format as a Number (doesn’t really matter if you use number or string here).  Now in your workflow, you need to Wait for Field Change to be the field ApprovalCompletedCalc and it needs to equal the value 1 – 1 for true, and 0 for false.  No quotes, just the number 1.

Extras:

Couple of interesting things to note.  First is, is that if you have that Calculated Field in your view, and you’ve TOLD it that it’s supposed to be a number, it’s still going to read as “Yes” and “No” EVEN THOUGH it’s really storing 0 and 1.  To be clear, do NOT set your SPD workflow to look for “Yes” or “No” – it needs to be 1 or 0.  But yes, the view is confusing and shows you the text.

Also, in SPD, in a condition, the Yes/No value seems to WORK right.  So if you say “If Approval Is Equal To Yes” it works right.  It captures the “true” or the 1.   So it’s just that other action, the wait for field change action, that is messed up.

 

SharePoint 2013, InfoPath and Claims – GetUserProfileByName

You would not believe the hoops you have to go through to get data auto-populated in an InfoPath Form if you’re using Claims-based authentication, which I believe is the default in SharePoint 2013.

The Scenario:

You have an on-premise SharePoint 2013 Server installation with InfoPath Forms Services (so you have enterprise) and you want to create an InfoPath form that auto-populates a logged in user’s ID, email, and phone number.  You are using Claims-based authentication in the web application in question.

In this scenario, I’m using InfoPath Designer 2010 – I haven’t upgraded to 2013 yet, but I don’t think it makes a difference.

The Problem:

When you use Claims-based authentication, your user name is prefaced by “0#.w|”.  So for example, if your user name is SuesDomain\jdoe then your Claims-based user token will be, without the quotes:     “0#.w|SuesDomain\jdoe”

InfoPath can’t handle that, or more specifically, the UserProfile.asmx method GetUserProfileByName method can’t handle that.  InfoPath tries to pass in your Claims-based user token instead of your domain\User Name.

You get 2 problems – you have an authentication problem where the currently logged in user is not allowed to hit the web service, so you get an Access Denied 401 authentication error.  The second problem is getting the right user from the web service.

The Solution:

The steps you have to take are listed here.  See the reference posts for more details and pictures.

  • Have your Active Directory team create you a User Name and Password for a “generic” InfoPath Account.  The one I created was called Domain\SP2013_IPRdr for “InfoPath Reader”.
  • Have the Central Administration team create a Secure Store service application, if they don’t already have one.
    • Generate the Secure Store key if you haven’t already.
    • Create a new application in the Secure Store called something like InfoPathGUPBN (“GetUserProfileByName” is what the acronym stands for); target application type is Group, and User Field Types are the Windows User Name and Password.
    • Set the credentials:  Whomever you want to administer the application should be set as the Target Application Administrators and for the Members, select All Users.
    • Set the Credentials to the Application and use the User Name and Password that you received from the A/D team, example Domain\SP2013_IPRdr.
  • In SharePoint 2013 – in the site you wish to publish your form to, create a Data Connection library.
  • Go Into InfoPath and go to your GetUserProfileByName secondary data connection.  If you don’t already have one, you can create one the “normal” way, making sure you do not choose a user name (just like you would normally do).  For an example of how to set one up, see the first reference in the section at the bottom of this post.
  • Modify the GetUserProfileByName secondary data connection so that it does NOT automatically load the data upon form load.  This is not required, but saves performance.
  • Convert the GetUserProfileByName secondary data connection into a UDCX file (“Convert to Connection File”) – store it in the Data Connection Library that you created a few steps ago.
  • Download the UDCX file that you just created from your SharePoint 2013 Data Connection library and put it on your desktop.  Edit it in Notepad.
  • Change the commented out line to read something like this (depending on what you called your Secure Store Application):
    <udc:Authentication><udc:SSO AppId='InfoPathGUPBN' CredentialType='NTLM' /></udc:Authentication>
  • Upload the UDCX file back in to the Data Connection Library.
  • Publish your newly changed InfoPath form into your SharePoint 2013 InfoPath Form Library
  • Make sure to give your special A/D account, for example the Domain\SP2013_IPRdr account, full read access to that site, so that they have permissions to hit the web service.

All of the above was just to get you to Authenticate properly with the Web Service.  If you were to stop now, and try to get the information from the web service you would get the information for the user Domain\SP2013_IPRdr.  Not the logged in user.  Now you have to take the following steps to get the real logged in user:

  • Go into InfoPath and go to create a Form Load rule
  • For the first rule, call it “Set User Profile” or something similar
    • Create an action to Set a Field’s Value
    • Choose the field – it’s the GetUserProfileByName secondary data connection, in the queryFields node, all the way in:  AccountName.
    • Set it to the following formula value:
      substring-after(userName(), "0#.w|")
    • Add another action, Query for Data, and choose your GetUserProfileByName secondary data connection
  • For the second rule, call it something like “Set Fields”
    • This is where you fill in your fields from the data source.  To find a good example of how to do this, see the first reference in the section at the bottom of this post.

Bottom line:  Pain in the rear.

References:

SharePoint Designer 2010: You do not have permission to do this operation

SharePoint Designer 2010:  You do not have permission to do this operation
–  or  –
Master Page Error:  The Master Page File ‘https://yourserver.com/yourPath/yourSC/_catalogs/masterpage/v4.master‘ cannot be loaded.  Attach a different Master Page, or correct the problem in Code view.

Environment Specifications: 

  • SharePoint Designer 2010. 
  • Windows XP OR Windows 7
  • All of the problem sites were sites that had been migrated from 2007
  • All of the users having problems had FULL CONTROL of the sites they were trying to get into
  • When using a VPN, we always get both errors
  • When outside of the network completely (our site is accessible on the internet with credentials) then we sometimes do not get the first error, but we can’t put the page into Design mode because of the second error.

So we figured out that it had something to do with permissions at the Master Page Gallery at the top of the site collection.  But what was wierd was that any new subsite we created did NOT have this problem.

Well, to make a long story short, we were right, and it’s a “feature”.  KB Article http://support.microsoft.com/kb/2592376 from Mirosoft explains that you have to be one of 3 different permissions, at the site collection level, in order to edit in designer:

  • Site Collection Administrator
  • Designer
  • Owner

Now we whittled it down to this – you don’t have to be in the “Designers” group at the top of the site collection – but if your configuration of the Master Pages Gallery was untouched, that WOULD work (because Designers were set as Design level in the Master Pages Gallery).  Basically the short of it is, you need to HAVE DESIGN OR GREATER PERMISSIONS ON THE MASTER PAGE BEING USED (or just the whole gallery) at the TOP OF THE SITE COLLECTION.

Turns out the reason for these varying behaviors is this:  In 2007, new sites would automatically inherit their master pages from their parents.  If you made no changes to that, it inherited right up to the top.  So it’s looking at the master page from the top, in those circumstances.  And as we’ve seen here, if you don’t have Design permissions to it, it doesn’t work.

In the case of a new 2010 site (just a regular Team Site), the new 2010 behavior is to use the Master Page in the gallery it has in its own site.  And since the user is an Administrator (Full Control) of that site, then no problem getting to the Master Page!

Just so you know, we did end up calling in a Microsoft Ticket on this one, to confirm our findings.  We knew at that point it had to be something about permissions at the top, but we didn’t know why.  They confirmed it, providing the KB article I mentioned earler, and the explanation you see in this article.

SharePoint 2010 Crashes after Service Pack 1 SP1 and August 2012 Cumulative Update CU

Well we just applied Service Pack 1 (SP1) to our SharePoint 2010 server – both foundation and server – and then the August 2012 Cumulative Update (CU) server only (that’s what we were told to do by Microsoft – your situation may differ).

When we tried to browse to the root web after all of this was done (we had to run a few -force commands using command line PSConfig.exe after the GUI failed), and our site gave us a NASTY 500 error.

I tried to go to Inetmgr (Internet Information Services Manager) and tried to take a look at where the log files are kept, I couldn’t remember.  So it pops up with this message saying there’s something wrong with our web.config – specifically the system.web/pages node was duplicated.

So I took a backup of the web.config and looked, and indeed it had a duplicate entry.  Now here’s the wierd part, here was the dup:

 <pages enableSessionState="false" enableViewState="true" enableViewStateMac="true" validateRequest="false" pageParserFilterType="Microsoft.SharePoint.ApplicationRuntime.SPPageParserFilter, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" asyncTimeout="7">
      <controls>
        <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=1.0.61025.0, 
Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      </controls>
      <namespaces>
        <remove namespace="System.Web.UI.WebControls.WebParts" />
      </namespaces>
      <tagMapping>
        <add tagType="System.Web.UI.WebControls.SqlDataSource, System.Web, Version=2.0.0.0, Culture=neutral, 
PublicKeyToken=b03f5f7f11d50a3a" mappedTagType="Microsoft.SharePoint.WebControls.SPSqlDataSource, 
Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      </tagMapping>
 </pages>

Be very careful that this is the one referencing the 12 version assembly – not the 14 version.  So I removed these lines and it now worked.

Stupid Mistake – stored procedure expects parameter which was not supplied

I made a boneheaded mistake today and wanted to write it down so I don’t make the same boneheaded mistake again.

I was writing a program for gathering some custom metrics in SharePoint, to be stored in a custom SQL table.   I had written a stored procedure and properly passed in all parameters, or so I thought.

SqlCommand cmd = new SqlCommand("NameOfMyStoredProcedure", mainConnectionObj);

cmd.Parameters.Add(new SqlParameter("@TimeStamp", startTime));
cmd.Parameters.Add(new SqlParameter("@WebURL", web.Url));
... [more parameters here]

I kept getting “Stored Procedure NameOfMyStoredProcedure expects parameter @TimeStamp which was not supplied”.  I double-checked and triple-checked to make sure I had the syntax right.  I then went down the route of thinking that TimeStamp was a reserved word.  But then I tried something – I reversed my first 2 parameters, and sure enough, it was complaining about @WebURL this time.

Turns out it was extremely simple.  I just needed to specify the CommandType, telling it that it was indeed a stored procedure.

SqlCommand cmd = new SqlCommand("NameOfMyStoredProcedure", mainConnectionObj);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@TimeStamp", startTime));
cmd.Parameters.Add(new SqlParameter("@WebURL", web.Url));

That was it. It then worked like a charm!