I have written a small program which has 2 main classes : ServerSideMMS: This can access the SharePoint Managed Metadata Services, but only from the server which is running SharePointClientSideMMS: This can access the SharePoint Managed Metadata Services from any client machineThese can both be used to access MMS services and retrieve the data. In my case, I only need to grab the actual set of terms (which is the lowest level of metadata).Server    using Microsoft.SharePoint;    using Microsoft.SharePoint.Taxonomy;    namespace CallTaxonomyWebService    {        /// <summary>        /// A class used for retrieving MMS data. This must be run on the server        /// which is hosting the metadata.        /// </summary>        class ServerSideMMS        {            private const string ManagedMetadataService = Managed Metadata Service;            private TaxonomySession Taxonomy;            private SPSite Site;            /// <summary>            /// The constructor assigns the given SPSite and the resulting TaxonomySession            /// to some private fields.             /// </summary>            /// <param name=spSite></param>            public ServerSideMMS(string spSite)            {                Site = new SPSite(spSite);                Taxonomy = new TaxonomySession(Site);            }            /// <summary>            /// Get all the terms which are stored in the given termset under the given group            /// </summary>            /// <param name=groupName></param>            /// <param name=setName></param>            /// <returns></returns>            public TermCollection GetTermSet(string groupName, string setName)            {                var group = GetGroup(groupName);                var termset = group.TermSets[setName];                return termset.Terms;            }            /// <summary>            /// Returns an MMS group of the given name            /// </summary>            /// <param name=groupName></param>            /// <returns></returns>            public Group GetGroup(string groupName)            {                var Mms = Taxonomy.TermStores[ManagedMetadataService];                return Mms.Groups[groupName];            }        }    }ClientI wasn't particularly happy with the outcome of this class. As you can see I had to split the TermSet retrieval into 2 classes, because I wasn't sure how to create and return an empty TermCollection object in case something wasn't found, so I resorted to just assigning this to a class field and returning it from the main GetTermSet method :     using Microsoft.SharePoint.Client;    using Microsoft.SharePoint.Client.Taxonomy;    namespace CallTaxonomyWebService    {        /// <summary>        /// A class used for retrieving MMS data. This can be run from a client machine.        /// Because of the more complicated nature of these queryies (when compared to         /// the server-side calls), I have defined a few private fields to store        /// return data.         ///         /// For more on what we can do with CSOM and MMS, see https://github.com/OfficeDev/PnP/tree/dev and        /// https://msdn.microsoft.com/en-us/library/office/dn904534.aspx.         /// </summary>        class ClientSideMMS        {            private TaxonomySession Taxonomy;            private ClientContext Context;            private TermStore Store;            private TermGroup Group;            private TermCollection Collection;            /// <summary>            /// The constructor sets and validates the ClientContext before loading            /// the required elements of each Store, Group, TermSet and Term. This can            /// be amended if we every want to retrieve more data but for now I am             /// just getting the names of each.             /// </summary>            /// <param name=spSite></param>            public ClientSideMMS(string spSite)            {                Context = new ClientContext(spSite)                    { AuthenticationMode = ClientAuthenticationMode.Default};                Taxonomy = TaxonomySession.GetTaxonomySession(Context);                Store = Taxonomy.GetDefaultSiteCollectionTermStore();                  Context.Load(Store,                        store => store.Name,                        store => store.Groups.Include(                            group => group.Name,                            group => group.TermSets.Include(                                termSet => termSet.Name,                                termSet => termSet.Terms.Include(                                    term => term.Name)                            )                        )                );                Context.ExecuteQuery();            }            /// <summary>            /// This returns the private Set field after calling LoadTermSet (which assigns the             /// requested TermSet to Set)            /// </summary>            /// <param name=groupName></param>            /// <param name=setName></param>            /// <returns></returns>            public TermCollection GetTermSet(string groupName, string setName)            {                LoadTermSet(groupName, setName);                return Collection;            }            /// <summary>            /// Iterate through each Group until we find the right one (Name = groupName). Then             /// find the correct TermSet in that group and assign the Set field to that value.            ///             /// This is structured like this to avoid local return variables ...             /// </summary>            /// <param name=groupName></param>            /// <param name=setName></param>            private void LoadTermSet(string groupName, string setName)            {                foreach (var group in Store.Groups)                {                    if (group.Name == groupName)                    {                        Group = group;                        foreach (var set in group.TermSets)                        {                            if (set.Name == setName)                            {                                // I would much rather this function just return the Collection,                                // but as I can't guaruntee that a collection will be found, this                                // could cause an error, hence the private field ...                                 Collection = set.Terms;                                return;                            }                        }                    }                }            }        }    }Calling the classes    using System.Linq;    namespace CallTaxonomyWebService    {        static class Program        {            private const string site = @http://mysharepointsite/;            private static void Main(string[] args)            {                var mms = new ClientSideMMS(site);                var termcollection = mms.GetTermSet(People, Department);                var localTerms = termcollection.Select(term => term.Name).ToList();                var remoteMms = new ServerSideMMS(site);                var remoteCollection = remoteMms.GetTermSet(People, Department);                var remoteTerms = remoteCollection.Select(term => term.Name).ToList();            }        }    }Any feedback is much appreciated, particularly in relation to my use of a private field instead of a return type to try and avoid NullRefernceExceptions in the client side class. For example, would it be better to try and initialise an empty return value inside the LoadTermSet method and just return that at the end?
Accessing SharePoint's Managed Metadata TermSets Locally and Remotely
c#;server;web services;client;sharepoint
private TaxonomySession Taxonomy;private SPSite Site;These fields should be readonly. Also, private fields usually use camelCase, not PascalCase./// <param name=groupName></param>/// <param name=setName></param>/// <returns></returns>There is no reason to have empty documentation comments. Either fill them in with useful information, or remove them.var Mms = Taxonomy.TermStores[ManagedMetadataService];Local variables also usually use camelCase.As you can see I had to split the TermSet retrieval into 2 [methods], because I wasn't sure how to create and return an empty TermCollection object in case something wasn't found, so I resorted to just assigning this to a class field and returning it from the main GetTermSet methodThis approach is wrong. If you call GetTermSet for the second time and nothing is found, it will return the previous result.If you want to return null in the case when nothing is found (i.e. the outer loop completes without returning), then just do that. There also isn't any reason to assign to the Group field, or even to have it.public TermCollection GetTermSet(string groupName, string setName){    foreach (var group in store.Groups)    {        if (group.Name == groupName)        {            foreach (var set in group.TermSets)            {                if (set.Name == setName)                {                    return set.Terms;                }            }        }    }    return null;}Or even better, use LINQ:public TermCollection GetTermSet(string groupName, string setName){    var query = from g in store.Groups                where g.Name == groupName                from set in g.TermSets                where set.Name == setName                select set.Terms;    return query.FirstOrDefault();}