Using C# to get to MultiValuedProperty

Today's post is to address a question we dealt with fairly recently through a customer escalation. The customer was trying to figure out the best way to programmatically get access to the entries in a MultiValuedProperty collection returned as part of an Exchange object.

Specific example we'll use here: Read in one or more mailbox objects (using Get-Mailbox cmdlet) and then iterate across and output some properties. Since we're outputting "Name" and "EmailAddresses" properties here, one will be a singleton ("Name") and one will be a collection ("EmailAddresses").

To accomplish extracting this data, immediately the customer had driven straight into the Exchange.Management namespaces looking for Mailbox. But that's not the right approach. Vivek talks about why in this blog post, but the short version is that there's nothing to be gained by tying your code directly into Exchange (no useful public methods and limited benefit from the property strong-types) and quite a bit to be lost (you become tied to the assembly version, etc).

So, how then do you do it? Well, you use PSObject and non-Exchange types everywhere. There's a pretty good starter example in Technet: https://msdn2.microsoft.com/en-us/library/bb332449.aspx

Which leads to the customer's real question -- "I can get the object, but how do I drill into the strong-type properties that are of types like MultiValuedProperty?"

Good news! MultiValuedProperty type implements ICollection, so it's actually pretty simple. When you extract the property value, just cast it as ICollection and you're golden!

Here's some sample code (based on the Technet code) with a few comments added:

    RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
    PSSnapInException snapInException = null;
    PSSnapInInfo info = rsConfig.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out snapInException);
    Runspace myRunSpace = RunspaceFactory.CreateRunspace(rsConfig);
    myRunSpace.Open();

    Pipeline pipeline = myRunSpace.CreatePipeline();
    Command myCommand = new Command("Get-Mailbox");

    pipeline.Commands.Add(myCommand);

    Collection<PSObject> commandResults = pipeline.Invoke();

    // Ok, now we've got a bunch of mailboxes, cycle through them
    foreach (PSObject mailbox in commandResults)
    {
        //define which properties to get
        foreach (String propName in new string[] { "Name", "EmailAddresses" })
        {
            //grab the specified property of this mailbox
            Object objValue = mailbox.Properties[propName].Value;
            // is it a collection? "Name" isn't, but "EmailAddresses" is in this example
            if (objValue is ICollection)
            {
                ICollection collection = (ICollection)objValue;
                // Loop through each entry in the collection and output it
                foreach (object value in collection)
                {
                    Console.WriteLine(value.ToString());
                }
            }
            else
            {
                Console.WriteLine(objValue.ToString());
            }
        }
    }
    myRunSpace.Close();

Thanks to Mike Hamler for assistance with the generic solution!

Comments

  • Anonymous
    January 01, 2003
    An easy way to remove (some) entries from the autocompletion list in OWA 2007 Exchange Server Public

  • Anonymous
    January 01, 2003
    Reinhard - Nope, there are not cmdlets in Exchange which can give you access to the contacts stored within the mailbox (generally thought of as the Outlook contacts). The Exchange cmdlets only give access to the AD-stored objects. It's not inconceivable that there might be some way to script Outlook through COM or some other interface to get access to these items, or you could also probably access them through EWS (http://msdn.microsoft.com/en-us/library/exchangewebservices(EXCHG.80).aspx) -- of course both of these will be more difficult than having cmdlets at the ready.

  • Anonymous
    January 01, 2003
    Hi Wilfred - I don't have any of the real context of the support incident, so I was not aware of who the customer was or any of the machinations you went through to reach your solution. The back and forth between the product group and the support folks consisted primarily of discussing the best generic solution and documenting it. The goal of posting this here was more about providing details on the recommended solution to the public in a generic sense than about relating how we solved your specific issue. That's just the style I use on this blog... Glad you have reached a supported solution at this stage! Evan

  • Anonymous
    May 13, 2008
    I was the customer whom logged the case with Microsoft to get this resolved. There are a number of things incorrect in your blog entry.

  • I did not drive down into the mailbox. The method I used to get access to the mailbox was exactly as described above, I even provided that as example code.
  • The iteration over the collection of PSObject's was already there, just as the ToString calls on the property value.
  • The issue was that there was no documented way to get access to the strong typed objects. A ToString on the strong typed object returns the object type which does not help.
  • I provided a solution to get access to the objects using reflection. I was the one that was not happy with that solution due to performance constraints and the possible version dependency of that solution.
  • On one of the mailing lists the suggestion was made to link against the exchange dll directly which I tried that and never considered it a solution for the problem due to the fact that it depends on the version. After pushing for a supported and documented way to get access to the values the information about the ICollection was provided. This by itself is still not a solution because it is not documented anywhere and could change with a new version. A new KB document has been created, but not published yet, which does provide the same information as you have given above. With the comments from the development team that the ICollection is here to stay we now have a supported solution.
  • Anonymous
    May 23, 2008
    Hi. great tutorial thank you. Is there a possibility to get the mail contacts for a specific user which  are stored in the Mailbox Database.edb file ? I have to write an extension for exchange 2007 and have to check if the mail sender is already included in the "global" mail contacts (get-mailcontact) and in the user specific mail contacts generated in owa or outlook 2007 ?  Is it possible to get these values with powershell ? How can I access / set these values ?  Thank in advance for your  response.. Reinhard Wagner [reinhard at gmx.at]