ASP.NET Developers Who Use Eval()... Evaluate Yourself!

The title of this post might seem a tad extreme, but I just feel so strongly about it! Ever since I started learning ASP.NET those many years ago, I've never been a fan of using "Eval" in data-bound controls I primarily use, such as GridViews, Repeaters and DataList. When I see it still being used regularly in web applications I cringe a little and I feel I need to express some reasons to why it should stop being used.

I think working on an application handed down to me from an external development agency pushed me to write this post... Let's call it a form of therapy! I won't make this post a rant and will "try" to be constructive and concise. My views might come across a little one-sided, but I promise I will start with at least one good thing to say about our evil friend Eval.

Postive: Quick To Render Simple Data

If the end goal is to list out some string values as is from the database with some minor manipulation from a relatively small dataset, I almost have no problem with that, even though I still believe it can be used and abused by inexperienced developers.

Negative: Debugging

The main disadvantage of embedding code inside your design file (.aspx or .ascx) is that it's not very easy to view the output during debugging. This causes a further headache when your Eval contains some conditional statements to alter the output on a row-by-row basis.

Negative: Difficult To Carry Out Complex HTML Changes

I wouldn't recommend using Eval in scenario's where databound rows require some form of HTML change. I've seen some ugly implementations where complex conditional statements were used to list out data in a creative way. If the HTML ever had to be changed through design updates, it would be a lot more time consuming to carry when compared to moving around some form controls that are databound through a RowDataBound event.

Negative: Ugly To Look At

This point will come across very superficial. Nevertheless, what I find painful to look at is when Eval is still used to carry out more functionality by calling additional methods and potentially repeating the same functionality numerous times.

Performance/Efficiency

From my research, it's not clear if there specifically is a performance impact in using Eval alone, especially with the advances in the .NET framework over the years. A post from 2012 on StackExchange brought up a very good point:

Eval uses reflection to get the value of the relevant property/field, and using Reflection to get values from object members is very slow.

If the type of an object can be determined at runtime, you're better off explicitly declaring this. After all, it's good coding standards. In the real world, the performance impact is nominal depending on the number of records you are dealing with. Not recommended for building scalable applications. I generally notice a slow down (in milliseconds) when outputting 500 rows of data.

I have read that reflection is not as much of an issue in the most recent versions of the .NET framework when compared to say, .NET 1.1. But I am unable to find any concrete evidence of this. Regardless, I'd always prefer to use the faster approach, even if I am happening to shave off a few milliseconds in the process.

Conclusion

Just don't use Eval. Regardless of the size of the dataset I am dealing with, there would only be two approaches I'd ever use:

  1. RowDataBoundEvent: A controls RowDataBoundEvent event is triggered every time a row is databound with data. This approach enables us to modify the rows appearance and structure in a specific way depending on the type of rules we have in place.
  2. Start From Scratch: Construct the HTML markup by hand based on the datasource and render to the page.

If I were to be building a scalable application dealing with thousands of rows of data, I am generally inclined to go for option 2. As you're not relying on a .NET control, you won't be contributing to the page viewstate.

Even though I have been working on a lot more applications using MVC where I have more control on streamlining the page output, I still have to dabble with Web Forms. I feel with Web Forms, it's very easy to make a page that performs really bad, which makes it even more important to ensure you are taking all necessary steps to ensure efficiency.

Salesforce .NET API: Get Picklist Values

I have been doing a lot of Saleforce integration lately, which has been both interesting and fun. Throughout my time working on Salesforce, I noticed that I am making very similar calls when pulling information out for consumption into my website. So I decided to make an extra effort to develop methods that would allow me to re-use commonly used functionality into a class library to make overall coding quicker.

I am adding all my Salesforce object query related functionality to a class object called "ObjectDetailInfoProvider". This will give me enough scope to expand with additional methods as I see fit. 

To start with, I decided to deal with returning all information from both picklist and multi-select picklists fields, since I find that I constantly require the values of data due to the vast number of forms I am developing. To be extra efficient in every request, I taken the extra step to cache all returned data for a set period of time. I hate the idea of constantly hammering away at an API unless absolutely necessary.

Before we get into it, it's worth noting that I am referencing a custom "AuthenticationResponse" class I created. You can grab the code here.

Objects

There are around seven class objects used purely for deserialization when receiving data from Salesforce. I'll admit I won't use all fields the API has to offer, but I normally like to have a complete fieldset to hand on the event I require further data manipulation.

The one to highlight out of all the class objects is "ObjectFieldPicklistValue", that will store key information about the picklist values, such as Label, Value and Active state. All methods will return this object.

public class ObjectFieldPicklistValue
{
    [JsonProperty("active")]
    public bool Active { get; set; }

    [JsonProperty("defaultValue")]
    public bool DefaultValue { get; set; }

    [JsonProperty("label")]
    public string Label { get; set; }

    [JsonProperty("validFor")]
    public string ValidFor { get; set; }

    [JsonProperty("value")]
    public string Value { get; set; }
}

I have added all other Object Field class objects to a snippets section on my Bitbucket account.

GetPicklistFieldItems() & GetMultiSelectPicklistFieldItems() Methods

Both methods perform similar functions; the only difference is cache keys and lambda expression to only pull out either a picklist or multipicklist by its field name.

/// <summary>
/// Gets a values from a specific picklist within a Salesforce object. Items returned are cached for 15 minutes.
/// </summary>
/// <param name="objectApiName"></param>
/// <param name="pickListFieldName"></param>
/// <returns>Pick list values</returns>
public static async Task<List<ObjectFieldPicklistValue>> GetPicklistFieldItems(string objectApiName, string pickListFieldName)
{
    string cacheKey = $"GetPicklistFieldItems|{objectApiName}|{pickListFieldName}";

    List<ObjectFieldPicklistValue> pickListValues = CacheEngine.Get<List<ObjectFieldPicklistValue>>(cacheKey);

    if (pickListValues == null)
    {
        Authentication salesforceAuth = await AuthenticationResponse.Rest();

        HttpClient queryClient = new HttpClient();

        string apiUrl = $"{SalesforceConfig.PlatformUrl}services/data/v37.0/sobjects/{objectApiName}/describe";

        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, apiUrl);
        request.Headers.Add("Authorization", $"Bearer {salesforceAuth.AccessToken}");
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                
        HttpResponseMessage response = await queryClient.SendAsync(request);

        string outputJson = await response.Content.ReadAsStringAsync();

        if (!string.IsNullOrEmpty(outputJson))
        {
            // Get all the fields information from the object.
            ObjectFieldInfo objectField = JsonConvert.DeserializeObject<ObjectFieldInfo>(outputJson);

            // Filter the fields to get the required picklist.
            ObjectField pickListField = objectField.Fields.FirstOrDefault(of => of.Name == pickListFieldName && of.Type == "picklist");
                    
            List<ObjectFieldPicklistValue> picklistItems = pickListField?.PicklistValues.ToList();

            #region Set cache

            pickListValues = picklistItems;

            // Add collection of pick list items to cache.
            CacheEngine.Add(picklistItems, cacheKey, 15);

            #endregion
        }
    }

    return pickListValues;
}

/// <summary>
/// Gets a values from a specific multi-select picklist within a Salesforce object. Items returned are cached for 15 minutes.
/// </summary>
/// <param name="objectApiName"></param>
/// <param name="pickListFieldName"></param>
/// <returns>Pick list values</returns>
public static async Task<List<ObjectFieldPicklistValue>> GetMultiSelectPicklistFieldItems(string objectApiName, string pickListFieldName)
{
    string cacheKey = $"GetMultiSelectPicklistFieldItems|{objectApiName}|{pickListFieldName}";

    List<ObjectFieldPicklistValue> pickListValues = CacheEngine.Get<List<ObjectFieldPicklistValue>>(cacheKey);

    if (pickListValues == null)
    {
        Authentication salesforceAuth = await AuthenticationResponse.Rest();

        HttpClient queryClient = new HttpClient();

        string apiUrl = $"{SalesforceConfig.PlatformUrl}services/data/v37.0/sobjects/{objectApiName}/describe";

        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, apiUrl);
        request.Headers.Add("Authorization", $"Bearer {salesforceAuth.AccessToken}");
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        HttpResponseMessage response = await queryClient.SendAsync(request);

        string outputJson = await response.Content.ReadAsStringAsync();

        if (!string.IsNullOrEmpty(outputJson))
        {
            // Get all the fields information from the object.
            ObjectFieldInfo objectField = JsonConvert.DeserializeObject<ObjectFieldInfo>(outputJson);

            // Filter the fields to get the required picklist.
            ObjectField pickListField = objectField.Fields.FirstOrDefault(of => of.Name == pickListFieldName && of.Type == "multipicklist");

            List<ObjectFieldPicklistValue> picklistItems = pickListField?.PicklistValues.ToList();

            #region Set cache

            pickListValues = picklistItems;

            // Add collection of pick list items to cache.
            CacheEngine.Add(picklistItems, cacheKey, 15);

            #endregion
        }
    }

    return pickListValues;
}

Get Facebook Comments For A Page Using Graph API In ASP.NET

For a site I'm working on, the Facebook's Comments plugin is being utilised on all our article pages. There was a requirement to pull in the latest comments in a listing page for each of these article pages as well as number of comments. Facebook's JavaScript library provides the ability to display a comments counter but not the ability to pull out x number of comments. So we'll have to go server-side and use Graph API to get the data we want.

In this post, I will show you how you can get back all comments for a page by it's full URL.

Prerequisites

Before we get into the main C# logic methods, you need to make sure we have a few things in place:

  • ApiWebRequestHelper Class
  • Newtonsoft Json
  • Facebook App Settings
  • Class Objects

ApiWebRequestHelper Class

Whenever I am making a call to Facebook's Graph API endpoints, I will be making references to a "ApiWebRequestHelper" helper class. This is something I developed last month to make it easier for me to deserialize XML or JSON requests to a strongly-typed class object. You can take a look at the full code here.

Newtonsoft Json

The Newtonsoft Json library is a key ingredient to any JSON web requests. I'd be surprised if you've never heard or used it. :-) Nevertheless, you can get it here: http://www.newtonsoft.com/json.

Facebook App Settings

I haven't created a Facebook App for quite some time and things have changed very slightly in terms of the interface and options presented. The key things you need to get out of your created App is:

  • Application ID
  • Application Secret
  • Client Token

I set the security settings with the following modes, which can be found in Settings > Advanced >  Security.

Facebook App Advanced API Settings

Class Objects

The following class objects will be used to deserialize Graph API requests into class objects.

The FacebookPageInfo, FacebookPage and FacebookPageShare objects will get the core information about the queried page, such as the Title and Description, as well as the comments and share counts.

namespace Site.BusinessObjects.Facebook
{
    public class FacebookPageInfo
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("og_object")]
        public FacebookPage Page { get; set; }

        [JsonProperty("share")]
        public FacebookPageShare Share { get; set; }
    }

    public class FacebookPage
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("description")]
        public string Description { get; set; }

        [JsonProperty("title")]
        public string Title { get; set; }

        [JsonProperty("type")]
        public string Type { get; set; }

        [JsonProperty("updated_time")]
        public DateTime UpdatedTime { get; set; }

        [JsonProperty("url")]
        public string Url { get; set; }
    }
}
namespace Site.BusinessObjects.Facebook
{
    public class FacebookPageShare
    {
        [JsonProperty("comment_count")]
        public int CommentCount { get; set; }

        [JsonProperty("share_count")]
        public int ShareCount { get; set; }
    }
}

All comments for a page will be stored in the following objects:

namespace Site.BusinessObjects.Facebook
{
    public class FacebookPageCommentInfo
    {
        public int TotalComments { get; set; }
        public List<FacebookCommentItem> Comments { get; set; }
    }
}
namespace Site.BusinessObjects.Facebook
{
    public class FacebookCommentItem
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("created_time")]
        public DateTime CreatedTime { get; set; }

        [JsonProperty("from")]
        public FacebookCommentFrom From { get; set; }

        [JsonProperty("message")]
        public string Message { get; set; }
    }

    public class FacebookCommentFrom
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; }
    }
}

Facebook Logic Class

Now that we have the pre-requisites in place, lets get to the code that will perform the required functions:

namespace Site.BusinessLogic
{
    public class FacebookLogic
    {
        private string _accessToken;

        /// <summary>
        /// Uses default Client ID and Secret as set in the web.config.
        /// </summary>
        public FacebookLogic()
        {
            GetAccessToken(Config.Facebook.ClientId, Config.Facebook.ClientSecret);
        }

        /// <summary>
        /// Requires  Client ID and Secret.
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="clientSecret"></param>
        public FacebookLogic(string clientId, string clientSecret)
        {
            GetAccessToken(clientId, clientSecret);
        }

        /// <summary>
        /// Gets page info that has been shared to Facebook.
        /// </summary>
        /// <param name="pageUrl"></param>
        /// <returns></returns>
        public FacebookPageInfo GetPage(string pageUrl)
        {
            return ApiWebRequestHelper.GetJsonRequest<FacebookPageInfo>($"https://graph.facebook.com/{pageUrl}?access_token={_accessToken}");
        }

        /// <summary>
        /// Gets comments for a page based on its absolute URL.
        /// </summary>
        /// <param name="pageUrl"></param>
        /// <param name="maxComments"></param>
        public FacebookPageCommentInfo GetPageComments(string pageUrl, int maxComments)
        {
            try
            {
                // Get page information in order to retrieve page ID to pass to commenting.
                FacebookPageInfo facebookPage = GetPage(pageUrl);

                if (facebookPage.Page != null)
                {
                    return new FacebookPageCommentInfo
                    {
                        TotalComments = facebookPage.Share.CommentCount,
                        Comments = GetCommentsByPageId(facebookPage.Page.Id, maxComments).Comments
                    };
                }
                else
                {
                    return null;
                }
            }
            catch (Exception ex)
            {
                // NOTE: Log exception here...

                return null;
            }
        }

        /// <summary>
        /// Gets comments by Facebook's Page ID.
        /// </summary>
        /// <param name="fbPageId"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public FacebookCommentInfo GetCommentsByPageId(string fbPageId, int max = 10)
        {
            return ApiWebRequestHelper.GetJsonRequest<FacebookCommentInfo>($"https://graph.facebook.com/comments?id={fbPageId}&access_token={_accessToken}&limit={max}");
        }

        /// <summary>
        /// Retrieves Access Token from Facebook App.
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="clientSecret"></param>
        private void GetAccessToken(string clientId, string clientSecret)
        {
            UriBuilder builder = new UriBuilder($"https://graph.facebook.com/oauth/access_token?client_id={Config.Facebook.ClientId}&client_secret={Config.Facebook.ClientSecret}&grant_type=client_credentials");

            try
            {
                using (WebClient client = new WebClient())
                {
                    // Get Access Token from incoming response.
                    string data = client.DownloadString(builder.Uri);

                    NameValueCollection parsedQueryString = HttpUtility.ParseQueryString(data);

                    _accessToken = parsedQueryString["access_token"];
                }
            }
            catch (Exception ex)
            {
                // NOTE: Log exception here...
            }
        }
    }
}

By default, on initiation of the FacebookLogic class, the Application ID and Secret values will be inherited from the web.config, or you can pass in these values directly with the class overload parameters.

Out of all the methods used here, we're interested in only using one: GetPageComments(). What you will notice from this method is that we cannot get the comments from one API call alone. We first have to make an extra API call to get the ID of the page. This ID is passed to the GetCommentsByPageId() method, to return all comments.

Usage

Comments for a page can be returned by adding the following in your code, where you will then be able to access properties to iterate through the comments:

FacebookLogic fbl = new FacebookLogic();

// Pass in the page URL and number of comments to be returned.
var pageComments = fbl.GetPageComments("https://www.surinderbhomra.com/", 2);

Whenever you call this piece of code, I would make sure you cache the results for 5 - 10 minutes, so you do not use up your API request limits.

Is ReactJS A Worthwhile Addition To An ASP.NET MVC Application?

ReactJSI've been meddling around with ReactJS over the last week or so, seeing if this is something viable to use for future client projects. I am always constantly on the lookout to whether there are better alternatives on how my fellow developers and I develop our sites.

Throughout the sample applications I've been building, I constantly asked myself one question: Why Would I Use ReactJS In My Day To Day Development? I am ASP.NET developer who build websites either using Web Forms or MVC Razor. So I am finding it difficult to comprehend whether using ReactJS is viable in these frameworks, especially MVC.

ReactJS is primarily a view framework where you have the ability to write component-based web applications directly into your JavaScript that then gets output to the DOM virtually - making for a very fast and responsive webpage. It's a different approach to developing websites that I quite like. But for the moment, I just don't see how it can benefit me when the full MVC framework does a pretty good job with all the bells and whistles.

For example, I segregate all my key HTML markup into partial views in order to increase re-use throughout my web application, which works really well when making AJAX calls where the markup needs to be displayed on the page asynchronously as well as server-side. I can just see by implementing ReactJS, I will be duplicating this process at JavaScript and CSHTML level if a markup change ever needed to be made. If partial views does the job effectively, I'm not too sure the need for ReactJS in my future ASP.NET MVC creations.

Don't get me wrong - I really like ReactJS. It makes writing JavaScript an even more enjoyable experience purely due to the JSX syntax. Long gone are the days where you have to concatenate strings to form HTML. More importantly, it's readable and truly scalable.

Unfortunately, it doesn't look like ReactJS is a viable option for me at this moment in time. I can see how it would be a very useful framework for building web applications where there is a requirement for the view to be created strictly client-side along with heavy use of AJAX calls from an API layer to serve data to your application. But in situations where you have two frameworks that provide the ability to create views, in this case ReactJS and ASP.NET MVC, it doesn't make sense.

I could be talking absolute nonsense and missing the whole point. If this is the case (most likely!), please leave a comment.

TextMode Causes Max Length To Not Work In ASP.NET Web Forms

After quite a successful stint in creating some really amazing websites using MVC Razor 5, I had to revert back to working in the world of Web Forms for a new client project. Now I have to deal with .NET web controls, Postbacks and Viewstates.

When creating a few simple forms consisting of some drop down lists, textboxes and textareas, I noticed wherever I set a "MaxLength" property it would not work. A character limit on my input field would just be ignored in all browsers. I could only replicate this bug when the Max Length property is used alongside the "TextMode" property.

There were various places where I had the "TextMode" property set to either "Number" or "MultiLine". Soon as this was removed, the value set in "MaxLength" would work.

How very odd...

Thankfully, I am not the only person to experience the same issue. Currently, the only way to get around this problem is to add the following JavaScript code (slightly refactored for my own implementation) to check the character length "onKeyDown".

var ASPNETForm = {
    "CheckTextAreaLength": function(textBox, e, length) {
        var mLen = textBox["MaxLength"];

        if (null == mLen)
            mLen = length;

        var maxLength = parseInt(mLen);
        if (!ASPNETForm.CheckSpecialKeys(e)) {
            if (textBox.value.length > maxLength - 1) {
                if (window.event) { //IE
                    e.returnValue = false;
                    return false;
                }
                else //Firefox
                    e.preventDefault();
            }
        }
    },
    "CheckSpecialKeys": function(e) {
        if (e.keyCode != 8 && e.keyCode != 46 && e.keyCode != 35 && e.keyCode != 36 && e.keyCode != 37 && e.keyCode != 38 && e.keyCode != 39 && e.keyCode != 40)
            return false;
        else
            return true;
    }
}

Add to your ASP.NET TextBox control as so:

<asp:textbox id="Instructions" MaxLength="50" onkeydown="return ASPNETForm.CheckTextAreaLength(this,event,'50');" runat="server" textmode="MultiLine">
</asp:textbox>

I am hoping there will be a more elegant solution in ASP.NET 5, or even better, a fix. This problem has been lurking around since 2009!

Dynamic Robots.txt file for ASP.NET MVC Sites

Changing the contents of a robots.txt file when a site is moved from staging to a live environment is quite a manual and somewhat cumbersome process. I sometimes have the fear of forgetting to replace the "Disallow: /" line with correct indexable entries required for the website.

To give me one less thing to remember during my pre-live deployments, all my current and upcoming ASP.NET MVC sites will use a dynamic robots.txt file containing different entries depending on whether a site is in stage or live. In order to do this, we need to let the ASP.NET MVC application serve up the robots.txt file. This can be done by the following:

  • Create a new controller called "SEOController"
  • Add a new FileContentResult action called "Robots".
  • Add a new route.
  • Modify web.config.

SEOController

I have created a new controller that renders our "Robots" action as a plain text file. As you can see, my controller is not a type of "ActionResult" but a "FileContentResult". The great thing about "FileContentResult" is that it allows us to return bytes from the controller.

In this example, I am converting bytes from a string using Encoding.UTF8.GetBytes() method. Ideal for what we need to generate a robots.txt file.

[Route("robots.txt")]
[Route("Robots.txt")]
public FileContentResult Robots()
{
    StringBuilder robotsEntries = new StringBuilder();
    robotsEntries.AppendLine("User-agent: *");

    //If the website is in debug mode, then set the robots.txt file to not index the site.
    if (System.Web.HttpContext.Current.IsDebuggingEnabled)
    {
        robotsEntries.AppendLine("Disallow: /");
    }
    else
    {
        robotsEntries.AppendLine("Disallow: /Error");
        robotsEntries.AppendLine("Disallow: /resources");
        robotsEntries.AppendLine("Sitemap: http://www.surinderbhomra.com/sitemap.xml");
    }

    return File(Encoding.UTF8.GetBytes(robotsEntries.ToString()), "text/plain");
}

RouteConfig

Since I add my routing at controller level, I add the "MapMvcAttributeRoutes" method to the RouteConfig.cs file. But if you prefer to add your routes directly here, then this method can be removed.

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapMvcAttributeRoutes(); //Add this line!

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

Web.config

Add a "runAllManagedModulesForAllRequests" attribute to the modules tag in the web.config file to allow our robot.txt text file to be rendered by ASP.NET.

<system.webserver>
  <modules runallmanagedmodulesforallrequests="true"></modules>
</system.webserver>

Caching Static Files Through Web.config

When running my website through Google Page Insights, one of things I didn't do was cache static content, such as CSS, JavaScript and site images. Since I am on a shared hosting plan, I didn't think it was possible to have the option to cache a specific directory without direct IIS access.

Normally, when working on client sites hosted on a dedicated server, I set the cache header within "HTTP Response Headers" area in IIS. But all this actually does is generate a web.config file within the directory you wish to cache:

<!--?xml version="1.0" encoding="UTF-8"?-->
<configuration>
    <system.webServer>
        <httpProtocol>
            <customHeaders>
                <add name="Cache-Control" value="public, max-age=604800" />
            </customHeaders>
        </httpProtocol>
    </system.webServer>
</configuration>

So if you too are on shared hosting, add a web.config file with similar settings. In this case, I have cached my files for a week.

You can also set the cache settings in your main web.config file by wrapping a location path around the <system.webServer> node:

<location path="resources">
  <system.webServer>
        <httpProtocol>
            <customHeaders>
                <add name="Cache-Control" value="public, max-age=604800" />
            </customHeaders>
        </httpProtocol>
    </system.webServer>
 </location>

Render Partial View As A String

One of the many nice things of using ASP.NET MVC Razor is that you have full control over how you segregate your HTML markup when building a page through rendering PartialViews. Since becoming an avid MVC developer, I am increasingly noticing how easy it is to make nice neat reusable code, whether it is used server or client-side.

Just today, I found something really useful that is a truly defines this, where markup within PartialViews can be output to a page as string:

/// <summary>
/// Controller extension class that adds controller methods
/// to render a partial view and return the result as string.
///
/// Based on http://craftycodeblog.com/2010/05/15/asp-net-mvc-render-partial-view-to-string/
/// </summary>
public static class ControllerExtension
{
 
  /// <summary>
  /// Renders a (partial) view to string.
  /// </summary>
  /// <param name="controller">Controller to extend</param>
  /// <param name="viewName">(Partial) view to render</param>
  /// <returns>Rendered (partial) view as string</returns>
  public static string RenderPartialViewToString(this Controller controller, string viewName)
  {
    return controller.RenderPartialViewToString(viewName, null);
  }
 
  /// <summary>
  /// Renders a (partial) view to string.
  /// </summary>
  /// <param name="controller">Controller to extend</param>
  /// <param name="viewName">(Partial) view to render</param>
  /// <param name="model">Model</param>
  /// <returns>Rendered (partial) view as string</returns>
  public static string RenderPartialViewToString(this Controller controller, string viewName, object model)
  {
    if (string.IsNullOrEmpty(viewName))
      viewName = controller.ControllerContext.RouteData.GetRequiredString("action");
 
      controller.ViewData.Model = model;
 
      using (var sw = new StringWriter())
      {
        var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
        var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
        viewResult.View.Render(viewContext, sw);
 
        return sw.GetStringBuilder().ToString();
      }
    } 
}

I can't take credit for this code. But here is the guy who can: Jan Jonas.

Being able to output PartialViews as a string is actually quite handy, since you could have a paginated news listings page that displays the first page of articles server-side and any additional pages could be loaded in via jQuery Ajax. Each article item would be a PartialView so you could serve the same markup client-side. My code below probably explains things a little better:

Article Listing View

This page will list all my News Articles. As you can see, I am using an "ArticleListItem" as my PartialView.

@model List<Article>

@if (Model.Any())
{
    <div class="article-list">
    @foreach (var a in Model.Select((value, index) => new { value, index }))
    {
        Html.RenderPartial("/Views/Article/_ArticleListItem.cshtml", new ArticleListItemView { Article = a.value, CssClass = ArticleHtmlHelper.GetItemCssClass((a.index + 1)), IsFullWidth = false});
    }
    </div>
}
else
{
    <div>
        No articles could be returned.
    </div>
}

Article List Item PartialView

My PartialView has quite a bit going on to determine how the markup should be rendered and it's definitely something I wouldn't want to have to duplicate elsewhere just to load in client-side. Nice!

@model Site.Web.Models.Views.ArticleListItemView
@{
    string fullWidthClass = String.Empty;

    if (Model.IsFullWidth)
    {
        fullWidthClass = "full-width";
    }
}
<div class="article-summary @Model.CssClass @fullWidthClass">
    <a href="@Model.Article.PageUrl" class="img">
        @if (Model.CssClass == "large")
        {
        <img src="@Model.Article.Images.ImageCollection[1].Url" />
        }
        else
        {
        <img src="@Model.Article.Images.ImageCollection[0].Url" />
        }
    </a>
    @if (Model.Article.Category != null)
    {
    <span class="cat">@Model.Article.Category.Name</span>
    }
    @if (Model.Article.ReadTime != null)
    {
    <span class="time">@String.Format("{0} read", Model.Article.ReadTime)</span>
    }
    <h2 class="@Model.CssClass"><a href="@Model.Article.PageUrl">@Model.Article.Title</a></h2>
    @if (Model.Article.Author != null)
    {
    <a href="@Model.Article.Author.PageUrl.Url" class="author">
        <img src="@Model.Article.Author.Images.ImageCollection[0].Url" />
        <span>@String.Concat(Model.Article.Author.FirstName, " ", Model.Article.Author.LastName)</span>
    </a>
    }
</div>

GetArticleItems() Controller

This is where the RenderPartialViewToString() method shines! This controller is called within my jQuery Ajax function to get the next page of news articles. I am then calling my "ArticleListItem" PartialView to return the HTML markup as a string through my client-side call.

[HttpPost]
public JsonResult GetArticleItems(DBContext ctx, int pageNo, int pageSize, string categoryId)
{
    ApiDocumentInfo docInfo = DocumentHelper.SearchDocuments(ctx, true, "article", "category", categoryId, pageSize, pageNo, "articles", "date desc");

    List<Article> articles = docInfo.Documents.Select(doc => doc.ToArticle(ctx)).ToList();

    StringBuilder articleHtml = new StringBuilder();

    if (articles.Any())
    {
        for (int a = 0; a < articles.Count; a++)
            articleHtml.Append(this.RenderPartialViewToString("_ArticleListItem", new ArticleListItemView { Article = articles[a], CssClass = ArticleHtmlHelper.GetItemCssClass((a + 1)), IsFullWidth = false } ));
    }

    return Json(articleHtml.ToString());
}

SyndicationFeed.Load - The easy way to read an RSS feed

I always found writing code to read an RSS feed within my .NET application very time-consuming and long-winded. My RSS code was always a combination of using WebRequest, WebResponse, Stream, XmlDocument, XmlNodeList and XmlNode. That’s a lot of classes just to read an RSS feed.

Yesterday, I stumbled on an interesting piece of code on my favourite programming site StackOverflow.com, where someone asked how to parse an RSS feed in ASP.NET. The answer was surprisingly simple. RSS feeds can now be consumed using the System.ServiceModel.Syndication namespace in .NET 3.5 SP1. All you need is two lines of code:

var reader = XmlReader.Create("http://mysite.com/feeds/serializedFeed.xml");
var feed = SyndicationFeed.Load(reader);

Here’s a full example on how we can iterate through through the SyndicationFeed class:

public static List<BlogPost> Get(string rssFeedUrl)
{
    var reader = XmlReader.Create(rssFeedUrl);
    var feed = SyndicationFeed.Load(reader);

    List<BlogPost> postList = new List<BlogPost>();

    //Loop through all items in the SyndicationFeed
    foreach (var i in feed.Items)
    {
        BlogPost bp = new BlogPost();
        bp.Title = i.Title.Text;
        bp.Body = i.Summary.Text;
        bp.Url = i.Links[0].Uri.OriginalString;
        postList.Add(bp);
    }

    return postList;
}

That’s too simple, especially when compared to the 70 lines of code I normally use to do the exact same thing.

Beginner’s Guide To Using Google Plus .NET API Part 1: Profile Data

Google has always impressed me with the quality of their API libraries allowing us to interface with their products in a somewhat straight-forward manner. In the past, I’ve used a couple of Google’s API’s for implementing YouTube video’s or Checkout merchant within my own sites. What makes life even easier is that the API’s are available in my native programming framework - .NET.

Google were quite slow in launching an official API upon Google Plus’s initial release and even though unofficial API’s were available, I thought it would be best to wait until an official release was made. I’ve been playing around with Google’s .NET API for a couple weeks now and only just had the time to blog about it.

I am hoping to make this beginners guide a three part series:

  1. Profile Data
  2. User Posts
  3. User’s +1’s

So let’s get to it!

Today, I shall be showing you the basic API principles to get you started in retrieving data from your own Google+ profile.

Prerequisites

Before we can start thinking about coding our page to retrieve profile information, it’s a requirement to register your application by going to: https://code.google.com/apis/console. Providing you already have an account with Google (and who hasn’t?), this shouldn’t be a problem. If you don’t see the page (below), a new API Project needs to be created.

Google Plus API - Console

Only the Client ID, Client Secret and API Key will be used in our code allowing us to carry our API requests from our custom application.

Next, download the Google Plus .NET Client. My own preference is to use the Binary release containing compiled .NET Google Client API and all dll's for all supported services.

Building A Custom Profile Page

1) Create a new Visual Studio Web Application.

2) Unzip the Binary Zip file containing all Google service DLL’s. Find and reference the following DLL’s in your project:

  • Google.Apis.dll
  • Google.Apis.Authentication.OAuth2.dll
  • Google.Apis.Plus.v1.dll

3) Copy and paste the following front-end HTML code:

<h2>
    About Me
</h2>
<br />
<table>
    <tr>
        <td valign="top">
            <asp:Image ID="ProfileImage" runat="server"></asp:Image>
        </td>
        <td valign="top">
            <strong>Name:</strong> <asp:Label ID="DisplayName" runat="server"></asp:Label>
            <br /><br />
            <strong>About Me:</strong> <asp:Label ID="AboutMe" runat="server"></asp:Label>
            <br />
            <strong>Gender:</strong> <asp:Label ID="Gender" runat="server"></asp:Label>
            <br /><br />
            <strong>Education/Employment:</strong> <asp:Literal ID="Work" runat="server"></asp:Literal>
        </td>
    </tr>
    <tr>
        <td colspan="2" valign="middle">
            <asp:HyperLink ID="GotoProfileButton" runat="server">Go to my Google+ profile</asp:HyperLink>
        </td>
    </tr>
</table>

4) Copy and paste the following C# code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;
using Google.Apis.Authentication.OAuth2;
using Google.Apis.Plus.v1;
using Google.Apis.Plus.v1.Data;

namespace GooglePlusAPITest
{
    public partial class About : System.Web.UI.Page
    {
        private string ProfileID = "100405991313749888253"; // My public Profile ID
        private string GoogleIdentifier = "<GoogleIdentifier>";
        private string GoogleSecret = "<GoogleSecret>";
        private string GoogleKey = "<GoogleKey>";

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
                GetGooglePlusProfile();
        }

        private void GetGooglePlusProfile()
        {
            var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
            provider.ClientIdentifier = GoogleIdentifier;
            provider.ClientSecret = GoogleSecret;

            var service = new PlusService();
            service.Key = GoogleKey;

            var profile = service.People.Get(ProfileID).Fetch();

            // Profile Name
            DisplayName.Text = profile.DisplayName;

            //About me
            AboutMe.Text = profile.AboutMe;

            //Gender
            Gender.Text = profile.Gender;

            // Profile Image
            ProfileImage.ImageUrl = profile.Image.Url;

            // Education/Employment
            StringBuilder workHTML = new StringBuilder();

            workHTML.Append("<ul>");

            foreach (Person.OrganizationsData work in profile.Organizations.ToList())
            {
                workHTML.AppendFormat("<li>{0} ({1})", work.Title, work.Name);
            }

            workHTML.Append("</ul>");

            Work.Text = workHTML.ToString();

            //Link to Google+ profile
            GotoProfileButton.NavigateUrl = profile.Url;            
        }
    }
}

Once completed, the page should resemble something like this:

Google Plus Profile Page

I think you can all agree this example was pretty straight-forward. We are simply using the people.get method which translates into the following HTTP request:

https://www.googleapis.com/plus/v1/people/100405991313749888253?key=APIKey

Unless you really want to display my profile information on your site (who wouldn’t!), you can keep the code as it is. But you have the flexibility to change the “ProfileID” variable to an ID of your own choice. To find your Profile ID, read: How Do I Find My Google Plus User ID?.