Running ASPNET 5 and .NET Core on Linux

I have been hearing quite a bit about the new ASPNET 5 (vNext) and .NET Core for a while now. However, the frequent breaking changes between beta versions was too much for me to want to try it out. With ASPNET 5 and .NET Core in release candidate and a 1.0 release in sight, I thought it’s time to give it a try.

The super basics on ASPNET 5 and .NET Core

A one line summary on ASPNET 5 and .NET Core to set the stage.

ASP.NET 5 is a lean and composable framework for building web and cloud applications. ASP.NET 5 is fully open source
.NET Core is a cross-platform implementation of .NET that is primarily being driven by ASP.NET 5 workloads, but also by the need and desire to have a modern runtime that is modular and whose features and libraries can be cherry picked based on the application’s needs.

Choosing the OS

ASPNET 5 and CoreCLR can run on Windows, Mac, or Linux. I choose try it on Linux as it’s the most interesting option to me. I think one can safely assume that ASPNET 5 will run relatively smooth on Windows compared to other OS options. Moreover, if I can get it to work on another OS, it should only work better in the windows environment.  Mac is interesting, but it’s use case is mostly as a development environment. Linux on the other can be both a viable development environment as well as an attractive hosting option.  In my test, I used Ubuntu Server 14.04 as it’s one of the most popular linux distributions and it’s easy to use.

Choosing between Mono Runtime vs .NET Core Runtime

Developers are going to have a lot more options in the new ecosystem. Even on Linux, there are two options for .NET runtime to choose from: mono runtime or coreclr. Mono is an open source implementation of .NET framework by Xamarin and has its own mono runtime. It has been around 10+ years and supports ( some ) web and desktop .NET applications to run on Linux. .NET Core is a a cross-platform implementation of .NET by Microsoft and coreclrt is it’s runtime.

Picking between mono runtime and coreclr on linux is like picking between the full CLR and coreclr on windows. The full CLR supports any third party libraries available today. The coreclr while leaner and modular, requires libraries to specifically target the .NET Core. As a result, the amount of libraries supported in .NET Core will be limited in the beginning, but should improve over time as adoption increases.

One of the main goals of ASPNET 5 is about lightweight and composable framework, and I do think it’s worth pursuing. I am going to try out coreclr.

Setting up the environment

Getting the necessary components installed is surprisingly easy using the getting started page on asp.net site.

Install the .NET Version Manager (DNVM)

dnvm is the .NET Version Manager, a set of command line utilities to update and configure which .NET Runtime to use.

curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh

Install the .NET Execution Environment (DNX)

The .NET Execution Environment contains the code required to bootstrap and run an application. This includes things like the compilation system, SDK tools, and the native CLR hosts.

#install dnx prerequisite
sudo apt-get install libunwind8 gettext libssl-dev libcurl4-openssl-dev zlib1g libicu-dev uuid-dev

#use dnvm to install .net core
dnvm upgrade -r coreclr

At this point, if you run dnvm list to list the installed runtime, you should see something similar to:

Active Version              Runtime Architecture OperatingSystem Alias
------ -------              ------- ------------ --------------- -----
  *    1.0.0-rc1-update1    coreclr x64          linux           default

Notice only the coreclr runtime is installed. The getting started page on asp.net site also has instruction to install the mono runtime. Since I am interested in trying out the coreclr, not installing mono runtime will actually make it easier for me to verify it's working in coreclr.

Install Libuv

Libuv is a multi-platform asynchronous IO library that is used by Kestrel, a cross-platform HTTP server for hosting ASP.NET 5 web applications.

sudo apt-get install make automake libtool curl
curl -sSL https://github.com/libuv/libuv/archive/v1.4.2.tar.gz | sudo tar zxfv - -C /usr/local/src
cd /usr/local/src/libuv-1.4.2
sudo sh autogen.sh
sudo ./configure
sudo make
sudo make install
sudo rm -rf /usr/local/src/libuv-1.4.2 && cd ~/
sudo ldconfig

Running the HelloMVC sample project

Once the environment is set, it's also easy to run the Hello World MVC project.

#install git if it's not already installed
sudo apt-get install git

#clone the aspnet 5 repo
git clone https://github.com/aspnet/home

#change directory into the HelloMvc sample
#1.0.0-rc1-update matches the runtime version and could be different for you depends on what runtime you installed
cd home/samples/1.0.0-rc1-update1/HelloMvc/

#restore the packages required by HelloMvc project
dnu restore

#start the app
dnx web

If you browse to http://my-ip-address:5004, you should see the running sample application

HelloMvc

dnu restore command

dnu restore command looks at the dependencies of the application and download them, adding them to the apps packages directory. The dependencies is defined in project.json file. In the HelloMvc project, the dependencies are:

"dependencies": {
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
"Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
"Microsoft.AspNet.Diagnostics": "1.0.0-rc1-final",
"Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final"
},

dnx web command

dnx web command runs the application.

In earlier documentation, you might see references to dnx kestrel command instead. If you try to run the dnx kestrel command. You will get this error message: Error: Unable to load application or execute command 'kestrel'. Available commands: web. The reason is that web and kestrel are special commands expected to be defined in project.json. In the latest version of the HelloMvc sample, only web command is defined. In earlier beta version of the sample has both web and kestrel defined.

Open the latest version of the HelloMvc project.json file in 1.0.0-rc1-update1/HelloMvc directory, here only the web command is defined. Also pay attention to the server.urls argument, the value is http://*:5004, this explains why the sample runs on port 5004.

commands: {
 web: Microsoft.AspNet.Server.Kestrel --server.urls http://*:5004
},

Open an earlier version of the HelloMvc project.json file in1.0.0-beta7/HelloMvc, you will see that both web and kestrel commands are defined.

commands: {
 web: Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5001,
 kestrel: Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5004
},

Running MusicStore sample project

Getting the HelloMvc sample to run was good, but I need to see a non trivial application running before I want to invest more time in this. My next test is getting the AspNet MusicStore sample project to run. This project has authentication and EF7, which makes it a good target. This project uses InMemory provider for database access as only SqlServer, SqlLite and InMemory providers are available at the moment.

Following similar steps we did for HelloMvc project

#clone the musicstore repo
git clone https://github.com/aspnet/MusicStore.git

#change the directory into the sample project
cd MusicStore/src/MusicStore

#restore package
dnu restore

#run the application
dnx web

At this point, I see the following in the console which indicated the application is running. It looks promising.

ubuntu@:~/MusicStore/src/MusicStore$ dnx web
Hosting environment: Production
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

However, if I  try to access it, it get the following scary looking exception.

<img class="alignnone size-full wp-image-660" src="https://binaryexplorer.files.wordpress.com/2015/12/musicstore.png&quot; alt="MusicStore" width="1179" height="937" />

fail: Microsoft.AspNet.Server.Kestrel[13]
An unhandled exception was thrown by the application.
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.CompilationAbstractions.ILibraryExporter' while attempting to activate 'Microsoft.AspNet.Mvc.Razor.Compilation.RoslynCompilationService'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.CreateCallSite(ServiceProvider provider, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetResolveCallSite(IService service, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetServiceCallSite(Type serviceType, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.CreateCallSite(ServiceProvider provider, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetResolveCallSite(IService service, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetServiceCallSite(Type serviceType, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.CreateCallSite(ServiceProvider provider, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetResolveCallSite(IService service, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetServiceCallSite(Type serviceType, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.CreateCallSite(ServiceProvider provider, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetResolveCallSite(IService service, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetServiceCallSite(Type serviceType, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType, ServiceProvider serviceProvider)
at System.Collections.Concurrent.ConcurrentDictionaryExtensions.GetOrAdd[TKey,TValue,TArg](ConcurrentDictionary`2 dictionary, TKey key, Func`3 valueFactory, TArg arg)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.AspNet.Mvc.Razor.Internal.MvcRazorMvcViewOptionsSetup.ConfigureMvc(IServiceProvider serviceProvider, MvcViewOptions options)
at Microsoft.Extensions.OptionsModel.OptionsCache`1.CreateOptions()
at System.Threading.LazyInitializer.EnsureInitializedCore[T](T&amp;amp;amp;amp;amp; target, Boolean&amp;amp;amp;amp;amp; initialized, Object&amp;amp;amp;amp;amp; syncLock, Func`1 valueFactory)
at Microsoft.AspNet.Mvc.ViewEngines.CompositeViewEngine..ctor(IOptions`1 optionsAccessor)
at lambda_method(Closure , ServiceProvider )
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.AspNet.Mvc.ViewResult.&amp;amp;amp;amp;lt;ExecuteResultAsync&amp;amp;amp;amp;gt;d__26.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.&amp;amp;amp;amp;lt;InvokeResultAsync&amp;amp;amp;amp;gt;d__45.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.&amp;amp;amp;amp;lt;InvokeResultFilterAsync&amp;amp;amp;amp;gt;d__44.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.&amp;amp;amp;amp;lt;InvokeAllResultFiltersAsync&amp;amp;amp;amp;gt;d__43.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.&amp;amp;amp;amp;lt;InvokeResourceFilterAsync&amp;amp;amp;amp;gt;d__38.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.&amp;amp;amp;amp;lt;InvokeAsync&amp;amp;amp;amp;gt;d__33.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Mvc.Infrastructure.MvcRouteHandler.&amp;amp;amp;amp;lt;InvokeActionAsync&amp;amp;amp;amp;gt;d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Builder.RouterMiddleware.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Session.SessionMiddleware.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Session.SessionMiddleware.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Diagnostics.ExceptionHandlerMiddleware.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Diagnostics.ExceptionHandlerMiddleware.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Diagnostics.StatusCodePagesMiddleware.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Hosting.Internal.RequestServicesContainerMiddleware.&amp;amp;amp;amp;lt;Invoke&amp;amp;amp;amp;gt;d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Hosting.Internal.HostingApplication.&amp;amp;amp;amp;lt;ProcessRequestAsync&amp;amp;amp;amp;gt;d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at Microsoft.AspNet.Server.Kestrel.Http.Frame`1.&amp;amp;amp;amp;lt;RequestProcessingAsync&amp;amp;amp;amp;gt;d__3.MoveNext()

After some googling, the solution turned out to be a simple one. The sample requires the latest runtime. Run the following to update it to latest.

#upgrade the runtime to latest version
#-r specifies the runtime we are upgrading is coreclr
#-u use the unstable feed to get the latest
dnvm upgrade -u -r coreclr

#verify the version
dnvm list

#dnvm list output indicates that we have version 1.0.0-rc2-16319 and it's the active runtime
Active Version              Runtime Architecture OperatingSystem Alias

------ -------              ------- ------------ --------------- -----

       1.0.0-rc1-update1    coreclr x64          linux           

  *    1.0.0-rc2-16319      coreclr x64          linux           default

MusicStore
Run the dnx web command again and access the site. Yay! I am greeted with the home page of the music store. Browsing around the site and adding items to cart is also working.

Conclusion

In the blog post, we configured the .net runtime environment on linux. We also setup the HelloMvc and MusicStore sample application to run on the coreclr runtime. There were small hiccups but the overall experience is a pleasant one.

The libraries available on the Core CLR are still limited. But it looks like more are coming soon. A quick look at a couple libraries that I plan to use already has some support:
* Npgsql’s (PostgreSQL data access provider) development branch already fully support CoreCLR.
* AWS SDK also has beta support for the CoreCLR.

References:

Link

Using PostgreSQL with Entity Framework in ASP.NET MVC

In this blog post we are going to learn how to use PostgreSQL with Entity Framework in an ASP.NET MVC application.

Introduction:

I wanted to investigate using Entity Framework with PostgreSQL in an ASP.NET MVC application quickly. Instead of writing a trivial demo that simply read and write data from the database, I decided a better approach would be to convert an existing ASP.NET MVC application that already uses Entity Framework (with another database) to work with PostgreSQL. I decided to use MvcMusicStore. It is one of best documented tutorial ASP.NET MVC applications. This is the description on its project site: “MVC Music Store is a tutorial application built on ASP.NET MVC. It’s a lightweight sample store which demonstrates ASP.NET MVC using Entity Framework”.

By going through the process of converting a working application, I can concentrate on the area that are specific to using Entity Framework with PostgreSQL. I can also easily perform basic testing to verify the changes I make by just running the application. The rest of this post documents that steps I have gone though to convert the MvcMusicStore.

What you will need:

  • A working installation of PostgreSQL. I use PostgreSQL version 9.3.
  • A development environment where you can compile and run ASP.NET MVC application.

Instructions:

Step 1: Setup MvcMusicStore
Download “MvcMusicStore”. Unzip the folder and open the project. At this point, you should be able to compile the project. If not, there is something wrong with your development environment, resolve it now before you make more changes that could complicate it further. The project is configured to use SqlServerSe If it’s installed, you should be able to run the application.
MVC-Music-Store-Thumb

Step 2: Prepare the PostgreSQL database.
MvcMusicStore uses EntityFramework Code First.
In the global.asax.cs file, it specifies the SampleData class as the database initializer.

protected void Application_Start()
{
    // specify database initializer
    System.Data.Entity.Database.SetInitializer(new MvcMusicStore.Models.SampleData());

    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}

Take a look at the SampleData class, notice that it inherits from DropCreateDatabaseIfModelChanges. This means that the application will re-create and re-seed the database when the schema changes.

// inherits from DropCreateDatabaseIfModelChanges
public class SampleData : DropCreateDatabaseIfModelChanges
{
    // The seed method will seed the database with initial data
    protected override void Seed(MusicStoreEntities context)
    {
        var genres = new List
        {
            new Genre { Name = "Rock" },
             new Genre { Name = "Jazz" },
             new Genre { Name = "Metal" },
             new Genre { Name = "Alternative" },
             new Genre { Name = "Disco" },
             new Genre { Name = "Blues" },
             new Genre { Name = "Latin" },
             new Genre { Name = "Reggae" },
             new Genre { Name = "Pop" },
             new Genre { Name = "Classical" }
         };
// more code not shown here .......

Unfortunately database migration and creation is not yet supported in Npgsql Entity Framework. We will have to create the database and seed the data manually. a) Create a database in your PostgreSQL server. Name the database MvcMusicStore. b) Next, we need to create the tables and seed them with data. The MvcMusicStore download contains a \MvcMusicStore-Assets\Data\MvcMusicStore-Create.sql file that works for MSSQL. We can use it as the base and adapt it for postgreSQL. You can use the finished script here MvcMusicStore-Create-PostgreSQL. The file has documentation on what was changed from the original script.

Step 3: Install ADO.NET provider and Entity Framework provider for postgreSQL.
You will have multiple options here. We will use Npgsql.
The MvcMusicStore download uses EntityFramework 4.1, which is two versions older than the current version (Entity Framework 6). Let’s upgrade it to the latest first via Nuget.

Install-Package EntityFramework

Next, Install Npgsql PostgreSQL Entity Framework provider. This will also install its dependency which includes the Npgsql ADO.NET Provider.

Install-Package Npgsql.EntityFramework

Step 4: Update web.config to tell the run time about our database connection and Entity Framework configuration.

a) Update the connection string like below. Remember to replace the information in the connection string to the values in your environment.

<connectionStrings>
<add name="MusicStoreEntities" 
connectionString="Server=[myserver];Database=MusicStore;
User Id=[myusername];Password=[mypassword];" providerName="Npgsql" />
</connectionStrings>

Note: Do not change the name of the connection string name. The name MusicStoreEntities matches the project’s DbContext class name. This is how Entity Framework figures out which connection string to use.

b) Update the entityFramework element as follow

<entityFramework>
<!--<defaultConnectionFactory
type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>-->
<defaultConnectionFactory type="Npgsql.NpgsqlFactory, Npgsql" />
<providers>
<provider invariantName="System.Data.SqlClient"
type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
<provider invariantName="Npgsql"
type="Npgsql.NpgsqlServices, Npgsql.EntityFramework" />
</providers>
</entityFramework>

c) Add the system.data element as follow.

<system.data>
<DbProviderFactories>
<add name="Npgsql Data Provider" invariant="Npgsql"
support="FF" description=".Net Framework Data Provider for Postgresql"
type="Npgsql.NpgsqlFactory, Npgsql" />
</DbProviderFactories>
</system.data>

Step 5: Modify the MvcStoreEntities (DbContext) class to configure the table names the Entities mapped to.
PostgreSQL creates data tables in the public schema by default. This is different than the default Entity Framework convention. Override the OnModelCreating method to specify the new table name mapping.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity().ToTable("Artist", "public");
    modelBuilder.Entity().ToTable("Album", "public");
    modelBuilder.Entity().ToTable("Cart", "public");
    modelBuilder.Entity().ToTable("Order", "public");
    modelBuilder.Entity().ToTable("OrderDetail", "public");
    modelBuilder.Entity().ToTable("Genre", "public");
}

Step 6: Disable migration
Comment out the following line in the global.asax.cs file. Otherwise you will get error since Npgsql does not support migration and database creation.

//System.Data.Entity.Database.SetInitializer(new MvcMusicStore.Models.SampleData());

Run

Compile and run the application again. You are now running MvcMusicStore with Entity Framework 6 against a postgreSQL database.

Download

A working demo is available at GitHub

Update (2015-12-20)

The original post was written almost two years ago. The MvcMusicStore was based on ASPNET MVC 3 and ASPNET Membership. There are some clarifications and update I would like to make:

  • The original MvcMusicStore uses two databases: one for the store, another for the membership. The blog post only covers converting the store database to PostgreSQL.

  • The original MvcMusicStore uses ASPNET Membership, you probably want to use the new ASPNET Identity instead. (“The ASP.NET Identity system is designed to replace the previous ASP.NET Membership and Simple Membership systems”).

  • I have created a storage provider for ASPNET Identity using PostgresSQL & EntityFramework 6 a few weeks ago. It supports the new columns in ASPNET Identity 2.0.  A working demo and source is available on
    GitHub: PostgreSQL.AspNet.Identity.EntityFramework

  • In the process of creating PostgreSQL.AspNet.Identity.EntityFramework, I found that Npgsql now has better Entity Framework 6 support. It can now auto create the database. The GitHub repo has a demo that take advantage of the auto database creation.

  • There is a new MvcMusicStore project on GitHub that’s targeting ASPNET 5 (vNext). It uses the ASPNET Identity and Entity Framework 7. Note: EntityFramework 7 currently only has provider for  SQL Server, SQLite, and InMemory provider. Npgsql also has an EF7 experimental provider.