Patan
Patan

Reputation: 17883

Updating Components using the Core Service in SDL Tridion 2011

I am updating a Component using Core Service in Tridion 2011.

The sample code is as follows,

string COMPONENT_URI = "tcm:8-674";
string SCHEMA_URI = "tcm:8-426-8";

ComponentData component = client.TryCheckOut(COMPONENT_URI, null) as ComponentData;

try
{
    Response.Write("<BR>" + component.Content);
    XDocument xdoc = XDocument.Parse(component.Content);
    var element = xdoc.Elements("first").Single();
    element.Value = "updated";
    xdoc.Save(component.Content);
    client.Save(component, null);
    Response.Write("<BR"+"SAVED");
}
catch (Exception ex)
{
    Response.Write("Unable to save comp" + ex.Message);
}

client.CheckIn(COMPONENT_URI, null);

I am getting following exception:

 Unable to save compSequence contains no elements 

Details:

first - name of the field in the component

Can any one help regarding this?

Thank you

Upvotes: 2

Views: 2757

Answers (3)

Miguel
Miguel

Reputation: 711

Sometimes, specially if you are synchronizing the content from a different repository that acts as the master, you can just reconstruct the component from scratch. In that case, here is a basic example of how to do that:

    public static void updateComponent()
    {
        string componentWdUrl = "/webdav/020%20Content/Building%20Blocks/Content/wstest/testComponent.xml";
        CoreServicesUtil coreServicesUtil = new CoreServicesUtil();
        coreServicesUtil.coreServiceClient.CheckOut(componentWdUrl, true, coreServicesUtil.readOptions);
        ComponentData componentData = coreServicesUtil.getComponentData(componentWdUrl);
        SchemaData schemaData = coreServicesUtil.getSchemaData(componentData.Schema.IdRef);
        componentData.Content = xmlUtil.GetNewXmlNode("Content", schemaData.NamespaceUri);
        componentData.Metadata = xmlUtil.GetNewXmlNode("Metadata", schemaData.NamespaceUri);
        componentData.AddSingleField("singlefield", "singlefield sample", schemaData.NamespaceUri);
        componentData = (ComponentData)coreServicesUtil.coreServiceClient.Save(componentData, coreServicesUtil.readOptions);
        coreServicesUtil.coreServiceClient.CheckIn(componentData.Id, coreServicesUtil.readOptions);
        coreServicesUtil.coreServiceClient.Close();
    }

More description about methods used in this sample are explained in the article:

Faulted State error while creating component with Core Service

Upvotes: 2

Frank van Puffelen
Frank van Puffelen

Reputation: 598817

The XML of a Component in Tridion is of the following format:

<Content xmlns="uuid:2607D20D-1B22-4994-98C1-66D9ACF85C20">
  <first>The value of my first field</first>
  <second>The value of my second field</second>
</Content>

Your relevant code snippet is:

var element = xdoc.Elements("first").Single();

This is failing to select an element, since it is:

  1. not providing a namespace to the selection
  2. only selecting direct children of the document root

You seem to expect that the default namespace will be automatically selected if you don't specify a namespace, which simply is not true. As soon as you have XML that deals with namespaces, every query will have to specify the correct namespace.

If you modify the code to deal with these two issues, it should look something like this:

XNamespace ns = xdoc.Root.GetDefaultNamespace();
var element = xdoc.Descendants(ns+"first").Single();

You might want to consider reading up on .NET handling of namespaces in XML and on XML namespaces in general, since this is a very common mistake that you simply need to get out of your system quickly.

People who have wanted to update the Component XML through the Core Service before you found the helper class given here useful.

In addition as Mihai points out the way you invoke XDocument.Save is wrong. It expects a file name as its parameter, while you are passing it the XML of your Component.

Upvotes: 13

Mihai Cădariu
Mihai Cădariu

Reputation: 2407

Your code attempts to save the XML DOM to a file.

XDocument.Save(string fileName) - Serialize this XDocument to a file, overwriting an existing file, if it exists.

You should use something like this:

using (var client = new SessionAwareCoreServiceClient(netTcpBinding, remoteAddress))
{
    ReadOptions readOptions = new ReadOptions();
    ComponentData component = client.Read(compTcmUri, readOptions) as ComponentData;
    XDocument dom = XDocument.Parse(component.Content);
    // do your modifications to dom
    component.Content = dom.ToString();
    component = client.Update(component, readOptions) as ComponentData;

    Console.WriteLine("Component updated: " + component.LocationInfo.WebDavUrl);
}

Upvotes: 3

Related Questions