C#: Avoiding Performance Issues with Inserts in SQLite

Posted on December 15 2014 08:49 PM by jatten in SQLite, Database, C#, CodeProject   ||   Comments (0)

Coronado-Island Parking-Meter-320

If you are new to SQLite, you may well run across one of the most confounding of its implementation details the moment you attempt to do some sort of bulk or batch processing of inserts or updates.

What you will discover is that unless properly implemented, inserting or updating multiple records in a SQLite database can seem abysmally slow. Slow to the point of unsuitability in certain cases.

Not to fear, this has to do with some default (and not entirely improper) design choices in SQLite, for which there is an easy workaround.

Image by Lance McCord  |  Some Rights Reserved

SQLite is a wonderfully simple to use, cross-platform/open source database with terrific performance specs. It is a mature product, and, if we are to believe the estimates of SQLite.org, is the most widely deployed SQL database in the world.

SQLite manages to cram a host of mature, well-developed features into a compact and well-documented package, including full transaction support.

This transaction support, and the way it is implemented, has a significant impact on certain performance characteristics of SQLite.

Transactions by Default in SQLite

As stated previously, one of the selling points of SQLite, despite it being a simple, file-based database, is that it is fully transactional. What does this mean?

From Wikipedia:

A transaction comprises a unit of work performed within a database management system (or similar system) against a database, and treated in a coherent and reliable way independent of other transactions. Transactions in a database environment have two main purposes:

  1. To provide reliable units of work that allow correct recovery from failures and keep a database consistent even in cases of system failure, when execution stops (completely or partially) and many operations upon a database remain uncompleted, with unclear status.
  2. To provide isolation between programs accessing a database concurrently. If this isolation is not provided, the program's outcome are possibly erroneous.

A database transaction, by definition, must be atomic, consistent, isolated and durable.[1] Database practitioners often refer to these properties of database transactions using the acronym ACID.

Transactions provide an "all-or-nothing" proposition, stating that each work-unit performed in a database must either complete in its entirety or have no effect whatsoever. Further, the system must isolate each transaction from other transactions, results must conform to existing constraints in the database, and transactions that complete successfully must get written to durable storage.

SQLite is not alone, of course, in implementing transactions - in fact, transactions are a core concept in database design. However, the implementation of SQLite proposes that, unless otherwise specified, each individual write action against your database (any action through which you modify a record) is treated as an individual transaction.

In other words, if you perform multiple INSERTs (or UPDATEs, or DELETEs) in a "batch," each INSERT will be treated as a separate transaction by SQLite.

The trouble is, transactions carry processing overhead. When we decide we need to perform multiple INSERTs in a batch, we can run into some troubling performance bottlenecks.

Batch Processing in SQLite - A Console Example

If we are using SQLite from the SQLite Console, we can see exactly what I am talking about by running an easy insert script, and seeing how things go. For this example, I borrowed a few lines from the Chinook Database to create and populate a table of Artists. If you don't have the SQLite Command Line Console on your machine, install it now (see Installing and Using SQLite on Windows for details). Then copy the SQL script from my Gist on Github, paste it into a text file, and save the file in your user folder as create-insert-artists.sql.

The script should look like this in the text file before you save:

Paste the SQL Script Into a Text File and Save:
DROP TABLE IF EXISTS [Artist];
 
CREATE TABLE [Artist]
(
    [ArtistId] INTEGER  NOT NULL,
    [Name] NVARCHAR(120),
    CONSTRAINT [PK_Artist] PRIMARY KEY  ([ArtistId])
);
 
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (1, 'AC/DC');
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (2, 'Accept');
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (3, 'Aerosmith');
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (4, 'Alanis Morissette');
 
-- . . . A bunch more artist records . . . 
 
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (273, 'C. Monteverdi, Nigel Rogers 
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (274, 'Nash Ensemble');
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (275, 'Philip Glass Ensemble');

 

If we open a new database in the SQLite Console (navigate to your User folder to do this for our purposes here) and read the script, we can see how long it takes. There are 275 Artist records in the script to be INSERTED.

Run SQLite3, Open a New Database, and Read the Artists Script:
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
C:\Users\John>sqlite3
SQLite version 3.8.7.3 2014-12-05 22:29:24
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> .open txdemo.db
sqlite> .read create-insert-artists.sql

 

We can see that (depending on your machine - your mileage may vary) executing the script takes roughly 10 seconds. Inserting 275 records should NOT take 10 seconds. Ever.

Console Output from Running Script (Took Way Too Long!):

run-create-insert-artists-script-no-transactions

As mentioned previously, unless we tell it otherwise, SQLite will treat each of those INSERT commands as an individual transaction, which slows things WAAAYYYY DOOOOWWWWN. We can do better. We tell SQLite to override this behavior by explicitly specifying our own transaction, beginning before the INSERT batch, and committing after each INSERT batch.

Add Transactions to SQLite Scripts Using BEGIN and COMMIT

When we are executing batches of INSERTs, UPDATEs, or DELETEs in a script, wrap all the writes against each table up in a transaction using the BEGIN and COMMIT SQLite Keywords. Modify the create-insert-artists.sql script in out text file by adding a BEGIN before the table INSERTs, and a COMMIT after the table inserts (for scripts involving more than one table, do this for the INSERTs for each table):

Modified Script Wraps INSERTs in single transaction:
DROP TABLE IF EXISTS [Artist];
 
CREATE TABLE [Artist]
(
    [ArtistId] INTEGER  NOT NULL,
    [Name] NVARCHAR(120),
    CONSTRAINT [PK_Artist] PRIMARY KEY  ([ArtistId])
);
 
BEGIN;
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (1, 'AC/DC');
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (2, 'Accept');
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (3, 'Aerosmith');
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (4, 'Alanis Morissette');
 
-- . . . A bunch more artist records . . . 
 
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (273, 'C. Monteverdi, Nigel Rogers 
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (274, 'Nash Ensemble');
INSERT INTO [Artist] ([ArtistId], [Name]) VALUES (275, 'Philip Glass Ensemble');
COMMIT;

 

If we re-run our script now, we see a significant performance boost. In fact, the script execution is nearly immediate.

Re-Run the Script in the SQLite Console (this time, with a Transaction):

run-create-insert-artists-script-with-transaction

The above will apply to all INSERTs, UPDATEs, and DELETEs when you execute scripts in the SQLite console.

Improve SQLite Performance in Your .NET Application Using Transactions

We see a similar problem when we use SQLite in a .NET application, and the solution is conceptually the same, although the implementation is necessarily a little different. If you are new to using SQLite (and many .NET developers are, at some point), this is exactly the type of confounding quirk that can have you running back to yet another "integrated" Microsoft database solution before giving this great database a chance. "I tried SQLite, but the inserts and updates were too damn slow . . ."

Sample .NET Application - The Slow, Hard Way

Consider the following Console application example. It is a small, simplistic example, and has no exception handling, but you get the idea. The Main() method performs some basic set-up, then builds a List<User> which is passed to the AddUsers() method.

Program to Insert a List of Users Using System.Data.SQLite:
class Program
{
    static string _connectionString;
    static void Main(string[] args)
    {
        // 'Data' directory in the current directory ( ..\bin\Debug\):
        string dbDirectory = Environment.CurrentDirectory;
        string dbName = "test.db";
 
        // Add System.IO to the using statements at the top of your code file:
        string dbPath = Path.Combine(dbDirectory, dbName);
        _connectionString = string.Format("Data Source = {0}", dbPath);
 
        CreateDbIfNotExists(dbPath);
        CreateUsersTable();
 
        int qtyToAdd = 100;
 
        // Load some users into a list...
        var usersToAdd = new List<User>();
        for(int i = 0; i < qtyToAdd; i++)
        {
            usersToAdd.Add(new User { Name = "User #" + i });
        }
 
        // And THEN add them:
        var sw = new System.Diagnostics.Stopwatch(); ;
        sw.Start();
        int qtyAdded = AddUsers(usersToAdd);
        sw.Stop();
 
        Console.WriteLine("Added {0} Users successfully in {1} ms", 
        	qtyAdded, sw.ElapsedMilliseconds);
 
        var allUsers = ReadUsers();
 
        Console.WriteLine("Read {0} Users from SQLite", allUsers.Count());
        Console.Read();
    }
 
 
    static void CreateDbIfNotExists(string dbPath)
    {
        string directory = Path.GetDirectoryName(dbPath);
        if (!File.Exists(dbPath))
        {
            // Creates directory if it doesn't already exist:
            Directory.CreateDirectory(directory);
 
            // Creates file if it doesn't already exist:
            SQLiteConnection.CreateFile(dbPath);
        }
    }
 
 
    static SQLiteConnection CreateConnection()
    {
        return new SQLiteConnection(_connectionString);
    }
 
 
    static void CreateUsersTable()
    {
        string sqlTestTable =
            @"CREATE TABLE IF NOT EXISTS Users 
            ( 
                Id INTEGER PRIMARY KEY AUTOINCREMENT, 
                Name TEXT NOT NULL 
            )";
 
        using (var cn = new SQLiteConnection(_connectionString))
        {
            using (var cmd = new SQLiteCommand(sqlTestTable, cn))
            {
                cn.Open();
                cmd.ExecuteNonQuery();
                cn.Close();
            }
        }
    }
 
 
    class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
 
 
    static int AddUsers(IEnumerable<User> users)
    {
        var results = new List<int>();
        string sqlInsertUsers =
            @"INSERT INTO Users (Name) VALUES (@0);";
 
        using (var cn = new SQLiteConnection(_connectionString))
        {
            // Open the connection, and also atransaction:
            cn.Open();
            using(var transaction = cn.BeginTransaction())
            {
                foreach (var user in users)
                {
                    using (var cmd = cn.CreateCommand())
                    {
                        cmd.CommandText = sqlInsertUsers;
                        cmd.Parameters.AddWithValue("@0", user.Name);
                        results.Add(cmd.ExecuteNonQuery());
                    }
                }
                transaction.Commit();
            }
            cn.Close();
        }
        return results.Sum();
    }
}

 

The AddUsers() method creates a connection and a command, opens the connection, and then iterates over the IEnumerable<User>, successively inserting the user data for each into the SQLite database. We are using a System.Diagnostics.Stopwatch to time the execution of the call to AddUsers() from Main().

It looks like we've done everything right here - we set up the connection only once, open it only once (opening and closing connections for each loop iteration causes its own performance hit). However, it still takes upwards of four seconds to insert only 100 users. We can see the results in our console output.

Console Output from Example Program Inserting 100 Users:

add-users-no-transaction-dotnet

Pretty lame, but not surprising, given what we have learned about transactionality defaults in SQLite. but, once again, we can do better.

Wrap SQLite Batch Operations in an ADO Transaction in Your .NET Application

Similar to using the SQLite console, the solution here is also to use a transaction. We can modify the code in the AddUsers() method as follows:

Modified Code for AddUsers() Method Wrapping Command Execution in a Transaction:
static int AddUsers(IEnumerable<User> users)
{
    var results = new List<int>();
    string sqlInsertUsers =
        @"INSERT INTO Users (Name) VALUES (@0);";
 
    using (var cn = new SQLiteConnection(_connectionString))
    {
        // Open the connection, and also atransaction:
        cn.Open();
        using(var transaction = cn.BeginTransaction())
        {
            foreach (var user in users)
            {
                using (var cmd = cn.CreateCommand())
                {
                    cmd.CommandText = sqlInsertUsers;
                    cmd.Parameters.AddWithValue("@0", user.Name);
                    results.Add(cmd.ExecuteNonQuery());
                }
            }
            transaction.Commit();
        }
        cn.Close();
    }
    return results.Sum();
}

 

With that, if we run our application again, we see an order of magnitude performance improvement:

Improved SQLite Insert Performance Using Transaction in .NET:

add-users-with-transaction-dotnet

Yep. 52 milliseconds, down from over 4,000 milliseconds.

Be Cautious with Your Transactions in SQLite

We've seen how we can realize some serious performance wins in SQLite by using transactions to wrap up bulk operations. However, let's not put the cart before the horse without thinking it through. Sometimes, you actually need a more granular level of transaction to ensure data integrity.

It simply would not do to maximize performance of a banking application if transactions were implemented only at the top level of a batch operation. After all, transactions in the world of relational databases are first and foremost about creating assurance that an operation succeed in its entirety, or not at all.

Additional Resources and Items of Interest

 

Posted on December 15 2014 08:49 PM by jatten     

Comments (0)

ASP.NET Web Api and Identity 2.0 - Customizing Identity Models and Implementing Role-Based Authorization

Posted on October 26 2014 03:23 PM by jatten in ASP.Net, C#, ASP.NET MVC, CodeProject   ||   Comments (11)

hermself 240In a previous post, we took a high-level look at using Identity 2.0 in the context of a Web Api application. We essentially poked and prodded the default Visual Studio Web Api project template, learned where things live, and got a basic sense for how it all is supposed to work.

However, the VS project template is very basic, using Bearer Tokens as the primary authentication mechanism, and does not offer any out-of-the-box support for advanced authorization scenarios. In a nutshell, the VS Project template affords us basic token-based authentication, and that's about it.

Additionally, the User model is simplistic, and there are no Role models defined. This may be intentional, since in some cases you may be using a Web Api project as a simple authentication service. In other scenarios, though, you may want to customize the User model and/or add Role-Based Authentication to the mix.

Image by madamepsychosis  | Some Rights Reserved

We have previously looked at customizing User and Role models in the context of an ASP.NET MVC application, and how we need to modify the stock MVC project to accommodate these customizations. In this post, we will do the same for a Web Api project.

You can find the source code for the example Web Api project on Github:

Consider Your Use Case Before Deciding on Your Auth Strategy

There are a number of options for Authentication and Authorization strategies in a Web Api project. Use of Bearer tokens and Role-Based Authentication is relatively simple to implement, but is not the most advanced architecture for an authorization solution. Before deciding upon traditional Role-Based Authorization, you may want to examine the scope of your authentication and authorization needs, and determine if something simpler, or something more advanced, may be warranted. 

ASP.NET Web Api can take full advantage of Claims-Based Authorization, which, for more complex systems, may be a better choice. Similarly, as mentioned previously, if the primary purpose of your Web Api is to act as an Authentication Service, you may want to go with a more robust token system (for example, shared private keys as opposed to the bearer tokens used by default), and do away with authorization at this level.

Role-Based Authorization is a good fit in a project where there exists a modest need for different levels of authorization/access, and possibly the Web Api is a part of, or associated with, a larger MVC or other ASP.NET site where Roles are used to govern authorization. Consider a standard MVC project, in which a few roles are sufficient to manage authorization, and which serves web pages as well as offers API access.

Applying What We've Learned Previously

Fortunately, much of what we are about to do, we have seen previously, and we can even borrow bits and pieces of code we've already written. If you are just getting familiar with Identity 2.0, feel free to review previous posts in which we performed some similar customization of Users and Roles in the context of an ASP.NET MVC project:

Now that we have some idea what we are dealing with, let's see how we can apply it in the Web Api context.

Getting Started - Create a New ASP.NET Web Api Project

First, in Visual Studio, create a new ASP.NET Web Api project. Once the project is created, update the Nuget packages in the solution, either using Manage Packages for Solution in the context menu for Solution Explorer, or by using Update-Package in the Package Manager Console.

This will update all the nuget packages, and specifically update Web Api to version 2.2.

Adding a Role Model, and Customizing ApplicationUser

To get started, let's take another look at the Models => IdentityModes.cs file. Currently, there is not a lot there:

The Default IdentityModels.cs File in Web Api:
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
 
namespace AspNetIdentity2WebApiCustomize.Models
{
    public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(
        		UserManager<ApplicationUser> manager, string authenticationType)
        {
            // Note the authenticationType must match the one defined in 
            // CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
            // Add custom user claims here
            return userIdentity;
        }
    }
 
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
        }
        
        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
    }
}

 

As we did in our ASP.NET MVC examples, we begin by modifying and adding to the existing models defined in Models => IdentityModels.cs. In fact, since we did a lot of the work previously, we will start by stealing the IdentityModels.cs code from the ASP.NET Extensible Template Project. Careful here. We can save ourselves some pain by pasting the classes into the existing namespace defined in the current code file, and leaving the using statements as they are for the moment:

Updated IdentityModels.cs Code:
// You will not likely need to customize there, but it is necessary/easier to create our own 
// project-specific implementations, so here they are:
public class ApplicationUserLogin : IdentityUserLogin<string> { }
public class ApplicationUserClaim : IdentityUserClaim<string> { }
public class ApplicationUserRole : IdentityUserRole<string> { }
 
// Must be expressed in terms of our custom Role and other types:
public class ApplicationUser 
    : IdentityUser<string, ApplicationUserLogin, 
    ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationUser()
    {
        this.Id = Guid.NewGuid().ToString();
 
        // Add any custom User properties/code here
    }
 
 
    public async Task<ClaimsIdentity>
        GenerateUserIdentityAsync(ApplicationUserManager manager)
    {
        var userIdentity = await manager
            .CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        return userIdentity;
    }
}
 
 
// Must be expressed in terms of our custom UserRole:
public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
    public ApplicationRole() 
    {
        this.Id = Guid.NewGuid().ToString();
    }
 
    public ApplicationRole(string name)
        : this()
    {
        this.Name = name;
    }
 
    // Add any custom Role properties/code here
}
 
 
// Must be expressed in terms of our custom types:
public class ApplicationDbContext 
    : IdentityDbContext<ApplicationUser, ApplicationRole, 
    string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }
 
    static ApplicationDbContext()
    {
        Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
    }
 
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
    // Add additional items here as needed
}
 
// Most likely won't need to customize these either, but they were needed because we implemented
// custom versions of all the other types:
public class ApplicationUserStore 
    :UserStore<ApplicationUser, ApplicationRole, string,
        ApplicationUserLogin, ApplicationUserRole, 
        ApplicationUserClaim>, IUserStore<ApplicationUser, string>, 
    IDisposable
{
    public ApplicationUserStore()
        : this(new IdentityDbContext())
    {
        base.DisposeContext = true;
    }
 
    public ApplicationUserStore(DbContext context)
        : base(context)
    {
    }
}
 
 
public class ApplicationRoleStore
: RoleStore<ApplicationRole, string, ApplicationUserRole>,
IQueryableRoleStore<ApplicationRole, string>,
IRoleStore<ApplicationRole, string>, IDisposable
{
    public ApplicationRoleStore()
        : base(new IdentityDbContext())
    {
        base.DisposeContext = true;
    }
 
    public ApplicationRoleStore(DbContext context)
        : base(context)
    {
    }
}

 

Now, we need to add a few additional using statements at the top of the code file to bring in some references we need with the new code. Add the following to the using statements at the top of the file:

Additional Using Statements Added to IdentityModels.cs:
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
using System;

 

There are two immediate problems with the code we just pasted in there. The first is probably obvious, because the VS compiler is probably telling you that there is no DBInitializer class defined. Also, if you Build the project, a few other problems will surface in the VS error list. We'll take care of that in a moment.

The other is not so obvious. The code we pasted in here is from an MVC project. For the most part this is fine. However, our ApplicationUser class defines a method GenerateUserIdentityAsync. The code we stole from our MVC project has this method, but defines it in terms of a single parameter of type ApplicationUserManager. Recall the code we pasted over, which defined ApplicationUser with two constructor parameters. The missing parameter in our newly copied method is of type string, and represents the authenticationType.

This is important, because GenerateUserIdentityAsync is called when we need to retrieve a user's ClaimsIdentity, which represents the various claims the specific user has within our system.

Confused yet? We don't need to worry about the details of ClaimsIdentity just yet. What we DO need to do is update the GenerateUserIdentityAsync method defined on ApplicationUser to accept a string parameter representing the authenticationType.

Update ApplicationUser for Web Api

To make our ApplicationUser class ready for use in a Web Api context, we can replace the code for the GenerateUserIdentityAsync  method with the following:

Update GenerateUserIdentityAsync with Authentication Type Parameter:
// ** Add authenticationtype as method parameter:
public async Task<ClaimsIdentity>
    GenerateUserIdentityAsync(ApplicationUserManager manager, string authenticationType)
{
    // Note the authenticationType must match the one defined 
    // in CookieAuthenticationOptions.AuthenticationType
    var userIdentity = 
        await manager.CreateIdentityAsync(this, authenticationType);
    // Add custom user claims here
    return userIdentity;
}

 

                            Adding a DBInitializer and Other Identity Config Items

                            We mentioned earlier, and the compiler is helpfully pointing out to you, that the code we stole from the Identity Extensible Template project is attempting to use a DBInitializer class that doesn't exist (yet) in our Web Api project. Also, you probably notice (if you have built the project since adding the additional Identity Models), that there appear to be some problems with our new ApplicationUser class.

                            We will resolve most of these issues by once again stealing select bits of code from the Identity Extensible Template project.

                            If we look at the App_Start => IdentityConfig.cs file in our Web Api project, we see that, as with the original IdentityModels.cs file, there is not much there:

                            The Default Identity.config File from a Web Api Project:
                            using System.Threading.Tasks;
                            
                            using Microsoft.AspNet.Identity;
                            
                            using Microsoft.AspNet.Identity.EntityFramework;
                            
                            using Microsoft.AspNet.Identity.Owin;
                            
                            using Microsoft.Owin;
                            
                            using AspNetIdentity2WebApiCustomize.Models;
                            
                             
                            
                            namespace AspNetIdentity2WebApiCustomize
                            
                            {
                            
                                // Configure the application user manager used in this application. UserManager 
                            
                                // is defined in ASP.NET Identity and is used by the application.
                            
                                public class ApplicationUserManager : UserManager<ApplicationUser>
                            
                                {
                            
                                    public ApplicationUserManager(IUserStore<ApplicationUser> store)
                            
                                        : base(store)
                            
                                    {
                            
                                    }
                            
                             
                            
                                    public static ApplicationUserManager Create(
                            
                                            IdentityFactoryOptions<ApplicationUserManager> options, 
                            
                                            IOwinContext context)
                            
                                    {
                            
                                        var manager = 
                            
                                                new ApplicationUserManager(
                            
                                                        new UserStore<ApplicationUser>(
                            
                                                                context.Get<ApplicationDbContext>()));
                            
                             
                            
                                        // Configure validation logic for usernames
                            
                                        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
                            
                                        {
                            
                                            AllowOnlyAlphanumericUserNames = false,
                            
                                            RequireUniqueEmail = true
                            
                                        };
                            
                                        // Configure validation logic for passwords
                            
                                        manager.PasswordValidator = new PasswordValidator
                            
                                        {
                            
                                            RequiredLength = 6,
                            
                                            RequireNonLetterOrDigit = true,
                            
                                            RequireDigit = true,
                            
                                            RequireLowercase = true,
                            
                                            RequireUppercase = true,
                            
                                        };
                            
                                        var dataProtectionProvider = options.DataProtectionProvider;
                            
                                        if (dataProtectionProvider != null)
                            
                                        {
                            
                                            manager.UserTokenProvider = 
                            
                                                    new DataProtectorTokenProvider<ApplicationUser>(
                            
                                                            dataProtectionProvider.Create("ASP.NET Identity"));
                            
                                        }
                            
                                        return manager;
                            
                                    }
                            
                                }
                            
                            }

                             

                            In order to work with Roles in our Web Api project, we will need an ApplicationRoleManager, and as mentioned previously, we will be adding the ApplicationDbInitializer from the Extensible Template project.

                            First, we need the following using statements at the top of the IdentityConfig.cs file:

                            Using Statements for the IdentityConfig.cs File:
                            using AspNetIdentity2WebApiCustomize.Models;
                            
                            using Microsoft.AspNet.Identity;
                            
                            using Microsoft.AspNet.Identity.EntityFramework;
                            
                            using Microsoft.AspNet.Identity.Owin;
                            
                            using Microsoft.Owin;
                            
                            using System.Data.Entity;
                            
                            using System.Web;

                             

                            Now, add the ApplicationRoleManager and ApplicationDbInitializer classes from the Extensible Template project to our IdentityConfig.cs file:

                            Add ApplicationRoleManager and ApplicationDbInitializer to IdentityConfig.cs:
                            using AspNetIdentity2WebApiCustomize.Models;
                            
                            using Microsoft.AspNet.Identity;
                            
                            using Microsoft.AspNet.Identity.EntityFramework;
                            
                            using Microsoft.AspNet.Identity.Owin;
                            
                            using Microsoft.Owin;
                            
                            using System.Data.Entity;
                            
                            using System.Web;
                            
                             
                            
                             
                            
                            namespace AspNetIdentity2WebApiCustomize
                            
                            {
                            
                                public class ApplicationUserManager : UserManager<ApplicationUser>
                            
                                {
                            
                                    public ApplicationUserManager(IUserStore<ApplicationUser> store)
                            
                                        : base(store)
                            
                                    {
                            
                                    }
                            
                             
                            
                                    public static ApplicationUserManager Create(
                            
                                        IdentityFactoryOptions<ApplicationUserManager> options, 
                            
                                        IOwinContext context)
                            
                                    {
                            
                                        var manager = new ApplicationUserManager(
                            
                                            new UserStore<ApplicationUser>(
                            
                                                context.Get<ApplicationDbContext>()));
                            
                             
                            
                                        // Configure validation logic for usernames
                            
                                        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
                            
                                        {
                            
                                            AllowOnlyAlphanumericUserNames = false,
                            
                                            RequireUniqueEmail = true
                            
                                        };
                            
                                        // Configure validation logic for passwords
                            
                                        manager.PasswordValidator = new PasswordValidator
                            
                                        {
                            
                                            RequiredLength = 6,
                            
                                            RequireNonLetterOrDigit = true,
                            
                                            RequireDigit = true,
                            
                                            RequireLowercase = true,
                            
                                            RequireUppercase = true,
                            
                                        };
                            
                                        var dataProtectionProvider = options.DataProtectionProvider;
                            
                                        if (dataProtectionProvider != null)
                            
                                        {
                            
                                            manager.UserTokenProvider = 
                            
                                                new DataProtectorTokenProvider<ApplicationUser>(
                            
                                                    dataProtectionProvider.Create("ASP.NET Identity"));
                            
                                        }
                            
                                        return manager;
                            
                                    }
                            
                                }
                            
                             
                            
                             
                            
                                public class ApplicationRoleManager : RoleManager<ApplicationRole>
                            
                                {
                            
                                    public ApplicationRoleManager(IRoleStore<ApplicationRole, string> roleStore)
                            
                                        : base(roleStore)
                            
                                    {
                            
                                    }
                            
                             
                            
                                    public static ApplicationRoleManager Create(
                            
                                        IdentityFactoryOptions<ApplicationRoleManager> options, 
                            
                                        IOwinContext context)
                            
                                    {
                            
                                        return new ApplicationRoleManager(
                            
                                            new ApplicationRoleStore(context.Get<ApplicationDbContext>()));
                            
                                    }
                            
                                }
                            
                             
                            
                             
                            
                                public class ApplicationDbInitializer 
                            
                                    : DropCreateDatabaseAlways<ApplicationDbContext>
                            
                                {
                            
                                    protected override void Seed(ApplicationDbContext context)
                            
                                    {
                            
                                        InitializeIdentityForEF(context);
                            
                                        base.Seed(context);
                            
                                    }
                            
                             
                            
                                    //Create User=Admin@Admin.com with password=Admin@123456 in the Admin role        
                            
                                    public static void InitializeIdentityForEF(ApplicationDbContext db)
                            
                                    {
                            
                                        var userManager = HttpContext.Current
                            
                                            .GetOwinContext().GetUserManager<ApplicationUserManager>();
                            
                             
                            
                                        var roleManager = HttpContext.Current
                            
                                            .GetOwinContext().Get<ApplicationRoleManager>();
                            
                             
                            
                                        const string name = "admin@example.com";
                            
                                        const string password = "Admin@123456";
                            
                                        const string roleName = "Admin";
                            
                             
                            
                                        //Create Role Admin if it does not exist
                            
                                        var role = roleManager.FindByName(roleName);
                            
                                        if (role == null)
                            
                                        {
                            
                                            role = new ApplicationRole(roleName);
                            
                                            var roleresult = roleManager.Create(role);
                            
                                        }
                            
                             
                            
                                        var user = userManager.FindByName(name);
                            
                                        if (user == null)
                            
                                        {
                            
                                            user = new ApplicationUser { UserName = name, Email = name };
                            
                                            var result = userManager.Create(user, password);
                            
                                            result = userManager.SetLockoutEnabled(user.Id, false);
                            
                                        }
                            
                             
                            
                                        // Add user admin to Role Admin if not already added
                            
                                        var rolesForUser = userManager.GetRoles(user.Id);
                            
                                        if (!rolesForUser.Contains(role.Name))
                            
                                        {
                            
                                            var result = userManager.AddToRole(user.Id, role.Name);
                            
                                        }
                            
                                    }
                            
                                }
                            
                            }

                             

                            Now, we need to make a few changes to our ApplicationUserManager. Since we have added our customizable models, including modified versions of UserStore and RoleStore, we need to adapt the ApplicationUserManager to play nice. We have expressed our models with different type arguments than the default implementation expected by the Web Api project. Specifically, we have employed a customized implementation of IUserStore. Rather than the concrete UserStore defined in Microsoft.AspNet.Identity.EntityFramework, we have implemented our own ApplicationUserStore, which is expressed in terms of specific type arguments.

                            We now need to tune up our ApplicationUserManager to work with our ApplicationUserStore.

                            Change the code for ApplicationUserManager in IdentityConfig.cs to the following:

                            Modified ApplicationUserManager:
                            public class ApplicationUserManager 
                            
                                : UserManager<ApplicationUser, string>
                            
                            {
                            
                                public ApplicationUserManager(IUserStore<ApplicationUser, string> store)
                            
                                    : base(store)
                            
                                {
                            
                                }
                            
                             
                            
                                public static ApplicationUserManager Create(
                            
                                    IdentityFactoryOptions<ApplicationUserManager> options, 
                            
                                    IOwinContext context)
                            
                                {
                            
                                    var manager = new ApplicationUserManager(
                            
                                        new UserStore<ApplicationUser, ApplicationRole, string, 
                            
                                            ApplicationUserLogin, ApplicationUserRole, 
                            
                                            ApplicationUserClaim>(context.Get<ApplicationDbContext>()));
                            
                             
                            
                                    // Configure validation logic for usernames
                            
                                    manager.UserValidator = new UserValidator<ApplicationUser>(manager)
                            
                                    {
                            
                                        AllowOnlyAlphanumericUserNames = false,
                            
                                        RequireUniqueEmail = true
                            
                                    };
                            
                             
                            
                                    // Configure validation logic for passwords
                            
                                    manager.PasswordValidator = new PasswordValidator
                            
                                    {
                            
                                        RequiredLength = 6,
                            
                                        RequireNonLetterOrDigit = true,
                            
                                        RequireDigit = true,
                            
                                        RequireLowercase = true,
                            
                                        RequireUppercase = true,
                            
                                    };
                            
                                    var dataProtectionProvider = options.DataProtectionProvider;
                            
                                    if (dataProtectionProvider != null)
                            
                                    {
                            
                                        manager.UserTokenProvider = 
                            
                                            new DataProtectorTokenProvider<ApplicationUser>(
                            
                                                dataProtectionProvider.Create("ASP.NET Identity"));
                            
                                    }
                            
                                    return manager;
                            
                                }
                            
                            }

                             

                            With that, the very minimal basics are in place for us to use our new, extensible model classes (including Roles, which were not directly available to us in the default Web Api implementation) in our Web Api project.

                            We do need to clean up one more issue though. The AccountController still appears to rely on Microsoft.AspNet.Identity.EntityFramework.IdentityUser, and we need it to use our new implementation ApplicationUser.

                            Modify AccountController to Use ApplicationUser

                            We can easily correct this last remaining issue. Open the AccountController class, and locate the GetManageInfo() method. We can see where a local variable user is declared, explicitly types as IdentityUser.

                            Below that, we can see in the foreach() loop, we explicitly declare an iterator variable linkedAccount as type IdentityUserLogin.

                            Existing Code in the Web Api AccountController GetManageInfo() Method:
                            [Route("ManageInfo")]
                            
                            public async Task<ManageInfoViewModel> GetManageInfo(
                            
                                string returnUrl, bool generateState = false)
                            
                            {
                            
                                IdentityUser user = 
                            
                                    await UserManager.FindByIdAsync(User.Identity.GetUserId());
                            
                                if (user == null)
                            
                                {
                            
                                    return null;
                            
                                }
                            
                             
                            
                                List<UserLoginInfoViewModel> logins = new List<UserLoginInfoViewModel>();
                            
                                foreach (IdentityUserLogin linkedAccount in user.Logins)
                            
                                {
                            
                                    logins.Add(new UserLoginInfoViewModel
                            
                                    {
                            
                                        LoginProvider = linkedAccount.LoginProvider,
                            
                                        ProviderKey = linkedAccount.ProviderKey
                            
                                    });
                            
                                }
                            
                             
                            
                                if (user.PasswordHash != null)
                            
                                {
                            
                                    logins.Add(new UserLoginInfoViewModel
                            
                                    {
                            
                                        LoginProvider = LocalLoginProvider,
                            
                                        ProviderKey = user.UserName,
                            
                                    });
                            
                                }
                            
                             
                            
                                return new ManageInfoViewModel
                            
                                {
                            
                                    LocalLoginProvider = LocalLoginProvider,
                            
                                    Email = user.UserName,
                            
                                    Logins = logins,
                            
                                    ExternalLoginProviders = GetExternalLogins(returnUrl, generateState)
                            
                                };
                            
                            }

                             

                            In both cases we have implemented our own versions of these types. Here, we can either change the declaration in each case to use the var keyword, which relieves us of the type constraint on the variable (but, some would argue, makes our code a bit ambiguous), or we can change the explicit type declaration in each case to use our own implementation.

                            For now, let's change the explicit type declaration to use our own implementations:

                            Modified Code for GetManageInfo() Method:
                            [Route("ManageInfo")]
                            
                            public async Task<ManageInfoViewModel> GetManageInfo(
                            
                                string returnUrl, bool generateState = false)
                            
                            {
                            
                                ApplicationUser user = 
                            
                                    await UserManager.FindByIdAsync(User.Identity.GetUserId());
                            
                                if (user == null)
                            
                                {
                            
                                    return null;
                            
                                }
                            
                             
                            
                                List<UserLoginInfoViewModel> logins = new List<UserLoginInfoViewModel>();
                            
                                foreach (ApplicationUserLogin linkedAccount in user.Logins)
                            
                                {
                            
                                    logins.Add(new UserLoginInfoViewModel
                            
                                    {
                            
                                        LoginProvider = linkedAccount.LoginProvider,
                            
                                        ProviderKey = linkedAccount.ProviderKey
                            
                                    });
                            
                                }
                            
                             
                            
                                if (user.PasswordHash != null)
                            
                                {
                            
                                    logins.Add(new UserLoginInfoViewModel
                            
                                    {
                            
                                        LoginProvider = LocalLoginProvider,
                            
                                        ProviderKey = user.UserName,
                            
                                    });
                            
                                }
                            
                             
                            
                                return new ManageInfoViewModel
                            
                                {
                            
                                    LocalLoginProvider = LocalLoginProvider,
                            
                                    Email = user.UserName,
                            
                                    Logins = logins,
                            
                                    ExternalLoginProviders = GetExternalLogins(returnUrl, generateState)
                            
                                };
                            
                            }

                             

                            Above, we have simply changed the declared type for the local user variable from IdentityUser to ApplicationUser, and the iterator variable linkedAccount from IdentityUserLogin to ApplicationUserLogin.

                            Add Initialization for ApplicationRoleManager in Startup.Auth

                            Recall from our high-level exploration of ASP.NET Web Api and Identity that initialization and configuration of Identity 2.0 occurs in the Startup class defined in App_Start => Startup.Auth.

                            As we have seen, the original VS Web Api template did not really provide for Role-Based anything, and consequently, provides no configuration or initialization for our recently added ApplicationRoleManager at startup. We need to add a line of initialization code to our Startup.Auth file:

                            Add Initialization for ApplicationRoleManager in Statup.Auth:
                            public void ConfigureAuth(IAppBuilder app)
                            
                            {
                            
                                app.CreatePerOwinContext(ApplicationDbContext.Create);
                            
                                app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
                            
                                app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
                            
                             
                            
                                app.UseCookieAuthentication(new CookieAuthenticationOptions());
                            
                                app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
                            
                             
                            
                                // Configure the application for OAuth based flow
                            
                                PublicClientId = "self";
                            
                                OAuthOptions = new OAuthAuthorizationServerOptions
                            
                                {
                            
                                    TokenEndpointPath = new PathString("/Token"),
                            
                                    Provider = new ApplicationOAuthProvider(PublicClientId),
                            
                                    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                            
                                    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
                            
                                    AllowInsecureHttp = true
                            
                                };
                            
                             
                            
                                // Enable the application to use bearer tokens to authenticate users
                            
                                app.UseOAuthBearerTokens(OAuthOptions);
                            
                             
                            
                                // ... Code for third-part logins omitted for brevity ...
                            
                            }

                             

                            We've added a single line, which initializes an instance of ApplicationRoleManager for each incoming request.

                            About the ApplicationDbInitializer

                            With the changes we've introduced so far, we should be able to take our new and improved Web Api project for a test spin to see if the most basic functionality works.

                            Before we do, though, we need to recognize that we have fundamentally changed how the EF/Code-First database generation has been changed with the introduction of our custom ApplicationDbInitializer.

                            Recall from our explorations of customizing an MVC project with extensible models, the ApplicationDbInitializer allows us to specify some options for how and when the database behind our application is generated, and to provide some initial data to work with.

                            As we move towards Role-Based Authorization and a more restrictive security model for our Api, this becomes important.

                            The way we currently have ApplicationDbInitializer configured, it derives from DbDropCreateDatabaseAlways, which means every time we run our application, the backing store will be destroyed, and re-created from scratch. We also have it set up to create a default User, and we assign that user to the Admin role. In this manner, we start our application with a user with Admin-level access permissions.

                            The default VS Web Api project doesn't take advantage of this out of the box. If we look at the class declaration for AccountController we see that the class itself is decorated with a simple [Authorize] attribute. What this essentially does is restrict access to all of the Action methods on the class to authorized users (except those methods specifically decorated with an [AllowAnonymous] attribute).

                            In other words, for now, any user who is registered, and who successfully signs in and presents a valid Bearer Token can access any of the Action methods on AccountController.

                            We'll take a closer look at implementing Role-Based Authentication momentarily, First, we will extend our ApplicationUser and ApplicationRole classes with some custom properties.

                            Adding Custom Properties to ApplicationUser and ApplicationRole

                            As we saw when we examine customizing Users and Roles within an MVC project, we will add a few simple properties to our ApplicationUser and ApplicationRole models. Modify the code for each as follows:

                            Add Custom Properties to ApplicationUser and ApplicationRole:
                            // Must be expressed in terms of our custom Role and other types:
                            
                            public class ApplicationUser
                            
                                : IdentityUser<string, ApplicationUserLogin,
                            
                                ApplicationUserRole, ApplicationUserClaim> 
                            
                            {
                            
                                public ApplicationUser()
                            
                                {
                            
                                    this.Id = Guid.NewGuid().ToString();
                            
                                }
                            
                             
                            
                             
                            
                                public async Task<ClaimsIdentity>GenerateUserIdentityAsync(
                            
                                    ApplicationUserManager manager, string authenticationType)
                            
                                {
                            
                                    // Note the authenticationType must match the one 
                            
                                    // defined in CookieAuthenticationOptions.AuthenticationType
                            
                                    var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
                            
                             
                            
                                    // Add custom user claims here
                            
                                    return userIdentity;
                            
                                }
                            
                             
                            
                                // Add Custom Properties:
                            
                                public string Address { get; set; }
                            
                                public string City { get; set; }
                            
                                public string PostalCode { get; set; }
                            
                            }
                            
                             
                            
                             
                            
                            // Must be expressed in terms of our custom UserRole:
                            
                            public class ApplicationRole : IdentityRole<string, ApplicationUserRole> 
                            
                            {
                            
                                public ApplicationRole() 
                            
                                {
                            
                                  this.Id = Guid.NewGuid().ToString();
                            
                                }
                            
                             
                            
                                public ApplicationRole(string name) : this() 
                            
                                {
                            
                                  this.Name = name;
                            
                                }
                            
                             
                            
                                // Add Custom Property:
                            
                                public string Description { get; set; }
                            
                            }

                             

                            Here, we have added an Address and related properties to ApplicationUser, and a simple Description property to ApplicationRole.

                            Now, let's update our ApplicationDbInitializer to set some sample values for these new properties. Update the code for the InitializeIdentityForEF() method as follows:

                            Set Initial Values for Custom Properties in ApplicationDbInitializer:
                            public static void InitializeIdentityForEF(ApplicationDbContext db)
                            
                            {
                            
                                var userManager = HttpContext.Current
                            
                                    .GetOwinContext().GetUserManager<ApplicationUserManager>();
                            
                             
                            
                                var roleManager = HttpContext.Current
                            
                                    .GetOwinContext().Get<ApplicationRoleManager>();
                            
                             
                            
                                const string name = "admin@example.com";
                            
                                const string password = "Admin@123456";
                            
                             
                            
                                // Some initial values for custom properties:
                            
                                const string address = "1234 Sesame Street";
                            
                                const string city = "Portland";
                            
                                const string state = "OR";
                            
                                const string postalCode = "97209";
                            
                             
                            
                                const string roleName = "Admin";
                            
                                const string roleDescription = "All access pass";
                            
                             
                            
                                //Create Role Admin if it does not exist
                            
                                var role = roleManager.FindByName(roleName);
                            
                                if (role == null)
                            
                                {
                            
                                    role = new ApplicationRole(roleName);
                            
                             
                            
                                    // Set the new custom property:
                            
                                    role.Description = roleDescription;
                            
                                    var roleresult = roleManager.Create(role);
                            
                                }
                            
                             
                            
                                var user = userManager.FindByName(name);
                            
                                if (user == null)
                            
                                {
                            
                                    user = new ApplicationUser { UserName = name, Email = name };
                            
                             
                            
                                    // Set the new custom properties:
                            
                                    user.Address = address;
                            
                                    user.City = city;
                            
                                    user.State = state;
                            
                                    user.PostalCode = postalCode;
                            
                             
                            
                                    var result = userManager.Create(user, password);
                            
                                    result = userManager.SetLockoutEnabled(user.Id, false);
                            
                                }
                            
                             
                            
                                // Add user admin to Role Admin if not already added
                            
                                var rolesForUser = userManager.GetRoles(user.Id);
                            
                                if (!rolesForUser.Contains(role.Name))
                            
                                {
                            
                                    var result = userManager.AddToRole(user.Id, role.Name);
                            
                                }
                            
                            }

                             

                            With that, we should be ready to see if everything at least works correctly . . .

                            Create a Simple Web Api Client Application

                            To see if everything is working properly to this point, we will create a simple console application as an Api client.

                            In Visual Studio, create a new Console Application, and the use the Manage Nuget Packages for Solutions to add the Microsoft Asp.NET Web Api 2.2 Client Libraries, or use the Package Manager Console and do:

                            Add Web Api 2.2 via the Nuget Package Manager Console:
                            PM> Install-Package Microsoft.AspNet.WebApi.Client

                             

                            Now that we have the required Web Api Client Libraries in our project, open the Program.cs file.

                            Make sure the following using statements are present at the top of the file. Note we have added references to System.Net.Http and Newtonsoft.Json, ad well as System.Threading:

                            Required Using Statements for Console Api Client Application:
                            using System;
                            
                            using System.Collections.Generic;
                            
                            using System.Linq;
                            
                            using System.Text;
                            
                            using System.Threading.Tasks;
                            
                            using System.Net.Http;
                            
                            using Newtonsoft.Json;

                             

                            Next, let's add some very basic client code used to retrieve a token from the token endpoint of our Web Api. Add the following within the Program class:

                            Add Client Code to Retreive Response from Web Api Token Endpoint:
                            // You will need to substitute your own host Url here:
                            
                            static string host = "http://localhost:63074/";
                            
                             
                            
                            static void Main(string[] args)
                            
                            {
                            
                                Console.WriteLine("Attempting to Log in with default admin user");
                            
                             
                            
                                // Get hold of a Dictionary representing the JSON in the response Body:
                            
                                var responseDictionary = 
                            
                                    GetResponseAsDictionary("admin@example.com", "Admin@123456");
                            
                                foreach(var kvp in responseDictionary)
                            
                                {
                            
                                    Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
                            
                                }
                            
                                Console.Read();
                            
                            }
                            
                             
                            
                             
                            
                            static Dictionary<string, string> GetResponseAsDictionary(
                            
                                string userName, string password)
                            
                            {
                            
                                HttpClient client = new HttpClient();
                            
                                var pairs = new List<KeyValuePair<string, string>>
                            
                                            {
                            
                                                new KeyValuePair<string, string>( "grant_type", "password" ), 
                            
                                                new KeyValuePair<string, string>( "username", userName ), 
                            
                                                new KeyValuePair<string, string> ( "Password", password )
                            
                                            };
                            
                                var content = new FormUrlEncodedContent(pairs);
                            
                             
                            
                                // Attempt to get a token from the token endpoint of the Web Api host:
                            
                                HttpResponseMessage response =
                            
                                    client.PostAsync(host + "Token", content).Result;
                            
                                var result = response.Content.ReadAsStringAsync().Result;
                            
                                // De-Serialize into a dictionary and return:
                            
                                Dictionary<string, string> tokenDictionary =
                            
                                    JsonConvert.DeserializeObject<Dictionary<string, string>>(result);
                            
                                return tokenDictionary;
                            
                            }
                            

                             

                            Note, at this point, we have added code the the Main() method, which calls out to a single, rather contrived method GetResponseAsDictionary() . All we are basically doing here is submitting an HTTP POST to the Token endpoint of our Web Api, and then de-serializing the JSON response body into a Dictionary<string, string> .

                            Once we have the Dictionary, we are iterating over each Key/Value pair, and writing the contents to the Console.

                            If everything has worked the way we expect, our Console output should be something along the lines of the following:

                            Console Output from Token Endpoint Response:
                            Attempting to Log in with default admin user
                            
                            access_token: AuzOQkgG3BYubP1rlljcPhAzW7R7gA4Vew8dHy_MScMn2-Rs3R6dNlwCU_SuFwKveq
                            
                            uf5rflB7PCfamlcT_-KJ4q3lfx7kiFNpSF9SdMLwKP_mCSOXGbrxrK3jXfH7bum3sZdl7w8k5irLa27i
                            
                            Bvp_RqtXgkSmgpcNWitCU8RBz7aOaHr8r-FCklg4wUkLNE26qlR6Sl42DAAiBZNLpUZUt-M7vaOs8TZB
                            
                            W4YehAzrqFAuTX3peMJBQB8K8_XxaTkRnEhSEMz9DnUnqzQjjVr5rnSdFSGxQmrQA8dBBwq4RaUfwbCU
                            
                            7au787CMn7EGiDO9KRcGHAsGHOJqb8P8Z7A-ssV7tfEqJayrNH-F_Z2p5kiasDODQrG53CZNUE0vuDT6
                            
                            Fp4_xOavE6wkYcHTfXWZJWFEMokE4NB9mtAl3lReYSZQyzKkcHWFNQCMAj3LoNGSdnEVVM_jzZtRSfWj
                            
                            IG2OmhyR1wZNRCHY_6NwEMOIHGLpA_L-kFFAJPgwQWi-WljeV-X2KiMQIeYlGGdskaNw
                            
                            token_type: bearer
                            
                            expires_in: 1209599
                            
                            userName: admin@example.com
                            
                            .issued: Sun, 26 Oct 2014 13:21:03 GMT
                            
                            .expires: Sun, 09 Nov 2014 13:21:03 GMT

                             

                            We've seen this before, in our overview article. The de-serialized JSON above represents the content of the response to our POST to the Token endpoint of our Web Api. The important part of the response is and access_token itself.

                            This Doesn't Look Any Different Than Before . . .

                            At this point, that de-serialized JSON response doesn't look any different than it did in our previous post, before we added all our fancy new Roles and custom properties. Shouldn't it have some new information in it now? Roles, and Addresses and stuff?

                            No.

                            A Little More On the Nature of Bearer Tokens (but only a little)

                            We had a really, really brief look Bearer Tokens in our Introduction to Identity in Web Api. As mentioned there, we will undertake a more in-depth exploration of Tokens in another post.

                            For our purposes here today, we are simply going to expand a little on what we learned previously, sufficient to understand how the access_token we retrieved as part of our JSON response above fits into the scheme of our newly modified Web Api project.

                            Bearer Tokens are, by design, "opaque" to the client. In other words, they are encoded (and sometimes encrypted) on the server, and can be decoded (and potentially decrypted) by the server. They are not designed to be decoded/decrypted by the client.

                            Recall from exploring the basic structure of an ASP.NET Web Api project, the ApplicationOauthProvider class, defined in the Providers => ApplicationOauthProvider.cs file.

                            When you POST a request to the Token endpoint of the ASP.NET Web Api application (at least, in the way we have it configured here), the server validates the credentials you present (in this case, user name + Password) by calling the GrantResourceOwnersCredentials() method defined on the ApplicationOauthProvider class:

                            The GrantResourceOwnersCredentials() Method from ApplicationOAuthProvider:
                            public override async Task GrantResourceOwnerCredentials(
                            
                                OAuthGrantResourceOwnerCredentialsContext context)
                            
                            {
                            
                                var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
                            
                                ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
                            
                             
                            
                                if (user == null)
                            
                                {
                            
                                    context.SetError("invalid_grant", "The user name or password is incorrect.");
                            
                                    return;
                            
                                }
                            
                             
                            
                                ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
                            
                                   OAuthDefaults.AuthenticationType);
                            
                                ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
                            
                                    CookieAuthenticationDefaults.AuthenticationType);
                            
                             
                            
                                AuthenticationProperties properties = CreateProperties(user.UserName);
                            
                                AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
                            
                                context.Validated(ticket);
                            
                                context.Request.Context.Authentication.SignIn(cookiesIdentity);
                            
                            }

                             

                            We can see that this code attempts to find a user with credentials matching those presented. If a valid user is found, the method calls the GenerateUserIdentityAsync() method defined on the ApplicationUser class to obtain an instance of ClaimsIdentity representing the user, and any claims the user may make within our system.

                            The ClaimsIdentity is then used to create an AuthenticationTicket.

                            In the code for GrantResourceOwnersCredentials(), above, when Validated() is called on the OAuthGrantResourceOwnerCredentialsContext, the OWIN middleware serializes the ClaimsIdentity into an encoded and signed token, to be returned in the HTTP Response header as a result of our request.

                            All of the above is a long-winded way of saying, the important user information, including user roles, and anything else we decide needs to be a part of our token, is present in the token when it is received by the client.

                            The Client just can't get to it.

                            A Note About Bearer Tokens and Security

                            As mentioned in our previous posts, using bearer tokens, and submitting credentials to obtain the token from the token endpoint should only be done over SSL/STL in a production system. Bearer tokens are exactly what their name implies - anyone presenting a valid bearer token will be granted access to the system, with whatever privileges the actual "owner" of the token has.

                            OAuth Bearer tokens represent a fairly simple authentication/authorization scheme, In building out your Web Api, consider all of the alternatives, and make the best choice for your application.

                            As mentioned previously, we will take a more thorough look at Claims Identity, and token authentication/authorization in later posts.

                            Adding Role-Based Authorization to the Web Api Application

                            Now that we understand how to authenticate ourselves using a bearer token, let's look at how we can use our new Role-Based Authorization capability within our application.

                            To start, let's look at the two primary controllers present in the Web Api application, AccountController and ValuesController (we are ignoring the HomeController, since it serves no purpose for our needs here).

                            We've already seen that AccountController is decorated with an [Authorize] attribute. This means that only authenticated users may access the action methods defined on this controller (unless, of course, the method itself is decorated with an [AllowAnonymous] attribute).

                            Let's look at the simplistic ValuesController. ValuesController is provided as a simple example of how one might add a basic CRUD-style functionality. ValuesController is similarly decorated with the [Authorize] attribute. Again, only authenticated users are able to access the Action methods defined on ValuesController.

                            As we saw in previous posts about implementing Role-Based Authorization in an MVC project, we can modify the access permissions for our Web Api, at either the Controller level, or the Action method level, by expanding on our use of [Authorize] .

                            Consider, we might want to restrict access to AccountController only to users who are in the Admin role, but allow access to ValuesController, and the functionality it provides, to any authenticated user.

                            In this case, we will want to make some modifications to our Web Api configuration.

                            Add a Vanilla Users Role as a Default in ApplicationDbInitializer

                            First, let's make sure we have two distinct Roles available in our application - the "Admin" role we already create as an initial value during configuration, and a new "Users" role. Update the InitializeDatabaseForEF() method as follows:

                            Add a Users Role and a Default User to InitializeDatabaseForEF() Method:
                            public static void InitializeIdentityForEF(ApplicationDbContext db)
                            
                            {
                            
                                var userManager = HttpContext.Current
                            
                                    .GetOwinContext().GetUserManager<ApplicationUserManager>();
                            
                             
                            
                                var roleManager = HttpContext.Current
                            
                                    .GetOwinContext().Get<ApplicationRoleManager>();
                            
                             
                            
                                // Initial Admin user:
                            
                                const string name = "admin@example.com";
                            
                                const string password = "Admin@123456";
                            
                             
                            
                                // Some initial values for custom properties:
                            
                                const string address = "1234 Sesame Street";
                            
                                const string city = "Portland";
                            
                                const string state = "OR";
                            
                                const string postalCode = "97209";
                            
                             
                            
                                const string roleName = "Admin";
                            
                                const string roleDescription = "All access pass";
                            
                             
                            
                                //Create Role Admin if it does not exist
                            
                                var role = roleManager.FindByName(roleName);
                            
                                if (role == null)
                            
                                {
                            
                                    role = new ApplicationRole(roleName);
                            
                             
                            
                                    // Set the new custom property:
                            
                                    role.Description = roleDescription;
                            
                                    var roleresult = roleManager.Create(role);
                            
                                }
                            
                             
                            
                                // Create Admin User:
                            
                                var user = userManager.FindByName(name);
                            
                                if (user == null)
                            
                                {
                            
                                    user = new ApplicationUser { UserName = name, Email = name };
                            
                             
                            
                                    // Set the new custom properties:
                            
                                    user.Address = address;
                            
                                    user.City = city;
                            
                                    user.State = state;
                            
                                    user.PostalCode = postalCode;
                            
                             
                            
                                    var result = userManager.Create(user, password);
                            
                                    result = userManager.SetLockoutEnabled(user.Id, false);
                            
                                }
                            
                             
                            
                                // Add user admin to Role Admin if not already added
                            
                                var rolesForUser = userManager.GetRoles(user.Id);
                            
                                if (!rolesForUser.Contains(role.Name))
                            
                                {
                            
                                    userManager.AddToRole(user.Id, role.Name);
                            
                                }
                            
                             
                            
                                // Initial Vanilla User:
                            
                                const string vanillaUserName = "vanillaUser@example.com";
                            
                                const string vanillaUserPassword = "Vanilla@123456";
                            
                             
                            
                                // Add a plain vannilla Users Role:
                            
                                const string usersRoleName = "Users";
                            
                                const string usersRoleDescription = "Plain vanilla User";
                            
                             
                            
                                //Create Role Users if it does not exist
                            
                                var usersRole = roleManager.FindByName(usersRoleName);
                            
                                if (usersRole == null)
                            
                                {
                            
                                    usersRole = new ApplicationRole(usersRoleName);
                            
                             
                            
                                    // Set the new custom property:
                            
                                    usersRole.Description = usersRoleDescription;
                            
                                    var userRoleresult = roleManager.Create(usersRole);
                            
                                }
                            
                             
                            
                                // Create Vanilla User:
                            
                                var vanillaUser = userManager.FindByName(vanillaUserName);
                            
                                if (vanillaUser == null)
                            
                                {
                            
                                    vanillaUser = new ApplicationUser 
                            
                                    { 
                            
                                        UserName = vanillaUserName, 
                            
                                        Email = vanillaUserName 
                            
                                    };
                            
                             
                            
                                    // Set the new custom properties:
                            
                                    vanillaUser.Address = address;
                            
                                    vanillaUser.City = city;
                            
                                    vanillaUser.State = state;
                            
                                    vanillaUser.PostalCode = postalCode;
                            
                             
                            
                                    var result = userManager.Create(vanillaUser, vanillaUserPassword);
                            
                                    result = userManager.SetLockoutEnabled(vanillaUser.Id, false);
                            
                                }
                            
                             
                            
                                // Add vanilla user to Role Users if not already added
                            
                                var rolesForVanillaUser = userManager.GetRoles(vanillaUser.Id);
                            
                                if (!rolesForVanillaUser.Contains(usersRole.Name))
                            
                                {
                            
                                    userManager.AddToRole(vanillaUser.Id, usersRole.Name);
                            
                                }
                            
                            }
                            

                             

                            Above, we have added a new role "Users" and another initial sample user.

                            Next, let's modify the [Authorize] attribute on our AccountController class, and add a Role argument:

                            Modified [Authorize] Attribute for AccountController:
                            [Authorize(Roles= "Admin")]
                            
                            [RoutePrefix("api/Account")]
                            
                            public class AccountController : ApiController
                            
                            {
                            
                                // ... All the Code ...
                            
                            }

                             

                            Now, we just need to change up our client code to attempt to access some methods from each of the two controllers to see how our Role-Based Authorization is working for us.

                            Modify Client Code to Attempt Controller Access

                            Here, we will simply set up some client code to attempt to retreive some basic data from both AccountController and ValuesController. We will do this as a user in the Admin Role, and then also as a user in the Users Role.

                            Change the code in your Console application to match the following:

                            Modified Client Code to Access Both Controllers with Different Roles:
                            class Program
                            
                            {
                            
                                // You will need to substitute your own host Url here:
                            
                                static string host = "http://localhost:63074/";
                            
                             
                            
                                static void Main(string[] args)
                            
                                {
                            
                                    // Use the User Names/Emails and Passwords we set up in IdentityConfig:
                            
                                    string adminUserName = "admin@example.com";
                            
                                    string adminUserPassword = "Admin@123456";
                            
                             
                            
                                    string vanillaUserName = "vanillaUser@example.com";
                            
                                    string vanillaUserPassword = "Vanilla@123456";
                            
                             
                            
                                    // Use the new GetToken method to get a token for each user:
                            
                                    string adminUserToken = GetToken(adminUserName, adminUserPassword);
                            
                                    string vaniallaUserToken = GetToken(vanillaUserName, vanillaUserPassword);
                            
                             
                            
                                    // Try to get some data as an Admin:
                            
                                    Console.WriteLine("Attempting to get User info as Admin User");
                            
                                    string adminUserInfoResult = GetUserInfo(adminUserToken);
                            
                                    Console.WriteLine("Admin User Info Result: {0}", adminUserInfoResult);
                            
                                    Console.WriteLine("");
                            
                             
                            
                                    Console.WriteLine("Attempting to get Values info as Admin User");
                            
                                    string adminValuesInfoResult = GetValues(adminUserToken);
                            
                                    Console.WriteLine("Admin Values Info Result: {0}", adminValuesInfoResult);
                            
                                    Console.WriteLine("");
                            
                             
                            
                                    // Try to get some data as a plain old user:
                            
                                    Console.WriteLine("Attempting to get User info as Vanilla User");
                            
                                    string vanillaUserInfoResult = GetUserInfo(vaniallaUserToken);
                            
                                    Console.WriteLine("Vanilla User Info Result: {0}", vanillaUserInfoResult);
                            
                                    Console.WriteLine("");
                            
                             
                            
                                    Console.WriteLine("Attempting to get Values info as Vanilla User");
                            
                                    string vanillaValuesInfoResult = GetValues(vaniallaUserToken);
                            
                                    Console.WriteLine("Vanilla Values Info Result: {0}", vanillaValuesInfoResult);
                            
                                    Console.WriteLine("");
                            
                             
                            
                                    Console.Read();
                            
                                }
                            
                             
                            
                             
                            
                                static string GetToken(string userName, string password)
                            
                                {
                            
                                    HttpClient client = new HttpClient();
                            
                                    var pairs = new List<KeyValuePair<string, string>>
                            
                                                {
                            
                                                    new KeyValuePair<string, string>( "grant_type", "password" ), 
                            
                                                    new KeyValuePair<string, string>( "username", userName ), 
                            
                                                    new KeyValuePair<string, string> ( "Password", password )
                            
                                                };
                            
                                    var content = new FormUrlEncodedContent(pairs);
                            
                             
                            
                                    // Attempt to get a token from the token endpoint of the Web Api host:
                            
                                    HttpResponseMessage response =
                            
                                        client.PostAsync(host + "Token", content).Result;
                            
                                    var result = response.Content.ReadAsStringAsync().Result;
                            
                             
                            
                                    // De-Serialize into a dictionary and return:
                            
                                    Dictionary<string, string> tokenDictionary =
                            
                                        JsonConvert.DeserializeObject<Dictionary<string, string>>(result);
                            
                                    return tokenDictionary["access_token"];
                            
                                }
                            
                             
                            
                             
                            
                                static string GetUserInfo(string token)
                            
                                {
                            
                                    using (var client = new HttpClient())
                            
                                    {
                            
                                        client.DefaultRequestHeaders.Authorization =
                            
                                        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
                            
                                        var response = client.GetAsync(host + "api/Account/UserInfo").Result;
                            
                                        return response.Content.ReadAsStringAsync().Result;
                            
                                    }
                            
                                }
                            
                             
                            
                             
                            
                                static string GetValues(string token)
                            
                                {
                            
                                    using (var client = new HttpClient())
                            
                                    {
                            
                                        client.DefaultRequestHeaders.Authorization =
                            
                                        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
                            
                             
                            
                                        var response = client.GetAsync(host + "api/Values").Result;
                            
                                        return response.Content.ReadAsStringAsync().Result;
                            
                                    }
                            
                                }
                            
                            }

                             

                            In the above, we changed our code up a bit. We now have a GetToken() method, which accepts a User Name/Password as arguments, and returns only the access_token string from the request to the Token endpoint of our Web Api.

                            Next, we added two different calls to our Web Api. One method calls the GetUserInfo() method on the AccountController, and the other calls the Get() method on our ValuesController.

                            If we spin up our Web Api, and, after it has spun up, run our client application, we should see the following output in our Console:

                            Console Output from Access Permissions Comparison:
                            Attempting to get User info as Admin User
                            
                            Admin User Info Result: {"Email":"admin@example.com","HasRegistered":true,"Login
                            
                            Provider":null}
                            
                            Attempting to get Values info as Admin User
                            
                            Admin Values Info Result: ["value1","value2"]
                            
                            Attempting to get User info as Vanilla User
                            
                            Vanilla User Info Result: {"Message":"Authorization has been denied for this req
                            
                            uest."}
                            
                            Attempting to get Values info as Vanilla User
                            
                            Vanilla Values Info Result: ["value1","value2"]

                             

                            Note the output from that third attempt. We are trying to call into GetUserInfo() as a plain vanilla User, in the Users Role. Appropriately, our Web Api has returned an authorization error, since Users are not allowed access to the method by virtue of the [Authorize(Roles="Admin")] attribute on the class declaration for AccountController.

                            In contrast, both users are able to access the Get() method on ValuesController, since this controller is decorated with a simple [Authorize] attribute, which requires only an authenticated user for access.

                            Accessing Custom User Properties

                            We have also added some custom properties to our ApplicationUserModel. Let's take a look and see if we can work with those in the context of our examples here.

                            If we look more closely at the GetUserInfo() method on AccountController, we find that the actual return type for this method is UserInfoViewModel, which is found in the Models => AccountViewModels.cs file. Now, in our crude, simple Console application we are not going to all the effort of de-serializing the JSON from our GET request into an object, but we COULD.

                            For our purposes, here, it will be sufficient to modify the UserInfoViewModel to reflect the additional properties we want to return, and then update the GetUserInfo() method to suit.

                            Add our custom User properties to the UserInfoViewModel class:
                            public class UserInfoViewModel
                            
                            {
                            
                                public string Email { get; set; }
                            
                                public bool HasRegistered { get; set; }
                            
                                public string LoginProvider { get; set; }
                            
                             
                            
                                // Add our custom properties from ApplicationUser:
                            
                                public string Address { get; set; }
                            
                                public string City { get; set; }
                            
                                public string State { get; set; }
                            
                                public string PostalCode { get; set; }
                            
                            }

                             

                            Next, update the GetUserInfo() method to provide values for the additional properties we just added to UserInfoViewModel:

                            Update GetUserInfo() Method on AccountController with Custom Properties:
                            public UserInfoViewModel GetUserInfo()
                            
                            {
                            
                                ExternalLoginData externalLogin 
                            
                                    = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
                            
                             
                            
                                // We wouldn't normally be likely to do this:
                            
                                var user = UserManager.FindByName(User.Identity.Name);
                            
                                return new UserInfoViewModel
                            
                                {
                            
                                    Email = User.Identity.GetUserName(),
                            
                                    HasRegistered = externalLogin == null,
                            
                                    LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null,
                            
                             
                            
                                    // Pass the custom properties too:
                            
                                    Address = user.Address,
                            
                                    City = user.City,
                            
                                    State = user.State,
                            
                                    PostalCode = user.PostalCode
                            
                                };
                            
                            }

                             

                            Above, we have called out to UserManager to retreive an instance of our user, so we can get at the new properties we have added. We then set the corresponding values on the UserInfoViewModel before returning.

                            This example is a little contrived, and we most likely would NOT do this in a production application this way. But for now, it will serve to demonstrate that our new and improved ApplicationUser indeed has the custom properties we added to the model, and that we are retrieving them from the database as expected.

                            If we run our Console Client application one last time, we should see the following output:

                            Output from the Console Application with Custom User Properties:
                            Attempting to get User info as Admin User
                            
                            Admin User Info Result: {"Email":"admin@example.com","HasRegistered":true,"Login
                            
                            Provider":null,"Address":"1234 Sesame Street","City":"Portland","State":"OR",
                            
                            "PostalCode":"97209"}
                            
                            Attempting to get Values info as Admin User
                            
                            Admin Values Info Result: ["value1","value2"]
                            
                            Attempting to get User info as Vanilla User
                            
                            Vanilla User Info Result: {"Message":"Authorization has been denied for this 
                            
                            request."}
                            
                            Attempting to get Values info as Vanilla User
                            
                            Vanilla Values Info Result: ["value1","value2"]

                             

                            And we see, our custom user properties are returned with the JSON response.

                            Role-Based Authorization Versus Claims-Based Authorization

                            In this post, we have looked briefly at implementing Role-Based Authorization in the context of an ASP.NET Web Api project, and we had the briefest look at how Bearer Tokens work. It is important to note though, that for any but the simplest of authorization/access control schemes, Role-Based Authorization ("RBA") is rapidly being overshadowed by Claims-Based Authorization.

                            Claims-Based Identity offers greater flexibility, and more effectively separates the Authentication and Authorization mechanism from your code. Note, in this example project, we need to specify, as part of the [Authorize] attribute, precisely which Roles are allowed access to which controllers and/or methods.

                            Claims-Based Auth is more complex than RBA, but is generally going to be a more natural fit for a Web Api scenario, unless your needs are fairly simple.

                            We will examine Tokens, and Claims-Based Identity, in an upcoming post.

                            Additional Resources and Items of Interest

                             

                            Posted on October 26 2014 03:23 PM by jatten     

                            Comments (11)

                            Setting Up for Mono Development in Linux Mint/Ubuntu

                            Posted on October 19 2014 06:08 PM by jatten in Linux, Learning Linux, C#   ||   Comments (5)

                            follow-the-yerllow-mint-roadI recently needed to create a cross-platform C# library which needs to work across Windows, OSX, and Linux. Obviously, getting a .NET library running on Windows presented no great difficulties. Getting the same library working under Mono on a Linux platform was a little more involved.

                            The folks at Xamarin have brought Mono, and the development tools available for Mono, a long ways. Xamarin Studio is a beautiful IDE which, while it differs from Visual Studio in some important ways, is a joy to work with. Xamarin's focus is building mobile application development tools. However, Xamarin Studio and Mono both lend themselves fairly well to working with C# code on either Windows or OSX.

                            Image by Neil Fowler  | Some Rights Reserved

                            There are some notable things missing (you can't work directly with Windows Forms, for example, nor is there significant support for ASP.NET), but overall, working with strictly library code presented only minor hurdles (not the least of which is a different workflow and navigation scheme from VS).

                            However, Xamarin Studio is only available for Windows and OSX - there is not currently any support for direct Linux platforms (yes, I know a shiny veneer on top of what amounts to Linux under the hood).

                            Getting things working on my Linux machine took a little more work.

                            Mono Development on Linux Using MonoDevelop

                            Xamarin Studio represents a "Super-Set" of the original, cross-platform Mono IDE, MonoDevelop. The Xamarin team have generously pushed a good number of improvements back to the MonoDevelop source. However, the most recent changes are not directly available as packages to all Linux distros.

                            Specifically, while the most recent version of MonoDevelop is 5.0.1.3, the most recent package available to Ubuntu, and Ubuntu-based systems from the MonoDevelop site is version 2.6.0.1.

                            In this article, we will look at how to get a proper Mono development environment set up on Ubuntu-based systems, including the most recent (as of this writing) release of MonoDevelop. We will also look at getting Nuget, and Nuget package restore, working properly in this environment.

                            Got Linux?

                            If you don't have a Linux box, set up a VM to work through this post. I prefer using Linux Mint for my Linux-based activity. I found that all of the following steps worked fine in the most recent version of Ubuntu. However, I DON'T like Ubuntu itself. I prefer Linux mint. The last version of Mint I was using (Mint 13 with the KDE desktop) did not work very well with the most recent version of MonoDevelop. I found the the most recent stable release of Mint 17, using the Cinnamon desktop, worked great, with none of the bloat I found with Ubuntu.

                            See the following for detailed instructions if you need to set up a Linux VM:

                            For the purpose of this post, we will assume you are starting with a reasonably fresh VM, and do not already have Mono or MonoDevelop Installed.

                            As mentioned previously, the links on the MonoDevelop download site don't get you the most recent Mono release for Ubuntu-based systems. Fortunately, there are some great Personal Package Archives out there which DO.

                            Install Mono

                            Your machine may or may not already have Mono installed. Either way, we are going to pull down the most recent, complete version.

                            Open a terminal in your home folder, and do the following:

                            Update Everything:
                            $ sudo apt-get update

                             

                            Install Mono:
                            $ sudo apt-get install mono-complete

                            Once the Mono Installation completes, update again before moving forward:

                            Update Everything Again:
                            $ sudo apt-get update

                             

                            The most recent Mono release is now installed on your machine. Now, let's get all the goodness of the most recent MonoDevelop release.

                            Add the Personal Package Archive (PPA) for Latest Stable MonoDevelop Release

                            To get the most recent stable release of MonoDevelop, we can thank Eberhard Beilharz for making the Stable Version of MonoDevelop PPA at Launchpad.Net.

                            To get the most recent version of MonoDevelop on our machine, we simply need to add the PPA to our Synaptic Package Manager's list of sources, and then use synaptic to pull it down.

                            Add the MonoDevelop PPA to Synaptic:
                            $ sudo add-apt-repository ppa:ermshiperete/monodevelop

                             

                            Update to refresh Synaptic:

                            Update Everything Yet Again:
                            $ sudo apt-get update

                             

                            Install MonoDevelop

                            Now that we have added the PPA, we can use Synaptic just like any other package:

                            Install MonoDevelop from PPA:
                            $ sudo apt-get install monodevelop-current

                             

                            Again, update everything:

                            Update Everything One More Time:
                            $ sudo apt-get update

                             

                            Now, as indicated on the MonoDevelop PPA site, the install we just completed placed a shell script at /opt/monodevelop/bin/monodevelop-launcher.sh

                            Let's add an alias for this, so that we don't have to deal with that big long path every time we want to open MonoDevelop.

                            If this is a fresh install of Mint 17 on your machine, you will need to add a .bashrc file. If you already have a .bashrc file (or a .bash_profile file - in Mint they are functionally the same), add one:

                            Add a .bashrc File in your Home Directory:
                            $ touch .bashrc

                             

                            Next, add an alias to .bashrc pointing to and executing the shell script:

                            Add Alias to Execute MonoDevelop-Launcher Script:
                            $ echo >> .bashrc "alias monodev=Exec=\"sh /opt/monodevelop/bin/monodevelop-launcher.sh\""

                             

                            Close the terminal, and open a new terminal instance. You should now be able to open MonoDevelop from the terminal by typing, simply "monodev"

                            With that, we have successfully installed the most recent release of MonoDevelop on our Mint 17 or Ubuntu machine. However, we are likely to find that Nuget, and Nuget package restore, give us difficulties in this out-of-the box scenario.

                            Making Nuget Package Restore Work in MonoDevelop

                            Out-of-the-box, Nuget works a little differently in MonoDevelop than we are accustomed to in Visual Studio (this is true of much about MonoDevelop, actually…).

                            To see what I mean, use your new terminal alias and open MonoDevelop. Then, create a new Console project. Once that's done, take a moment to get oriented.

                            We can add Nuget packages by right-clicking on individual projects within the solution in the tree to the left:

                            Add Nuget Packages in MonoDevelop:

                            add-nuget-packages

                            However, if we try to add Nuget packages right now, we get a big FAIL:

                            Add Nuget Packages Fails in MonoDevelop:

                            add-packages-fail

                            WAT?

                            What's equally confounding is that, if we were to open and build an existing project which required Nuget Package Restore, the Package Manager would is not always able to connect to Nuget to pull down the packages, and an error would result.

                            This doesn't affect all packages, but enough to be annoying. In particular, several NUnit-related packages and a few others, appear to be affected, and your project can't be properly built.

                            As it turns out, we are missing some important SSL certificates. The fix is simple, and we can do it from our terminal.

                            Add Required SSL Certificates for Nuget to Work in MonoDevelop

                            Since we are using our current terminal instance to run MonoDevelop, we will either need to quit MonoDevelop, or open a new terminal instance, and then execute the following to import a number of important SSL certificates:

                            Import SSL Certificates from mozroots:
                            $ mozroots --import --sync

                             

                            Once we have run this command, we should be able to add Nuget packages at will. Also, Update Packages, and Restore Packages commands from the Solution context menu both should work with no problems.

                            Additional Resources and Items of Interest

                             

                            Posted on October 19 2014 06:08 PM by jatten     

                            Comments (5)

                            About the author

                            My name is John Atten, and my "handle" on many of my online accounts is xivSolutions. I am Fascinated by all things technology and software development. I work mostly with C#, JavaScript/Node, and databases of many flavors. Actively learning always. I dig web development. I am always looking for new information, and value your feedback (especially where I got something wrong!). You can email me at:

                            jatten at typecastexception dot com

                            Web Hosting by