Saturday, 28 November 2015

Export MVC WebGrid to Excel (CSV)

Overview

Have you ever needed to export a WebGrid to Excel? In this article I will explain the steps needed to export the contents of a MVC WebGrid to Excel (CSV). The only prerequisite is that your input be IEnumerable<T>. The first step is to create your Util or Helper methods that actually do the conversion. Then we will add the HTMLHelper methods for the view. Then we will create the Model, View and Controller.
All of the required components:
· Util Methods
· HTML Helper
· Model
· View
· Controller

Util code for IEnumerable to CSV

There are two methods that are the central components of this solution. They convert data from an IEnumerable object to a CSV format that can be opened with Excel. In the first method, we will create a generic extension method (ToCsv) for IEnumerable data types that will allow a developer to call the method on any IEnumerable object. The second method (ToCsvValue) is a helper method to escape certain characters.
Header
To build the header row in Excel we use reflection to get a list of all of the properties of the object. It is worth pointing out here that the headers will be exactly the same as the property names in the object. If you want to get “pretty” names with spaces you will either need to have a lookup or some kind of Regular Expression to format the names. For this example though we will just use the camel case names in the object.
Body
To build the body of the CSV file we loop through all of the items in the IEnumerable object. Notice that we call ToCsvValue for each item in the enumeration. This helper method deals with any null values and any quotes that could interrupt the comma separated sequence. Another thing to note here is that the screenshot shows that the grid is paged; however, ToCsv method will add all the available values to the CSV file. If you only want to show the paged values you will have to pass the current page number back to your Model and return only those values to the ToCsv method.
<pre>
/// <summary>
/// Takes a generic IEnumerable and converts it to a CSV for using in an excel file.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <returns></returns>
public static string ToCsv<T>(this IEnumerable<T> items)
   where T : class
{
    var csvBuilder = new StringBuilder();
    var properties = typeof(T).GetProperties();
    //Header row
    foreach (var prop in properties)
    {
       csvBuilder.Append(prop.Name.ToCsvValue() + ",");      
    }
    csvBuilder.AppendLine("");//Add line break
    //Body
    foreach (T item in items)
    {
        string line = string.Join(",", properties.Select(p => p.GetValue(item, null).ToCsvValue()).ToArray());
        csvBuilder.AppendLine(line);
    }
    return csvBuilder.ToString();
}
/// <summary>
/// Helper method for dealing with nulls and escape characters
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item"></param>
/// <returns></returns>
public static string ToCsvValue<T>(this T item)
{
    if (item == null) return "\"\"";
    if (item is string)
    {
        return string.Format("\"{0}\"", item.ToString().Replace("\"", "\\\""));
    }
    double dummy;
    if (double.TryParse(item.ToString(), out dummy))
    {
        return string.Format("{0}", item);
    }
    return string.Format("\"{0}\"", item);
}
</pre>

HTML helpers allow us to clean up our view code. The ImageActionLink wraps an anchor tag around an image. This method is an extension to the HTMLHelper methods supplied by MVC. Because we are passing in “this HtmlHelper” as a parameter we can use @Html.ImageActionLink() in our view page. Note we are also adding a GUID or a unique identifier to the URL in order to prevent AJAX from caching the response.
public static MvcHtmlString ImageActionLink(
           this HtmlHelper helper,
           string imageUrl,
           string altText,
           string actionName,
           string controllerName,
           object routeValues,
           object linkHtmlAttributes,
           object imgHtmlAttributes)
        {
            var linkAttributes = AnonymousObjectToKeyValue(linkHtmlAttributes);
            var imgAttributes = AnonymousObjectToKeyValue(imgHtmlAttributes);
            var imgBuilder = new TagBuilder("img");
            imgBuilder.MergeAttribute("src", imageUrl);
            imgBuilder.MergeAttribute("alt", altText);
            imgBuilder.MergeAttributes(imgAttributes, true);
            var urlHelper = new UrlHelper(helper.ViewContext.RequestContext, helper.RouteCollection);
            var linkBuilder = new TagBuilder("a");
            //tack on a GUID to prevent AJAX caching
            var routeDictionary = new RouteValueDictionary(routeValues);
            routeDictionary.Add("guid", Guid.NewGuid());
            linkBuilder.MergeAttribute("href", urlHelper.Action(actionName, controllerName, routeDictionary));
            linkBuilder.MergeAttributes(linkAttributes, true);
            var text = linkBuilder.ToString(TagRenderMode.StartTag);
            text += imgBuilder.ToString(TagRenderMode.SelfClosing);
            text += linkBuilder.ToString(TagRenderMode.EndTag);
            return MvcHtmlString.Create(text);
        }
With a standard naming convention and location for icons you can create an enum that maps to your different icons.
public enum Icons
    {
        Add, //Add.png
        Edit, //Edit.png
        Error, //Error.png
        Excel, //Excel.png
        Info, //Info.png
    }
To complement the enum you can create an IconURL HTMLHelper method that you can use in any view to get an icon path. We use the .Net method GenerateContentUrl to get a path from the root of the web application. It takes two parameters: a string relative path and an HttpContext object that comes from the HtmlHelper object passed in from the view.
/// <summary> /// Returns a string relative path from root to the icon passed. /// </summary> /// <param name=”icon”></param> /// <returns></returns> public static string IconURL(this HtmlHelper helper, Icons icon) { return @UrlHelper.GenerateContentUrl( “~/Content/icons/” + icon.ToString().ToLower() +
“.png”, helper.ViewContext.HttpContext); }

Model

This is where we get our IEnumerable object from Entity Framework. In the GetFilteredItemsForExcel method we use LINQ to SQL to create a new ExcelAlert object which serves as a DTO (data transfer object). This method also call the GetFilteredAlerts method which uses LINQ to SQL and Lambda expressions to filter the list of alerts based on the two parameters we pass in. Returning an IQueryable object allows us to add additional filters before actually making the SQL call to the database.
Note _entities is our Entity Framework object.

/// <summary>
/// Gets unpaged, filtered alerts for display in excel format.
/// </summary>
/// <param name="TypeId"></param>
/// <param name="SeverityId"></param>
/// <returns></returns>
public IEnumerable<ExcelAlert> GetFilteredItemsForExcel (int? TypeId, int? SeverityId)
{
var excelFormatedAlerts = from a in GetFilteredAlerts(TypeId, SeverityId)
           select new ExcelAlert

                {
                     AlertLevel = a.AlertLevel.AlertLevelName,
                     AlertId = a.AlertKey,
                     BatchId = a.BatchKey,
                     CounterpartyName = a.Counterparty.CounterpartyName,
                     AlertType = a.AlertType.AlertTypeName,
                     AlertDescription = a.AlertMessage,
                     Timestamp = a.CreateDate
                 };
return excelFormatedAlerts;
}

/// <summary>
/// Return IQueryable of filtered Alerts
/// </summary>
/// <param name="TypeIdFilter"></param>
/// <param name="SeverityIdFilter"></param>
/// <returns></returns>
public IQueryable<Alert> GetFilteredAlerts(int? TypeIdFilter, int? SeverityIdFilter)
{
    var query = from a in _entities.Alerts.Include("Batch").Include("Counterparty")
                select a;
    if (TypeIdFilter != null)
        query = query.Where(a => a.AlertTypeKey == TypeIdFilter);
    if (SeverityIdFilter != null)
        query = query.Where(a => a.AlertLevelKey == SeverityIdFilter);

    return query.Where(a => !a.IsResolved);
}

View

We use the helper methods created above to simplify our view code. Notice we pass our filter id’s to the Action. We need these to ensure that we get the same data that is currently displayed in the WebGrid. If you are not filtering your WebGrid these are not necessary. The Action is “GetExcelFile” and the Controller is “ExcelGen”. Note that the WebGrid is paged in the screenshot; however, the export to Excel will export the entire set of data – not just the current page.

@Html.ImageActionLink(Html.IconURL(Icons.Excel), “Generate Excel File”, “GetExcelFile”, “ExcelGen”,
new { TypeId = Model.TypeIdFilter, SeverityId = Model.SeverityIdFilter }, null, null)

The ImageActionLink helper creates the Excel icon that is circled in the image below.

Controller

Our controller takes three parameters for filtering the larger dataset. If you are not filtering the WebGrid you do not need to accept any parameters. In this sample code I am getting a list of system alerts back from the AlertModel class. Once the alerts are processed into CSV format we return alerts.csv to the user.
/// <summary> Returns an excel file of items that are filtered but not sorted or paged.
/// </summary>
/// <param name="id"></param>
/// <param name="TypeId"></param>
/// <param name="SeverityId"></param>
/// <returns></returns>
public FileResult GetExcelFile(int? TypeId, int? SeverityId)
{
    using (AlertModel model = new AlertModel(_isAdmin))
    {
        var alerts = model.GetFilteredItemsForExcel(TypeId, SeverityId);
        string result = alerts.ToCsv();
        return File(System.Text.Encoding.ASCII.GetBytes(result), "application/excel", "alerts.csv");
     }
}
An IE8 user will see a dialog similar to the one below:

When the user clicks Open they will see something like the following in Excel.

Notice that that the CSV file is not formatted. The user will need to resize columns and add any additional formatting that they need.

Conclusion

There is not an out of the box solution from Microsoft to export data in a WebGrid to Excel but this article shows that there is a fairly straightforward way to export your IEnumerable data to a CSV file that Excel can open. As with any solution there are a few things that can improved on such as the headers that are shown in Excel but one of the nice things about this solution is that it can be reused for other things as well. There is not a requirement to have a WebGrid at all. The users could export to Excel before even seeing the “preview” in the WebGrid

Migrating Authentication and Identity From ASP.NET MVC 5 to MVC 6

In the previous article we migrated configuration from an ASP.NET MVC 5 project to MVC 6. In this article, we migrate the registration, login, and user management features.
This article covers the following topics:
  • Configure Identity and Membership
  • Migrate Registration and Login Logic
  • Migrate User Management Features
You can download the finished source from the project created in this article HERE (TODO).

Configure Identity and Membership

In ASP.NET MVC 5, authentication and identity features are configured in Startup.Auth.cs and IdentityConfig.cs, located in the App_Start folder. In MVC 6, these features are configured in Startup.cs. Before pulling in the required services and configuring them, we should add the required dependencies to the project. Open project.json and add “Microsoft.AspNet.Identity.EntityFramework” and “Microsoft.AspNet.Identity.Cookies” to the list of dependencies:
"dependencies": {
        "Microsoft.AspNet.Server.IIS": "1.0.0-beta3",
        "Microsoft.AspNet.Mvc": "6.0.0-beta3",
        "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta3",
        "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-beta3",
        "Microsoft.AspNet.Security.Cookies": "1.0.0-beta3"
},
Now, open Startup.cs and update the ConfigureServices() method to use Entity Framework and Identity services:
public void ConfigureServices(IServiceCollection services)
{
        // Add EF services to the services container.
        services.AddEntityFramework(Configuration)
                .AddSqlServer()
                .AddDbContext<ApplicationDbContext>();

        // Add Identity services to the services container.
        services.AddIdentity<ApplicationUser, IdentityRole>(Configuration)
                .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddMvc();
}
At this point, there are two types referenced in the above code that we haven’t yet migrated from the MVC 5 project: ApplicationDbContext and ApplicationUser. Create a new Models folder in the MVC 6 project, and add two classes to it corresponding to these types. You will find the MVC 5 versions of these classes in /Models/IdentityModels.cs, but we will use one file per class in the migrated project since that’s more clear.
ApplicationUser.cs:
using Microsoft.AspNet.Identity;

namespace NewMvc6Project.Models
{
        public class ApplicationUser : IdentityUser
        {
        }
}
ApplicationDbContext.cs:
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Data.Entity;

namespace NewMvc6Project.Models
{
        public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
        {
                private static bool _created = false;
                public ApplicationDbContext()
                {
                        // Create the database and schema if it doesn't exist
                        // This is a temporary workaround to create database until Entity Framework database migrations
                        // are supported in ASP.NET 5
                        if (!_created)
                        {
                                Database.AsMigrationsEnabled().ApplyMigrations();
                                _created = true;
                        }
                }

                protected override void OnConfiguring(DbContextOptions options)
                {
                        options.UseSqlServer();
                }
        }
}
The MVC 5 Starter Web project doesn’t include much customization of users, or the ApplicationDbContext. When migrating a real application, you will also need to migrate all of the custom properties and methods of your application’s user and DbContext classes, as well as any other Model classes your application utilizes (for example, if your DbContext has a DbSet<Album>, you will of course need to migrate the Album class).
With these files in place, the Startup.cs file can be made to compile by updating its using statements:
using Microsoft.Framework.ConfigurationModel;
using Microsoft.AspNet.Hosting;
using NewMvc6Project.Models;
using Microsoft.AspNet.Identity;
Our application is now ready to support authentication and identity services - it just needs to have these features exposed to users.

Migrate Registration and Login Logic

With identity services configured for the application and data access configured using Entity Framework and SQL Server, we are now ready to add support for registration and login to the application. Recall that earlier in the migration process we commented out a reference to _LoginPartial in _Layout.cshtml. Now it’s time to return to that code, uncomment it, and add in the necessary controllers and views to support login functionality.
Update _Layout.cshtml; uncomment the @Html.Partial line:
                        <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
                </ul>
                @*@Html.Partial("_LoginPartial")*@
        </div>
</div>
Now, add a new MVC View Page called _LoginPartial to the Views/Shared folder:
Update _LoginPartial.cshtml with the following code (replace all of its contents):
@using System.Security.Principal

@if (User.Identity.IsAuthenticated)
{
    using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))
    {
        @Html.AntiForgeryToken()
        <ul class="nav navbar-nav navbar-right">
            <li>
                @Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage" })
            </li>
            <li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
        </ul>
    }
}
else
{
    <ul class="nav navbar-nav navbar-right">
        <li>@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
        <li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
    </ul>
}
At this point, you should be able to refresh the site in your browser.

Summary

ASP.NET 5 and MVC 6 introduce changes to the ASP.NET Identity 2 features that shipped with ASP.NET MVC 5. In this article, you have seen how to migrate the authentication and user management features of an ASP.NET MVC 5 project to MVC 6.

Migrating Configuration From ASP.NET MVC 5 to MVC 6


In the previous article we began migrating an ASP.NET MVC 5 project to MVC 6. In this article, we migrate the configuration feature from ASP.NET MVC 5 to ASP.NET MVC 6.
In this article:
  • Set up Configuration
  • Migrate Configuration Settings from web.config
You can download the finished source from the project created in this article HERE (TODO).

Set up Configuration

ASP.NET 5 and ASP.NET MVC 6 no longer use the Global.asax and Web.config files that previous versions of ASP.NET utilized. In earlier versions of ASP.NET, application startup logic was placed in an Application_StartUp() method within Global.asax. Later, in ASP.NET MVC 5, a Startup.cs file was included in the root of the project, and was called using an OwinStartupAttribute when the application started. ASP.NET 5 (and ASP.NET MVC 6) have adopted this approach completely, placing all startup logic in the Startup.cs file.
The web.config file has also been replaced in ASP.NET 5. Configuration itself can now be configured, as part of the application startup procedure described in Startup.cs. Configuration can still utilize XML files, if desired, but typically ASP.NET 5 projects will place configuration values in a JSON-formatted file, such as config.json. ASP.NET 5’s configuration system can also easily access environment variables, which can provide a more secure and robust location for environment-specific values. This is especially true for secrets like connection strings and API keys that should not be checked into source control.
For this article, we are starting with the partially-migrated ASP.NET MVC 6 project from the previous article. To configure Configuration using the default MVC 6 settings, add the following constructor to the Startup.cs class in the root of the project:
public IConfiguration Configuration { get; set; }

public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
        var configurationBuilder = new ConfigurationBuilder()
                .SetBasePath(appEnv.ApplicationBasePath)
                .AddJsonFile("config.json");
        Configuration = configurationBuilder.Build();
}
Note that at this point the Startup.cs file will not compile, as we still need to add some using statements and pull in some dependencies. Add the following two using statements:
using Microsoft.Framework.Configuration;
Next, open project.json and add the Microsoft.Framework.ConfigurationModel.Json dependency:
{
        "webroot": "wwwroot",
        "version": "1.0.0-*",
        "dependencies": {
                ...
                "Microsoft.Framework.Configuration": "1.0.0-beta8",
                "Microsoft.Framework.Configuration.Json": "1.0.0-beta8"
        },
        ...
}
Finally, add a config.json file to the root of the project.
../_images/add-config-json.png

Migrate Configuration Settings from Web.config

Our ASP.NET MVC 5 project included the required database connection string in Web.config, in the <connectionStrings> element. In our MVC 6 project, we are going to store this information in the config.json file. Open Config.json, and you should see that it already includes the following:
{
        "Data": {
                "DefaultConnection": {
                        "ConnectionString": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;"
                }
        }
}
Change the name of the Database from _CHANGE_ME. In the case of this migration, we are going to point to a new database, which we’ll name NewMvc6Project to match our migrated project name.

Summary

ASP.NET 5 places all Startup logic for the application in a single file in which necessary services and dependencies can be defined and configured. It replaces the web.config file with a flexible configuration feature that can leverage a variety of file formats, such as JSON, as well as environment variables.

Specifying a CORS Policy


Browser security prevents a web page from making AJAX requests to another domain. This restriction is called the same-origin policy, and prevents a malicious site from reading sensitive data from another site. However, sometimes you might want to let other sites make cross-origin requests to your web app.
Cross Origin Resource Sharing is a W3C standard that allows a server to relax the same-origin policy. Using CORS, a server can explicitly allow some cross-origin requests while rejecting others. This topic shows how to enable CORS in your ASP.NET MVC 6 application. (For background on CORS, see How CORS works.)

Add the CORS package

In your project.json file, add the following:
  "dependencies": {
    "Microsoft.AspNet.Cors": "6.0.0-beta8"
  },

Configure CORS

To configure CORS, call AddCors in the ConfigureServices method of your Startup class, as shown here:
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddCors(options =>
    {
        // Define one or more CORS policies
        options.AddPolicy("AllowSpecificOrigin",
            builder =>
            {
                builder.WithOrigins("http://example.com");
            });
    });
}
This example defines a CORS policy named “AllowSpecificOrigin” that allows cross-origin requests from “http://example.com” and no other origins. The lambda takes a CorsPolicyBuilder object. To learn more about the various CORS policy settings, see CORS policy options.

Apply CORS Policies

The next step is to apply the policies. You can apply a CORS policy per action, per controller, or globally for all controllers in your application.

Per action

Add the [EnableCors] attribute to the action. Specify the policy name.
public class HomeController : Controller
{
    [EnableCors("AllowSpecificOrigin")] 
    public IActionResult Index()
    {
        return View();
    }

Per controller

Add the [EnableCors] attribute to the controller class. Specify the policy name.
[EnableCors("AllowSpecificOrigin")]
public class HomeController : Controller
{

Globally

Add the CorsAuthorizationFilterFactory filter to the global filter collection:
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.Configure<MvcOptions>(options =>
    {
        options.Filters.Add(new CorsAuthorizationFilterFactory("AllowSpecificOrigin"));
    });
}
The precedence order is: Action, controller, global. Action-level policies take precedence over controller-level policies, and controller-level policies take precedence over global policies.

Disable CORS

To disable CORS for a controller or action, use the [DisableCors] attribute.
    [DisableCors]
    public IActionResult About()
    {
        return View();
    }