2

I have learned (still a SP-Noob...) that I have to take care of removing webParts that I deployed as part fo a solution myself.

Now I have coded something like

string file = "MyWebPart.webpart";   
var webPartList = web.Lists["Webpartkatalog"];
if (webPartList == null) throw new Exception("webPartList is null...");

foreach (SPListItem item in webPartList.Items.Cast<SPListItem>().ToList())
{
    if (item.Name.Equals(file, StringComparison.OrdinalIgnoreCase))
    {
        item.Delete();
        webPartList.Update()
    }
}

Which works if the solution was deployed on a german-language-sharepoint (web.Lists["Webpartkatalog"]). It does however not work on an english server (should have been something like web.Lists["Web Part Gallery"]...)

Are the localized names of the WebPartGallery accessible somewhere? Or am I doing something totally wrong?

1 Answer 1

4

You will need to use the appropriate Resource. In your case, it's in core.resx, resource key "webpartgalleryList". Note that the Web Part Gallery is always at the root of the site collection, so for maximum portability you should access it using web.Site.RootWeb, not web. Also, SPListCollection[string listName] will already throw an exception if the list is not found, so no need to rethrow your own.

So, modify your code to:

string file = "MyWebPart.webpart";  
string webPartGalleryListName = SPUtility.GetLocalizedString("webpartgalleryList", "core", (uint)web.Language);
var webPartList = web.Site.RootWeb.Lists[webPartGalleryListName];

Alternatively, a better, more performant option, would be to get the list using its URL, which for this library is invariant :

string siteUrl = web.Site.ServerRelativeUrl;
if (!siteUrl.EndsWith("/"))
{
  siteUrl += "/";
}

var webPartList = web.Lists.GetList(siteUrl + "_catalogs/wp");
...

Lastly, a few notes on your loop in the second part of your code example.

  • You gain nothing with .Cast().ToList(), there is already an enumerator on SPListItemCollection. These two calls will cause the entire list to enumerate twice initially.
  • Instead of getting a new list to work with to be able to delete without getting the error "Collection was modified", you could use a for(;;) construct, which does allow you to modify the subject of the loop. In this case it is important to set aside SPList.Items in a SPListItemCollection object before entering the loop, otherwise using SPList.Items[index] will re-fetch the entire collection on each iteration.
  • You don't need to call SPList.Update() when deleting items. Regardless, you could have called it only once after the loop.
  • You should consider using a CAML query to specifically find matching names instead of looping through everything and selecting matching items post-query.
  • Also consider not fetching all fields for each item, as you just want to delete them. I'm not 100% sure here but I think only getting the ID and Name fields would be sufficient for your purpose. See point #2 here
5
  • Nice answer! One comment is that you can use SPUtility.ConcatUrls inside the GetList method call. It takes two strings and concatenates them, inserting the '/' if it is needed so you do not need to perform the check.
    – Rich Ross
    Feb 29, 2012 at 13:11
  • Thanks for the great answer. However (can't resist...) If SPList.Items is an enumerator, why would it re-fetch on each iteration? (The mentioned link seems quite good and it will read it again and again, but it talks about for loops, which are totally different) 2. SPList.Items returns something that can be cast to IEnumerable. I wanted to use ToList() for which I needed IEnumerable<T>. Cast<T>() does the trick (and IMO quite fast, too). I used ToList() because the result is a new List and not the original. Objects couldn't be Deleted()ed from the original while looping.
    – Nils
    Feb 29, 2012 at 13:49
  • You're right about the enumerator, I inadvertently mixed up foreach and for in my mind. However by chaining the Cast<>() and ToList() I believe you are still causing it to re-evaluate at least once too many, since it's doing a GetEnumerator, then a complete enumeration to convert ToList, then another GetEnumerator to get your final IEnumerator. As you say the result is a completely new List. If you have thousands of item + metadata that's a significant hit. To avoid having the "Collection was modified" error you could just put your matching items in a toBeDeleted collection.
    – Louis
    Mar 1, 2012 at 2:36
  • 1
    Edited post per previous comment and added some more thoughts.
    – Louis
    Mar 1, 2012 at 2:45
  • Just to mitigate, if you're confident that your list will never have more than a few dozen items and not a huge number of fields (as should be the case for the Web Part Gallery), the last two points are less critical, just things to keep in mind when querying lists in general.
    – Louis
    Mar 1, 2012 at 16:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.