Blog

Categorised by 'C#'.

  • I've been working with custom functionality for registering and authenticating external site users in Umbraco 13 using its Members feature.

    A custom Member Type was created so I could create field properties to specifically store all member registeration data. This consisted of Textboxes, Textareas and Dropdown fields.

    Getting values for fields in code is very straight-forward, but I encountered issues in when dealing with fields that consist of preset values, such as a Dropdown list of titles (Mr/Mrs/Ms/etc).

    Based on the Umbraco documentation for working with a Dropdown field, I should be able to get the selected value through this one line of code:

    @if (Model.HasValue("title"))
    {
        <p>@(Model.Value<string>("title"))</p>
    }
    

    When working with custom properties from a Member Type, the approach seems to be different. A GetValue() is the only accessor we have available to us to output a value - something we are already accustomed to working in Umbraco.

    IMember? member = memberService.GetByEmail("johndoe@gmail.com");
    string title = member.Properties["title"].GetValue()?.ToString(); // Output: "[\"Mr\"]"
    

    However, the value is returned as a serialized array. This is also the case when using the typed GetValue() accessor on the property:

    IMember? member = memberService.GetByEmail("johndoe@gmail.com");
    string title = member.GetValue<string>("title"); // Output: "[\"Mr\"]"
    

    Umbraco 13 - Dropdown Value From Custom Member Type Property

    The only way to get around this was to create a custom extension method to deserialize the string array so the value alone could be output:

    public static class MemberPropertyExtensions
    {
        /// <summary>
        /// Gets the selected value of a Dropdown property.
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        public static string? GetSelectedDropdownValue(this IProperty property)
        {
            if (property == null)
                return string.Empty;
    
            string? value = property?.GetValue()?.ToString();
    
            if (string.IsNullOrEmpty(value))
                return string.Empty;
    
            string[]? propertyArray = JsonConvert.DeserializeObject<string[]>(value);
    
            return propertyArray?.FirstOrDefault();
        }
    }
    

    It's a simple but effective solution. Now our original code can be updated by adding our newly created GetSelectedDropdownValue() method to the property:

    IMember? member = memberService.GetByEmail("johndoe@gmail.com");
    string title = member.Properties["title"].GetSelectedDropdownValue();
    

    Useful Information

  • Published on
    -
    3 min read

    C# Variable Type: To 'var', Or Not To 'var'

    This post has been in the works for some time in order to express my annoyance whenever I see every variable in a C# project declared with "var". This is simply laziness... I guess typing three letters is simpler than typing the actual variable type. Even so, I believe that this, among other things, contributes to readability issues.

    I am not completely opposed to its use, as I use it in places where I deem it acceptable and has no effect on readability. My biggest gripe is when an entire project is littered solely with this form of variable declaration. Whenever I come across a project like this, I have to run a Visual Studio tool that automatically changes variables to use an explicit type instead.

    Some may argue that readability is unaffected because you can obtain type information simply by hovering over any variable in Visual Studio. A perfectly valid point. However, things become more obfuscated when quickly glancing at some code via Notepad, Github.com, or during code reviews.

    From what I've seen on forums, there are various points of view, some of which I agree with and wanted to share my thoughts.

    Opinion 1: Saves Time Through Less Keystrokes

    List<ContactInfoProvider> contacts = new List<ContactInfoProvider>();
    // vs.
    var contacts = new List<ContactInfoProvider>();
    

    This assumes a developer is using an IDE with no intellisense capability. This shouldn't be a problem since intellisense will quickly resolve the class and types in use. According to some developers I work with, declaring long type names is messy and adds unnecessary extra noise.

    I don't mind this approach since the type in use is evident.

    Opinion 2: Readability Is Not An Issue

    To expand upon the point I made in my intro about code being obfuscated, lets take the following snippet of code:

    ...
    var contactInfo = ContactInfoProvider.GetContactInfo(token);
    ...
    

    I'm unable to see what type is being returned immediately unless I delve into the ContactInfoProvider class and view the method. "var" should never be used where the type is not known because it reduces recognition readability in code.

    Opinion 3: Use "var" On Basic Variable Types

    var maxRecords = 100;
    var price = 1.1;
    var successMessage = "Surinder thinks this is wrong!";
    var isSuccess = false;
    
    // vs.
    
    int maxRecords = 100;
    decimal price = 1.1;
    string successMessage = "Surinder thinks this is correct!";
    bool isSuccess = true;
    

    I did state earlier the use of "var" is acceptable where the type used is clearly visible. Basic variable types is an exception to the rule as it's completely unnecessary.

    Opinion 4: Encourages Descriptive Variable Names

    var productItem = new ProductInfo()
    // vs.
    ProductInfo pi = new ProductInfo()
    

    This is where I disagree. Descriptive variable names should always be used, regardless of whether the variable is declared explicitly or implicitly. In my time as a developer, I've seen some dreadful variable names in both instances.

    Conclusion

    There is no right or wrong way to use "var," and it is entirely up to personal preference. I only use "var" when I'm writing quick test code, playing with LINQ, or when I'm not sure what the outcome of a method is when dealing with external API libraries.

    Even though I relish learning new code notations and refactoring, I will continue to use "var" very sparingly (if at all!) in my own code. But the overall use of it just doesn't sit right with me. Strict typing ensures the most clearest approach, so a developer knows exactly what is going to happen once it is typed.

    We should always aim to make our code as clear as possible. It's difficult enough to understand explicitly set variables that aren't self-descriptive without having to add "var" to the mix.

    Forrest Gump - Thats All I Have To Say About That
  • I decided to write this post to primarily act as a reminder to myself when dealing with programmatically creating content pages in Umbraco and expanding upon my previous post on setting a dropdownlist in code. I have been working on a piece of functionality where I needed to develop an import task to pull in content from a different CMS platform to Umbraco that encompassed the use of different field-types, such as:

    • Textbox
    • Dropdownlist
    • Media Picker
    • Content Picker

    It might just be me, but I find it difficult to find solutions to Umbraco related problems I sometimes face. This could be due to results returned in search engines reference forum posts for older versions of Umbraco that are no longer compatible in the version I'm working in (version 8).

    When storing data in the field types listed (above), I encountered issues when trying to store values in all field types except “Textbox”. The other fields either required some form of JSON structure or Udi to be parsed.

    Code

    My code contains three methods:

    1. SetPost - to create a new blog post, or update an existing blog post if one already exists.
    2. GetAuthorIdByName - uses Umbraco Examine Search Index to get back an Author document and return the Udi.
    3. GetUmbracoMedia - uses the internal Examine Search Index to return details of a file in a form that will be acceptable to store within a Media Picker content field.

    The SetPost method consists of a combination of fields required by my Blog Post document, the primary ones being:

    • Blog Post Type (blogPostType) - Dropdownlist
    • Blog Post Author (blogPostAuthor) - Content Picker
    • Image (image) - Media Picker
    • Categories (blogPostCategories) - Tags
    /// <summary>
    /// Creates or updates an existing blog post.
    /// </summary>
    /// <param name="title"></param>
    /// <param name="summary"></param>
    /// <param name="postDate"></param>
    /// <param name="type"></param>
    /// <param name="imageUrl"></param>
    /// <param name="body"></param>
    /// <param name="categories"></param>
    /// <param name="authorId"></param>
    /// <returns></returns>
    private static PublishResult SetPost(string title, 
                                        string summary, 
                                        DateTime postDate, 
                                        string type, 
                                        string imageUrl, 
                                        string body, 
                                        List<string> categories = null, 
                                        string authorId = "")
    {
        PublishResult publishResult = null;
        IContentService contentService = Current.Services.ContentService;
        ISearcher searchIndex = ExamineUtility.GetIndex().GetSearcher();
    
        // Get blog post by it's page title.
        ISearchResult blogPostSearchItem = searchIndex.CreateQuery()
                                        .Field("pageTitle", title.TrimEnd())
                                        .And()
                                        .NodeTypeAlias("blogPost")
                                        .Execute(1)
                                        .FirstOrDefault();
    
        bool existingBlogPost = blogPostSearchItem != null;
    
        // Get the parent section where the new blog post will reside, in this case Blog Index.
        IContent blogIndex = contentService.GetPagedChildren(1099, 0, 1, out _).FirstOrDefault();
    
        if (blogIndex != null)
        {
            IContent blogPostContent;
    
            // If blog post doesn't already exist, then create a new node, otherwise retrieve existing node by ID to update.
            if (!existingBlogPost)
                blogPostContent = contentService.CreateAndSave(title.TrimEnd(), blogIndex.Id, "blogPost");
            else
                blogPostContent = contentService.GetById(int.Parse(blogPostSearchItem.Id));
    
            if (!string.IsNullOrEmpty(title))
                blogPostContent.SetValue("pageTitle", title.TrimEnd());
    
            if (!string.IsNullOrEmpty(summary))
                blogPostContent.SetValue("pageSummary", summary);
    
            if (!string.IsNullOrEmpty(body))
                blogPostContent.SetValue("body", body);
                    
            if (postDate != DateTime.MinValue)
                blogPostContent.SetValue("blogPostDate", postDate);
    
            // Set Dropdownlist field.
            if (!string.IsNullOrEmpty(type))
                blogPostContent.SetValue("blogPostType", JsonConvert.SerializeObject(new[] { type }));
    
            // Set Content-picker field by parsing a "Udi". Reference to an Author page. 
            if (authorId != string.Empty)
                blogPostContent.SetValue("blogPostAuthor", authorId);
    
            // Set Media-picker field.
            if (imageUrl != string.Empty)
            {
                string umbracoMedia = GetUmbracoMedia(imageUrl);
    
                // A stringified JSON object is required to set a Media-picker field.
                if (umbracoMedia != string.Empty)
                    blogPostContent.SetValue("image",  umbracoMedia);
            }    
    
            // Set tags.
            if (categories?.Count > 0)
                blogPostContent.AssignTags("blogPostCategories", categories);
    
            publishResult = contentService.SaveAndPublish(blogPostContent);
        }
    
        return publishResult;
    }
    
    /// <summary>
    /// Gets UDI of an author by fullname.
    /// </summary>
    /// <param name="fullName"></param>
    /// <returns></returns>
    private static string GetAuthorIdByName(string fullName)
    {
        if (!string.IsNullOrEmpty(fullName))
        {
            ISearcher searchIndex = ExamineUtility.GetIndex().GetSearcher();
    
            ISearchResult authorSearchItem = searchIndex.CreateQuery()
                                            .Field("nodeName", fullName)
                                            .And()
                                            .NodeTypeAlias("author")
                                            .Execute(1)
                                            .FirstOrDefault();
    
            if (authorSearchItem != null)
            {
                UmbracoHelper umbracoHelper = Umbraco.Web.Composing.Current.UmbracoHelper;
                return Udi.Create(Constants.UdiEntityType.Document, umbracoHelper.Content(authorSearchItem.Id).Key).ToString();
            }
        }
    
        return string.Empty;
    }
    
    /// <summary>
    /// Gets the umbracoFile of a media item by filename.
    /// </summary>
    /// <param name="fileName"></param>
    /// <returns></returns>
    private static string GetUmbracoMedia(string fileName)
    {
        if (!string.IsNullOrEmpty(fileName))
        {
            ISearcher searchIndex = ExamineUtility.GetIndex("InternalIndex").GetSearcher();
    
            ISearchResult imageSearchItem = searchIndex.CreateQuery()
                                            .Field("umbracoFileSrc", fileName)
                                            .Execute(1)
                                            .FirstOrDefault();
    
            if (imageSearchItem != null)
            {
                List<Dictionary<string, string>> imageData = new List<Dictionary<string, string>> {
                        new Dictionary<string, string>() {
                            { "key", Guid.NewGuid().ToString() },
                            { "mediaKey", imageSearchItem.AllValues["__Key"].FirstOrDefault().ToString() },
                            { "crops", null },
                            { "focalPoint", null }
                    }
                };
    
                return JsonConvert.SerializeObject(imageData);
            }
        }
    
        return string.Empty;
    }
    

    Usage Example - Iterating Through A Dataset

    In this example, I'm iterating through a dataset of posts and parsing the field value to each parameter of the SetPost method.

    ...
    ...
    ...
    SqlDataReader reader = sqlCmd.ExecuteReader();
    
    if (reader.HasRows)
    {
        while (reader.Read())
        {
            SetPost(reader["BlogPostTitle"].ToString(),
                    reader["BlogPostSummary"].ToString(),
                    DateTime.Parse(reader["BlogPostDate"].ToString()),
                    reader["BlogPostType"].ToString(),
                    reader["BlogPostImage"].ToString(),
                    reader["BlogPostBody"].ToString(),
                    new List<string>
                    {
                            "Category 1",
                            "Category 2",
                            "Category 3"
                    },
                    GetAuthorIdByName(reader["BlogAuthorName"].ToString()));
        }
    }
    ...
    ...
    ...
    

    Use of Umbraco Examine Search

    One thing to notice is that when I’m retrieving the parent page to where the new page will reside or checking for a page or media file, Umbraco Examine Search Index is used. I find querying the search index is the most efficient way to return data without consistently hitting the database - ideal for when carrying out a repetitive task like an import.

    In my code samples, I'm using a custom ExamineUtility class to retrieve the search index in a more condensed and tidy manner:

    public class ExamineUtility
    {
        /// <summary>
        /// Get Examine search index.
        /// </summary>
        /// <param name="defaultIndexName"></param>
        /// <returns></returns>
        public static IIndex GetIndex(string defaultIndexName = "ExternalIndex")
        {
            if (!ExamineManager.Instance.TryGetIndex(defaultIndexName, out IIndex index) || !(index is IUmbracoIndex))
                throw new Exception("Examine Search Index could not be found.");
    
            return index;
        }
    }
    

    Conclusion

    Hopefully, the code I have demonstrated in this post will give a clearer idea on how to programmatically work with content pages using a combination of different field types. For further code samples on working with different field types, take a look at the "Built-in Umbraco Property Editors" documentation.

  • Earlier this week I wrote about the reasons to why I decided to use Cloudflare for my website. I've been working on utilising Cloudflare's API to purge the cache on demand for when files need to be updated within the CDN. To do this, I decided to write a method that will primarily use one API endpoint - /purge_cache. This endpoint allows a maximum of 30 URL's at one time to be purged, which is flexible enough to fit the majority of day-to-day use cases.

    To communicate with the API, we need to provide three pieces of information:

    1. Account Email Address
    2. Zone ID
    3. API Key

    The last two pieces of information can be found within the dashboard of your Cloudflare account.

    Code - CloudflareCacheHelper Class

    The CloudflareCacheHelper class consists of a single method PurgeSelectedFiles() and the following class objects used for serializing and deserializing our responses from API requests:

    • CloudflareFileInfo
    • CloudflareZone
    • CloudflareResultInfo
    • CloudflareResponse

    Not all the properties within each of the class objects are being used at the moment based on the requests I am making. But the CloudflareCacheHelper class will be updated with more methods as I delve further into Cloudflare's functionality.

    public class CloudflareCacheHelper
    {
        public string _userEmail;
        public string _apiKey;
        public string _zoneId;
    
        private readonly string ApiEndpoint = "https://api.cloudflare.com/client/v4";
    
        /// <summary>
        /// By default the Cloudflare API values will be taken from the Web.Config.
        /// </summary>
        public CloudflareCacheHelper()
        {
            _apiKey = ConfigurationManager.AppSettings["Cloudflare.ApiKey"];
            _userEmail = ConfigurationManager.AppSettings["Cloudflare.UserEmail"];
            _zoneId = ConfigurationManager.AppSettings["Cloudflare.ZoneId"];
        }
    
        /// <summary>
        /// Set the Cloudflare API values explicitly.
        /// </summary>
        /// <param name="userEmail"></param>
        /// <param name="apiKey"></param>
        /// <param name="zoneId"></param>
        public CloudflareCacheHelper(string userEmail, string apiKey, string zoneId)
        {
            _userEmail = userEmail;
            _apiKey = apiKey;
            _zoneId = zoneId;
        }
            
        /// <summary>
        /// A collection of file paths (max of 30) will be accepted for purging cache.
        /// </summary>
        /// <param name="filePaths"></param>
        /// <returns>Boolean value on success or failure.</returns>
        public bool PurgeSelectedFiles(List<string> filePaths)
        {
            CloudflareResponse purgeResponse = null;
    
            if (filePaths?.Count > 0)
            {
                try
                {
                    HttpWebRequest purgeRequest = WebRequest.CreateHttp($"{ApiEndpoint}/zones/{_zoneId}/purge_cache");
                    purgeRequest.Method = "POST";
                    purgeRequest.ContentType = "application/json";
                    purgeRequest.Headers.Add("X-Auth-Email", _userEmail);
                    purgeRequest.Headers.Add("X-Auth-Key", _apiKey);
    
                    #region Create list of Files for Submission In The Structure The Response Requires
    
                    CloudflareFileInfo fileInfo = new CloudflareFileInfo
                    {
                        Files = filePaths
                    };
    
                    byte[] data = Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(fileInfo));
    
                    purgeRequest.ContentLength = data.Length;
    
                    using (Stream fileStream = purgeRequest.GetRequestStream())
                    {
                        fileStream.Write(data, 0, data.Length);
                        fileStream.Flush();
                    }
    
                    #endregion
    
                    using (WebResponse response = purgeRequest.GetResponse())
                    {
                        using (StreamReader purgeStream = new StreamReader(response.GetResponseStream()))
                        {
                            string responseJson = purgeStream.ReadToEnd();
    
                            if (!string.IsNullOrEmpty(responseJson))
                                purgeResponse = JsonConvert.DeserializeObject<CloudflareResponse>(responseJson);
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
    
                return purgeResponse.Success;
            }
    
            return false;
        }
    
        #region Cloudflare Class Objects
    
        public class CloudflareFileInfo
        {
            [JsonProperty("files")]
            public List<string> Files { get; set; }
        }
    
        public class CloudflareZone
        {
            [JsonProperty("id")]
            public string Id { get; set; }
    
            [JsonProperty("type")]
            public string Type { get; set; }
    
            [JsonProperty("name")]
            public string Name { get; set; }
    
            [JsonProperty("content")]
            public string Content { get; set; }
    
            [JsonProperty("proxiable")]
            public bool Proxiable { get; set; }
    
            [JsonProperty("proxied")]
            public bool Proxied { get; set; }
    
            [JsonProperty("ttl")]
            public int Ttl { get; set; }
    
            [JsonProperty("priority")]
            public int Priority { get; set; }
    
            [JsonProperty("locked")]
            public bool Locked { get; set; }
    
            [JsonProperty("zone_id")]
            public string ZoneId { get; set; }
    
            [JsonProperty("zone_name")]
            public string ZoneName { get; set; }
    
            [JsonProperty("modified_on")]
            public DateTime ModifiedOn { get; set; }
    
            [JsonProperty("created_on")]
            public DateTime CreatedOn { get; set; }
        }
    
        public class CloudflareResultInfo
        {
            [JsonProperty("page")]
            public int Page { get; set; }
    
            [JsonProperty("per_page")]
            public int PerPage { get; set; }
    
            [JsonProperty("count")]
            public int Count { get; set; }
    
            [JsonProperty("total_count")]
            public int TotalCount { get; set; }
        }
    
        public class CloudflareResponse
        {
            [JsonProperty("result")]
            public CloudflareZone Result { get; set; }
    
            [JsonProperty("success")]
            public bool Success { get; set; }
    
            [JsonProperty("errors")]
            public IList<object> Errors { get; set; }
    
            [JsonProperty("messages")]
            public IList<object> Messages { get; set; }
    
            [JsonProperty("result_info")]
            public CloudflareResultInfo ResultInfo { get; set; }
        }
    
        #endregion
    }
    

    Example - Purging Cache of Two Files

    A string collection of URL's can be passed into the method to allow for the cache of a batch of files to be purged in a single request. If all goes well, the success response should be true.

    CloudflareCacheHelper cloudflareCache = new CloudflareCacheHelper();
    
    bool isSuccess = cloudflareCache.PurgeSelectedFiles(new List<string> {
                                        "https://www.surinderbhomra.com/getmedia/7907d934-805f-4bd3-86e7-a6b2027b4ba6/CloudflareResponseMISS.png",
                                        "https://www.surinderbhomra.com/getmedia/89679ffc-ca2f-4c47-8d41-34a6efdf7bb8/CloudflareResponseHIT.png"
                                    });
    

    Rate Limits

    The Cloudflare API sets a maximum of 1,200 requests in a five minute period. Cache-Tag purging has a lower rate limit of up to 2,000 purge API calls in every 24 hour period. You may purge up to 30 tags in one API call.

  • I have created a helper class that will allow me to consume any XML or JSON request for deserialization into a class object. As you can see from the code below, the GetJsonRequest() and GetXmlRequest() methods allow you to pass an unknown type as well as the URL to where you are getting your request from. This makes things very straight-forward when you want to easily strongly type the data.

    public class ApiWebRequestHelper
    {
        /// <summary>
        /// Gets a request from an external JSON formatted API and returns a deserialized object of data.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="requestUrl"></param>
        /// <returns></returns>
        public static T GetJsonRequest<T>(string requestUrl)
        {
            try
            {
                WebRequest apiRequest = WebRequest.Create(requestUrl);
                HttpWebResponse apiResponse = (HttpWebResponse)apiRequest.GetResponse();
    
                if (apiResponse.StatusCode == HttpStatusCode.OK)
                {
                    string jsonOutput;
                    using (StreamReader sr = new StreamReader(apiResponse.GetResponseStream()))
                        jsonOutput = sr.ReadToEnd();
                        
                    var jsResult = JsonConvert.DeserializeObject<T>(jsonOutput);
    
                    if (jsResult != null)
                        return jsResult;
                    else
                        return default(T);
                }
                else
                {
                    return default(T);
                }
            }
            catch (Exception ex)
            {
                // Log error here.
    
                return default(T);
            }
        }
    
        /// <summary>
        /// Gets a request from an external XML formatted API and returns a deserialized object of data.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="requestUrl"></param>
        /// <returns></returns>
        public static T GetXmlRequest<T>(string requestUrl)
        {
            try
            {
                WebRequest apiRequest = WebRequest.Create(requestUrl);
                HttpWebResponse apiResponse = (HttpWebResponse)apiRequest.GetResponse();
    
                if (apiResponse.StatusCode == HttpStatusCode.OK)
                {
                    string xmlOutput;
                    using (StreamReader sr = new StreamReader(apiResponse.GetResponseStream()))
                        xmlOutput = sr.ReadToEnd();
    
                    XmlSerializer xmlSerialize = new XmlSerializer(typeof(T));
    
                    var xmlResult = (T)xmlSerialize.Deserialize(new StringReader(xmlOutput));
    
                    if (xmlResult != null)
                        return xmlResult;
                    else
                        return default(T);
                }
                else
                {
                    return default(T);
                }
            }
            catch (Exception ex)
            {
                // Log error here.
                return default(T);
            }
        }
    }
    

    The ApiWebRequestHelper class relies on the following namespaces:

    • Newtonsoft Json
    • System.Xml.Serialization
    • ​​System.IO;

    The ApiWebRequestHelper can be used in the following way:

    // Get Json Request
    ApiWebRequestHelper.GetJsonRequest<MyCustomJsonClass>("http://www.surinderbhomra.com/api/result.json");
    
    // Get XML Request
    ApiWebRequestHelper.GetXmlRequest<MyCustomXMLClass>("http://www.surinderbhomra.com/api/result.xml");
    
  • Today, I stumbled across a really neat feature in Visual Studio 2015 that gives you the ability to create a strongly-typed C# class directly into your class library. I'm amazed that I've happened to overlook this key feature since Visual Studio 2012!

    Better late than never.

    In the past, when consuming large JSON data-structures, I normally head off to http://json2csharp.com to help me get the initial class objects generated, which works a treat. I've blogged about my experiences using json2csharp here.

    The strongly-type class generator feature is hidden away in a place I would never have thought to look - Edit > Paste Special, where you will be given the option to either generate a class object based on XML or JSON. But it's in there for a reason.

    Paste Special - To Json or XML Class

    All that needs to be done now is to either copy some XML or JSON ready to paste into a newly created class. I do find the generated output quite untidy, but this is a great starting point to generating complex data structures.

  • Web Configuration Snippet When working on large projects whether it be websites or software applications, I like to try and make sure that settings from within my app/web configuration files are not only easily accessible within code, but also maintainable for future updates.

    Over the years, I have tried different approaches in an attempt to streamline how I use ASP.NET's configuration files within my day-to-day development and this is what I currently do.

    Group and Alphabetise

    I have started to sort all my settings alphabetically and group common functions/settings together. This alone makes navigating through a large configuration file much more easier.

    In addition, having a standard in-house development style and agreeing with your fellow developers how sections within the configuration file is expected to be structured can be useful. For example, on web projects I've worked on, all key sections are  sorted in the following order:

    1. configSections
    2. appSettings
    3. connectionStrings
    4. system.web
    5. customErrors
    6. system.webServer
    7. locations

    Common Naming Conventions

    I like to name my appsettings in the following format: "<Setting-Group>.<Name>". So if I were to add appsettings that related to Twitter, it would look something as the following:

    <add key="Twitter.ApiUrl" value="https://api.twitter.com/1.1/statuses/show.json" />
    <add key="Twitter.ApiKey" value="" />
    <add key="Twitter.ApiSecret" value="" />
    <add key="Twitter.AccessToken" value="" />
    <add key="Twitter.AccessTokenSecret" value="" />
    <add key="Twitter.Username" value="shomra" />
    

    This provides a simple and descriptive approach to breaking down all settings into manageable chunks.

    Strongly-Type AppSettings

    One thing that truly annoyed me when I first started .NET development is calling configuration values from within C# code. Not only did you have to write out (the very long-winded!) "ConfigurationManager.AppSettings["Twitter.Username"]", but also cast the value to a specific type.

    Using the "ConfigurationManager.AppSettings[]" call is awful. It creates the potential for typo's that aren't caught by the compiler and there's no nice intellisense to make our coding easier.

    I create a static configuration wrapper class to strongly-type all my config settings. The way I name my settings (as you can see from the "Common Naming Conventions" section) compliments the structure of my wrapper class.

    public class Config
    {
        public static class Social
        {
            #region Twitter
    
            public static class Twitter
            {
                public static string TwitterApiUrl
                {
                    get
                    {
                        return ConfigurationManager.AppSettings["Twitter.ApiUrl"];
                    }
                }
    
                public static string TwitterUsername
                {
                    get
                    {
                        return ConfigurationManager.AppSettings["Twitter.Username"];
                    }
                }
    
                public static string TwitterApiKey
                {
                    get
                    {
                        return ConfigurationManager.AppSettings["Twitter.ApiKey"];
                    }
                }
    
                public static string TwitterApiSecret
                {
                    get
                    {
                        return ConfigurationManager.AppSettings["Twitter.ApiSecret"];
                    }
                }
    
                public static string TwitterAccessToken
                {
                    get
                    {
                        return ConfigurationManager.AppSettings["Twitter.AccessToken"];
                    }
                }
    
                public static string TwitterAccessTokenSecret
                {
                    get
                    {
                        return ConfigurationManager.AppSettings["Twitter.AccessTokenSecret"];
                    }
                }
            }
    
            #endregion
        }
    }
    

    Admittingly, this can be quite time-consuming but when compared to the long-term benefits this has saved me a lot of headache.

    What Else Could Be Done?

    I've seen some developers use Applications Settings Architecture within the .NET framework when building Windows applications by creating a "Settings" file within the project.

    The only downside I can see with this approach is that all config values declared within the "Settings" file will be compiled at runtime and you lose the flexibility to change configuration values externally. Here's a nice StackOverflow post that describes this method in greater detail.

    If any of you readers have further suggestions or advice, I am all ears! :-)

    The Future...

    As Nick Dyer quite rightly pointed out to me yesterday on Google+, that our beloved web.config file will no longer form part of the applications we create in ASP.NET 5. We will have the freedom to create an applications configuration the way we want, simplifying the whole process.

    As I understand, there will be support for creating configuration inputs that can be placed inside JSON, XML and INI files using the new IConfiguration and ConfigurationModel sources.

    Sounds very promising.

  • Published on
    -
    1 min read

    Use Your Strings Wisely

    When I was first learning to code, I was always told to use my strings in applications wisely. It's very easy to use strings without much thought. I think strings are forgiving compared to any other data type...too forgiving.

    I was going to write a full blown blog post on the best way to use the string data type, but I found a good blog post that explains everything and I will use for reference when guiding new developers to my somewhat infinite wisdom. :-)

    Zeeshan Umar, wrote a very interesting blog post a few years ago (but still relevant) that I only happened to find, explaining some new interesting ways on how to use strings efficiently. The following approach caught my eye:

    string errorCode1 = "ec-1001";
    
    if (String.Compare(errorCode1,"ec-1001",true) == 0)
    {
       //Do something...
    }
    

    The String.Compare constantly stares me in the face via Visual Studio intellisense but never thought of using it. I will be using this approach if I need to check a string whilst using an conditional statement.

    If you happen to find anymore interesting ways to efficiently use strings, please leave a comment.

  • In nearly all my previous projects I've worked on required some kind of manipulation with reading some form of JSON data.

    To allow better integration into my application, I always create strongly-typed classes that mirror the structure of the incoming data feed. This in turn allows me to serialize the JSON and select specific properties I require. But sometimes, its quite difficult and confusing to the exact class structure right, especially when the JSON contains lots of fields of data.

    When I find this is the case, I use json2csharp.com. By simply pasting the contents of your JSON, the site does all the hard work and creates all the strongly-typed classes for you.

  • C# In Depth Third EditionWhen working as a programmer, it's really easy to continue coding in the same manner you have done since you picked up a language and made your first program.

    The saying: "Why fix it if it ain't broken?" comes to mind...

    I for one sometimes fail to move with the times (unknowingly to me) and find new and better ways of coding. It's only on the off chance I get introduced to different approaches through my work colleague or whilst Googling for an answer to one of my coding queries.

    After reading some rave reviews on C# In Depth, written by the one and only Stackoverflow god: Jon Skeet. I decided to part with my hard earned money and make a purchase.

    C# In Depth is different from other programming books I've read on C#. In fact it's really good and don't let the title of the book deter you. The contents is ideal for novice and semi-experienced programmers.

    Firstly, you start off by being shown code samples on how C# has evolved through its iterations (v1 - v4). In most cases I gave myself a gratifying pat on the back when I noticed the approaches I've taken in my own projects utilised practises and features of the current language. ;-)

    Secondly, unlike some programming books I've read in the past, it's not intimidating to read at all. Jon Skeet really has a great way to talk about some concepts I find difficult to comprehend in a clear a meaningful way, so I could utilise these concepts within my current applications.

    The only minor niggle I have is that there were a few places where I would have liked specific chapters to go into more detail. On the other hand, it gave me the opportunity to research the nitty-gritty details for myself.

    Since I purchased this book, I found myself referencing it many times and appreciating what C# has to offer along with it misconstrued and underused features.

    All in all, the author truly has a gift in clearly demonstrating his understanding on the subject with finesse and if I am able to comprehend even one-tenth of his knowledge, I will be a happy man.