1

So I'm trying to create a new content type at the site level, add it as an allowed content type in a custom document set content type, and then add it to all the libraries where the document set is used so that it's actually there for people to use in the doc sets.

All this in a feature upgrade receiver, so C# / server-side code.

Everything goes pretty smoothly except that I'm getting "Save Conflict" SPException when I try to add the content type to each of the lists. I am used to paying attention to when I call Update() on something and then making sure to get a new reference to it if I plan to update it again to avoid those conflicts, but in this case I don't call update on the lists, and I'm actually getting the references to the lists after I update the document set template, so I don't see how they could have gotten stale.

Here's basically how the code goes (although I omitted a lot of logging and error handling for clarity/brevity - I know that the error happens for each library because I'm catching those, so those exceptions are not interrupting the loop):

// at this point the new content type has been created
// successfully, so i get the _site_ level content types
SPContentType newCType = web.ContentTypes["New CType Name"];
SPContentType docSetCType = web.ContentTypes["Document Set Name"];

DocumentSetTemplate template = DocumentSetTemplate.GetDocumentSetTemplate(docSetCType);
template.AllowedContentTypes.Add(newCType.Id);

// update with pushdown, because the doc set is used in many document libraries
template.Update(true);

// however, that did not actually add the new content type to any libraries,
// it only updated the document set to _allow_ it.  i still need to get the
// new content type into the libraries so users can add them in the doc sets.

// so get the usages
List<SPContentTypeUsage> usages = SPContentTypeUsage.GetUsages(docSetCType).ToList();

foreach (SPContentTypeUsage usage in usages)
{
    if (usage.IsUrlToList)
    {
        // how can this not be a fresh reference to the libraries?
        // i didn't get this reference _before_ i updated the document set template,
        // i'm getting these references _after_ that update.
        SPList list = web.GetList(usage.Url);

        // i double check to make sure it wasn't automatically added when
        // the doc set template was updated, and i know it wasn't because...
        SPContentType listLevelCType = list.ContentTypes[newCType.Name];
        if (listLevelCType == null)
        {
            // ...i have a log entry here to indicate that i am about to try to add it
            listLevelCType = list.ContentTypes.Add(newCType);

            // and this is what causes the save conflict.
        }
    }
}

What am I missing here? What do I need to refresh my reference to in order to not get a save conflict when adding to the lists' content type collections?

3
  • I will not venture an answer, but I would use web.Lists["Foo"] instead of web.GetList(). Have you tried that? There might be some internal differences in the implementations. Jul 23 at 21:46
  • I am actually right in the middle of testing using that way to get a new reference to the lists. The tricky part is that SPContentTypeUsage will provide a server relative URL to the list, but not it's name or ID. Indexing directly into web.Lists[] you need the name or ID, but web.GetList() takes a server relative URL. So I'm about to test doing web.GetList(usage.Url) and then immediately doing newList = web.Lists[list.ID]. We'll see what happens... Jul 23 at 21:53
  • ...aaand: nope. Looks like I'm going to have to open a whole new site and web. Oh well. Jul 23 at 22:10
1

So, as mentioned in the comments, first I tried just immediately getting a new reference to each SPList by doing:

SPList list = web.GetList(usage.Url);
SPList newList = web.Lists[list.ID];

SPContentType listLevelCType = newList.ContentTypes[newCType.Name];
if (listLevelCType == null)
{
    listLevelCType = newList.ContentTypes.Add(newCType);
}

But that did not work. Same issue. So I resorted to opening all new SPSite and SPWeb objects, and that finally worked:

List<SPContentTypeUsage> usages = SPContentTypeUsage.GetUsages(docSetCType).ToList();

using (SPSite newSite = new SPSite(web.Site.ID))
using (SPWeb newWeb = newSite.OpenWeb(web.ID))
{
    foreach (SPContentTypeUsage usage in usages)
    {
        if (usage.IsUrlToList)
        {
            SPList list = newWeb.GetList(usage.Url);

            SPContentType listLevelCType = list.ContentTypes[newCType.Name];
            if (listLevelCType == null)
            {
                listLevelCType = list.ContentTypes.Add(newCType);
            }
        }
    }
}

Why, though? Why did I have to resort to that? <shakes fist angrily at Microsoft>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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