DFS Object service consumer #2b

In our previous conversation, we left with a missing header to resolve. This post will resolve this issue and wrap-up the sample DFS Object service consumer based on direct proxy via Visual Studio/WCF.

First, we need to introduce a new internal class, ServiceContextHeader, as follows:

internal class ServiceContextHeader : MessageHeader
{
    private string repositoryName = null;
    private string userName = null;
    private string password = null;

    public ServiceContextHeader(string repositoryName, string userName, string password)
    {
        this.repositoryName = repositoryName;
        this.userName = userName;
        this.password = password;
    }

    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        // Please note that there can be more to a ServiceContext instance than
        // what this intentionally simple example specifies. This message header
        // implementation doesn't cover all aspects of ServiceContext.

        // In order to proxy ServiceContext, you must first establish a service
        // reference to the DFS runtime Context Registry service.
        ServiceContext serviceContext = new ServiceContext();
        RepositoryIdentity repositoryIdentity = new RepositoryIdentity();
        repositoryIdentity.repositoryName = repositoryName;
        repositoryIdentity.userName = userName;
        repositoryIdentity.password = password;
        serviceContext.Identities = new Identity[1];
        serviceContext.Identities[0] = repositoryIdentity;

        // We haven't communicated with the DFS runtime Context Registry service;
        // so we don't have a token to "write," nor are we specifiying locale in 
        // our ServiceContext instance.

        StringWriter sw = new StringWriter();
        XmlSerializer xs = new XmlSerializer(typeof(ServiceContext));
        xs.Serialize(sw, serviceContext);
        String xml = sw.ToString();
        sw.Close();

        XmlDocument document = new XmlDocument();
        document.LoadXml(xml);

        foreach (XmlNode node in document.LastChild.ChildNodes)
        {
            XmlElement element = (XmlElement)node;
            XmlReader reader = new XmlNodeReader(element);
            writer.WriteNode(reader, true);
        }
    }

    public override string Name
    {
        get { return "ServiceContext"; }
    }

    public override string Namespace
    {
        get { return "http://context.core.datamodel.fs.documentum.emc.com/"; }
    }
}

Next, we need to “inject” this class into our application’s request message processing. So, we need wrap our original call to ObjectService.get() as follows:

DataPackage dataPackage = null;
using (OperationContextScope scope = new OperationContextScope(objectService.InnerChannel))
{
    OperationContext.Current.OutgoingMessageHeaders.Add(new ServiceContextHeader(repositoryName, userName, password));

    dataPackage = objectService.get(objectIdentitySet, operationOptions);
}

In a less contrived context (i.e. your production application), there are other approaches to be considered, and I recommend that you at least read Nick Allen’s post on the subject.

Now, let’s re-run the modified example and examine the SOAP request message:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Header>
        <ServiceContext xmlns="http://context.core.datamodel.fs.documentum.emc.com/">
            <Identities xsi:type="RepositoryIdentity" userName="_USER_" password="_PWD_" repositoryName="_DOCBASE_" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
        </ServiceContext>
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <get xmlns="http://core.services.fs.documentum.emc.com/">
            <forObjects isInternal="false" xmlns="">
                <Identities repositoryName="_DOCBASE_" valueType="OBJECT_ID" xmlns="http://core.datamodel.fs.documentum.emc.com/">
                    <ObjectId id="09003023800037c6"/>
                </Identities>
            </forObjects>
            <options xmlns="">
                <Properties isInternal="false" xmlns="http://core.datamodel.fs.documentum.emc.com/"/>
                <Profiles xsi:type="q1:PropertyProfile" isProcessIncludedUnknown="false" filterMode="SPECIFIED_BY_INCLUDE" xmlns="http://core.datamodel.fs.documentum.emc.com/" xmlns:q1="http://profiles.core.datamodel.fs.documentum.emc.com/">
                    <q1:IncludeProperties>object_name</q1:IncludeProperties>
                </Profiles>
            </options>
        </get>
    </s:Body>
</s:Envelope>

Much better! And, as important, the dialog reveals the same object name as the same example based on the DFS SDK .NET assemblies.

You can download the Visual Studio 2008-based solution for today’s (fully resolved) sample here.

I hope that this series of posts on how the EMC Documentum platform, and especially DFS, supports Microsoft-oriented developers has been helpful.

Cheers… :-)

Update 8/9/2008: There is actually one additional change that is required to properly retrieve the object’s name for presentation on the form. In the previous post, after calling ObjectService.get() and finding the “object_name” property, our form label was set as follows:

    lblObjName.Text = property.ToString();

However, this needs to change in order to avoid prefacing the string value with ToString() content:

    StringProperty s = property as StringProperty;
    lblObjName.Text = s.Value;

This sample has been updated to include this change. Kudos to John Sweeney for the assist.

OK, back to the Olympics… :-)

-Craig
http://craigrandall.net/
@craigsmusings