Sunday, August 16, 2009

ASP.Net Caching with Dynamics CRM Data

When writing websites that work with CRM data you have a couple options when it come to binding PickList attributes.  You can hard-code the options into list items.


        Account Relationship Type:
        <br />
        <asp:DropDownList runat="server" ID="accountRelationshipType">
            <asp:ListItem Value="1">Competitor</asp:ListItem>
            <asp:ListItem Value="2">Consultant</asp:ListItem>
            <asp:ListItem Value="3">Customer</asp:ListItem>
            <asp:ListItem Value="4">Investor</asp:ListItem>
            <asp:ListItem Value="5">Partner</asp:ListItem>
            <asp:ListItem Value="6">Influencer</asp:ListItem>
            <asp:ListItem Value="7">Press</asp:ListItem>
            <asp:ListItem Value="8">Prospect</asp:ListItem>
            <asp:ListItem Value="9">Reseller</asp:ListItem>
            <asp:ListItem Value="10">Supplier</asp:ListItem>
            <asp:ListItem Value="11">Vendor</asp:ListItem>
            <asp:ListItem Value="12">Other</asp:ListItem>
        </asp:DropDownList>



This option is tedious and it requires a code change if a system customizer adds another pick list value.  The second option is to hit the metadata service to retrieve the available options and then bind them to the dropdown list.  To pull back the metadata every time, performance is a concern, especially when you have multiple dropdown lists.  It is better to leverage ASP.Net caching to store the data.  You can set up a cache dependency to re-pull the data every X number of hours.


To get started you need to add a web reference to the MetaData web service.



The URL is http://andrewvpc:5555/mscrmservices/2007/metadataservice.asmx where andrewvpc is your servername and 5555 is your port number.  This does not require the uniquename as is necessary with the CRM service.



 


Setup MetaData Service Connection


Next, setup the connection by adding the following code.  The code is similar to connecting to the CRM service with a couple differences.


        private static String _org = System.Configuration.ConfigurationManager.AppSettings["CRMWSOrg"];
        private static String _userName = System.Configuration.ConfigurationManager.AppSettings["CRMWSUser"];
        private static String _password = System.Configuration.ConfigurationManager.AppSettings["CRMWSPassword"];
        private static String _domain = System.Configuration.ConfigurationManager.AppSettings["CRMWSDomain"];
        private static String CRM_WSURL = System.Configuration.ConfigurationManager.AppSettings["CRMWS"];
        private static String CRM_SERVER = System.Configuration.ConfigurationManager.AppSettings["CRMSERVER"];



        public static MetadataService.MetadataService InitiateMetaDataService(bool useSuperAdmin)
        {


            MetadataService.MetadataService service = new MetadataService.MetadataService();
            try
            {

                service.Url = CRM_SERVER + "metadataservice.asmx";

                if (useSuperAdmin)
                    service.Credentials = new NetworkCredential(_userName, _password, _domain);
                else
                    service.Credentials = System.Net.CredentialCache.DefaultCredentials;




                MetadataService.CrmAuthenticationToken token = new MetadataService.CrmAuthenticationToken();
                token.AuthenticationType = 0;
                token.OrganizationName = (string)System.Configuration.ConfigurationManager.AppSettings["CRMWSOrg"];


                service.CrmAuthenticationTokenValue = token;



                return service;
            }
            catch (Exception ex)
            {
                throw new Exception("Exception caught in InitiateMetaDataService: " + ex.Message);
            }
        }



 


Get PickList Data from MetaData Service


After connecting to the service, create a RetrieveAttributeRequest for a given entity and attribute.


        public static Option[] GetPickListValues(string entity, string attribute)
        {
            try
            {
                using (MetadataService.MetadataService service = CachingHelper.InitiateMetaDataService(true))
                {
                    RetrieveAttributeRequest req = new RetrieveAttributeRequest();
                    req.EntityLogicalName = entity;
                    req.LogicalName = attribute;
                    RetrieveAttributeResponse resp = service.Execute(req) as RetrieveAttributeResponse;
                    if (resp.AttributeMetadata as PicklistAttributeMetadata == null)
                        throw new ApplicationException("Not picklist attribute");



                    PicklistAttributeMetadata attributeMetaData = resp.AttributeMetadata as PicklistAttributeMetadata;
                    return attributeMetaData.Options;
                }
            }
            catch (SoapException ex2)
            {
                throw new Exception("Exception caught in GetPickListValues:  " + ex2.Detail.InnerText, ex2);



            }
            catch (Exception ex)
            {
                throw new Exception("Exception caught in GetPickListValues:  " + ex.Message);
            }
        }
 



Load Data from Cache


Use ASP.Net caching to store the data in the cache if it is expired.  Set a cache dependency of for X number of hours.  Retrieve the data from the cache and bind it to the drop down list.  The label is a little tricky because the label contains text for each culture.  The value is the int value associated with the PickList.

        public void LoadFromCache(string entityName, string attributeName, DropDownList list, int hoursToCache, Page page)
        {
            // If cache expired then reset in cache
            if (page.Cache[attributeName] == null)
            {
                // Get from CRM
                Option[] options = App_Code.CachingHelper.GetPickListValues(entityName, attributeName);
                page.Cache.Insert(attributeName, options, null, DateTime.Now.AddHours(hoursToCache), TimeSpan.Zero);
            }



            // Pull from cache
            Option[] optionsFromCache = page.Cache[attributeName] as Option[];
            list.Items.Clear();
            foreach (Option option in optionsFromCache)
            {
                string label = "";
                // Grab first label or add extra logic to grab the label for the
                // right culture
                if (option.Label.LocLabels.Length > 0)
                    label = option.Label.LocLabels[0].Label;



                list.Items.Add(new ListItem(label, option.Value.Value.ToString()));
            }
        }
       
    }


Create a Web Page


Create a web page and add a DropDownList .


Account Relationship Type:
<br />
<asp:DropDownList runat="server" ID="accountRelationshipType">
</asp:DropDownList>



 


Call Cached Data from Page


On page load bind the data to the DropDownList.


protected void Page_Load(object sender, EventArgs e)
        {
            if (!this.IsPostBack)
            {
                App_Code.CachingHelper helper = new CacheDemo.App_Code.CachingHelper();
                helper.LoadFromCache("account", "customertypecode", accountRelationshipType, 5, this);
            }
        }



 


Hit Run... ...  The data will be pulled from the CRM the first time and from the cache on subsequent calls.

No comments: