Build A New MusicStore Project Using NancyFX And PostgreSQL

Introduction

 
In this article, you will begin to build a new MusicStore project, using NancyFX and PostgreSQL.
 

NancyFX

 
Nancy is a lightweight, low-ceremony framework for building HTTP based Services on .NET Framework/Core and Mono.
 
For more information about NancyFx, you can visit GitHub and its official site.
  • https://github.com/NancyFx/Nancy
  • http://nancyfx.org/
Preparatory Work
 
As we all know, there are two parts of MVC MusicStore- one is related to the store and another is related to MemberShip, which is given below.
 
 
Before building the project, we should migrate the database from the SQL Server Compact Edition database file to PostgreSQL and  MvcMusicStore is the only thing that we require. We can easily generate SQL files from SQL Server Compact. Subsequently, edit and execute this file in pg Admin.
 
Now, we have to migrate the music store's data to PostgreSQL and we need a new table to store the users. Hence, we create a new table in the database. The script is given below.
  1. CREATE TABLE public.sysuser(  
  2.     sysuserid character varying(100) COLLATE pg_catalog."default" NOT NULL,  
  3.     sysusername character varying(100) COLLATE pg_catalog."default" NOT NULL,  
  4.     sysuserpassword character varying(100) COLLATE pg_catalog."default" NOT NULL,  
  5.     sysuseremail character varying(100) COLLATE pg_catalog."default",  
  6.     CONSTRAINT sysuser_pkey PRIMARY KEY (sysuserid))WITH (  
  7.     OIDS = FALSE)TABLESPACE pg_default;  
  8. ALTER TABLE public.sysuser  
  9.     OWNER to dev;   
The next step which we should do is to configure PostgreSQL, so that we can visit it via the remote way. We edit the file pg_hba.conf, update the line host all all 127.0.0.1/32 md5 to host all all 0.0.0.0/0 md5.
 
Create Project And Install Packages
 
Now, we can create a new empty ASP.NET project and install some packages from Package Manager console.
  1. Install-Package Nancy -Version 1.4.3  
  2. Install-Package Nancy.Hosting.Aspnet -Version 1.4.1  
  3. Install-Package Nancy.Viewengines.Razor -Version 1.4.3  
  4. Install-Package Nancy.Authentication.Forms -Version 1.4.1  
  5. Install-Package Dapper  
  6. Install-Package Npgsql -Version 3.1.9   
The packages given above are all we need to install in this project. I remove the useless DLL's from the project as well. After this step, the references may look, as shown below.
 
 
Add and Configure Static Content
 
Add two folders in the project, where one is Content, the other is Scripts, and copy the resource from mvcmusicstore and what we need are given below.
 
 
 
For using those static contents, we need to add some configuration in the bootstrapper class.
 
Here, the content of bootstrapper class is given below.
    1. using Nancy;  
    2. using Nancy.Authentication.Forms;  
    3. using Nancy.Bootstrapper;  
    4. using Nancy.Conventions;  
    5. using Nancy.Session;  
    6. using Nancy.TinyIoc;  
    7. using NancyMusicStore.Common;  
    8. namespace NancyMusicStore {  
    9.     public class CustomBootstrapper: DefaultNancyBootstrapper {  
    10.         protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) {  
    11.             //Prevent errors on Linux    
    12.             StaticConfiguration.DisableErrorTraces = false;  
    13.         }  
    14.   
    15.         protected override void ConfigureApplicationContainer(TinyIoCContainer container) {  
    16.             base.ConfigureApplicationContainer(container);  
    17.         }  
    18.   
    19.         protected override void ConfigureConventions(NancyConventions conventions) {  
    20.             base.ConfigureConventions(conventions);  
    21.             conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("Scripts"));  
    22.             conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("Content"));  
    23.         }  
    24.     }  

      Conect to The PostgreSQL
       
      Add a connectionStrings section to the web.config
      1. <connectionStrings>  
      2.    <add name="pgsqlConn" connectionString="Host=127.0.0.1;Username=dev;Password=123456;Database=nancymusicstore;" />  
      3. </connectionStrings>   
      Setup a DBHelper, which helps us to operate the database easily.
        1. using Dapper;  
        2. using Npgsql;  
        3. using System.Collections.Generic;  
        4. using System.Linq;  
        5. using System.Data;  
        6. namespace NancyMusicStore.Common {  
        7.     public class DBHelper {  
        8.         //open connection           
        9.         private static IDbConnection OpenConnection() {  
        10.             var conn = new NpgsqlConnection(ConfigHelper.GetConneectionStr());  
        11.             conn.Open();  
        12.             return conn;  
        13.         }  
        14.   
        15.         //execute     
        16.         public static int Execute(string sql, object param = null, IDbTransaction transaction = null,  
        17.             int ? commandTimeout = null, CommandType ? commandType = null) {  
        18.             using(var conn = OpenConnection()) {  
        19.                 return conn.Execute(sql, param, transaction, commandTimeout, commandType);  
        20.             }  
        21.         }  
        22.   
        23.         //execute     
        24.         public static object ExecuteScalar(string cmd, object param = null, IDbTransaction transaction = null,  
        25.             int ? commandTimeout = null, CommandType ? commandType = null) {  
        26.             using(var conn = OpenConnection()) {  
        27.                 return conn.ExecuteScalar(cmd, param, transaction, commandTimeout, commandType);  
        28.             }  
        29.         }  
        30.   
        31.         //do query and return a list    
        32.         public static IList < T > Query < T > (string sql, object param = null, IDbTransaction transaction = null,  
        33.             bool buffered = trueint ? commandTimeout = null, CommandType ? commandType = null) where T: class {  
        34.             using(var conn = OpenConnection()) {  
        35.                 return conn.Query < T > (sql, param, transaction, buffered, commandTimeout, commandType).ToList();  
        36.             }  
        37.         }  
        38.   
        39.         //do query and return the first entity    
        40.         public static T QueryFirstOrDefault < T > (string sql, object param = null, IDbTransaction transaction = null,  
        41.             int ? commandTimeout = null, CommandType ? commandType = null) where T: class {  
        42.             using(var conn = OpenConnection()) {  
        43.                 return conn.QueryFirstOrDefault < T > (sql, param, transaction, commandTimeout, commandType);  
        44.             }  
        45.         }  
        46.     }  

          Create Models to map the tables
           
          Create a Models folder and add some classes, which map the tables.
           
           
          Create a Layout Page of NancyMusicStore
           
          Create a layout page in the path /Views/Shared, which is similar to what we do in ASP.NET MVC.
          1. @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>  
          2. <!DOCTYPE html>  
          3. <html>  
          4. <head>  
          5.     <title>@ViewBag.Title</title>    
          6.     <meta charset="UTF-8" />  
          7.     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />  
          8.     <meta name="viewport" content="width=device-width, initial-scale=1" />      
          9. <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />    </head>  
          10. <body>  
          11.     <div id="header">  
          12.         <h1><a href="/">NancyFx MUSIC STORE</a></h1>  
          13.         <ul id="navlist">  
          14.             <li class="first"><a href="/" id="current">Home</a></li>  
          15.             <li><a href="/store">Store</a></li>              
          16.             <li><a id="cart-status" href="/shoppingcart/index"></a></li>  
          17.             <li><a href="/storemanager">Admin</a></li>  
          18.         </ul>  
          19.     </div>  
          20.       
          21.     <ul id="categories">         
          22.     </ul>  
          23.      
          24.     <div id="main">  
          25.         @RenderBody()  
          26.     </div>  
          27.   
          28.     <div id="footer">  
          29.         built with <a href="http://nancyfx.org">Nancy 1.4.3</a>  
          30.     </div>  
          31.   
          32.     <script src="~/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>  
          33. @RenderSection("scripts", required: false)  
          34. </body>  
          35. </html>   
          In this layout page, we should pay attention to the first line, as it indicates that the ViewEngine which is used is Razor.
           
          We can't use the Html.RenderAction in NancyFX's base Razor ViewEngine, so I will use AJAX to complete the categories and the number of shoppingcart on the layout page.
           
          Now, we need to make this layout page as a default layout page, when we forget to appoint the layout page in a view.
           
          Add a _ViewStart.cshtml file in the Views folder and here is the content of this file.
          1. @{ Layout = "Views/Shared/_Layout.cshtml";}   
          There is a little difference in the file between NancyFX and ASP.NET MVC.
           
          Create Modules
           
          NancyFX's module is similar to MVC's controller. We create a new folder named Modules to store our modules.
           
          To be the first, adding a HomeModule class in the Modules folder is to show the index page, the index page shows us the top five selling albums and the categories of albums.
          1. using Nancy;  
          2. using NancyMusicStore.Common;  
          3. using NancyMusicStore.Models;  
          4. using System.Collections.Generic;  
          5. using System.Data;  
          6. using System.Linq;  
          7. namespace NancyMusicStore.Modules {  
          8.         public class HomeModule: NancyModule {  
          9.             public HomeModule(): base("/") {  
          10.                 Get["/"] = _ => {  
          11.                     var albums = GetTopSellingAlbums(5);  
          12.                     return View["Index", albums];  
          13.                 };  
          14.             }  
          15.   
          16.             /// <summary>    
          17.             /// get top count selling albums     
          18.             /// </summary>    
          19.             /// <param name="count"></param>    
          20.             /// <returns></returns>    
          21.             private List < Album > GetTopSellingAlbums(int count) {  
          22.                 string sql = "public.get_top_selling_albums";  
          23.                 var list = DBHelper.Query < Album > (sql, new {  
          24.                     num = count  
          25.                 }, nulltruenull, CommandType.StoredProcedure).ToList();  
          26.                 return list;  
          27.             }  
          28.         } 
          1. }   
          In the private method GetTopSellingAlbums, it uses a stored procedure to find out the top selling albums :
          1. -- FUNCTION: public.get_top_selling_albums(integer)  
          2. -- DROP FUNCTION public.get_top_selling_albums(integer);  
          3. CREATE OR REPLACE FUNCTION public.get_top_selling_albums(  
          4.     num integer)  
          5.     RETURNS SETOF "TABLE(albumid integer, genreid integer, artistid integer, title character varying, price numeric, albumarturl character varying)"  
          6.     LANGUAGE 'plpgsql'  
          7.     COST 100.0  
          8.     VOLATILE NOT LEAKPROOF   
          9.     ROWS 1000.0AS $function$  
          10. begin          
          11.     RETURN QUERY SELECT   
          12.     A.albumid,  
          13.     A.genreid,  
          14.     A.artistid,  
          15.     A.title,  
          16.     A.price,  
          17.     A.albumarturl   
          18.     FROM albums A  
          19.     LEFT JOIN orderdetails O ON A.albumid = O.albumid  
          20.     GROUP BY A.albumid,A.genreid,A.artistid,A.title,A.price,A.albumarturl  
          21.     ORDER BY count(O.albumid) desc LIMIT num;          end;  
          22.   
          23. $function$;  
          24. ALTER FUNCTION public.get_top_selling_albums(integer)  
          25.     OWNER TO dev;  
          Now, we need to add a corresponding view for the index page like ASP.NET MVC.
           
          Create an Index.cshtml in the path Views/Home.
          1. @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<List<NancyMusicStore.Models.Album>>  
          2. @{  
          3.     ViewBag.Title = "NancyFx Music Store";      
          4. }  
          5. <div id="promotion"></div>  
          6. <h3><em>Fresh</em> off the grill</h3>  
          7. <ul id="album-list">  
          8.     @foreach (var album in Model)  
          9.     {  
          10.         <li>  
          11.             <a href="javascript:;">  
          12.                 <img alt="@album.Title" src="@album.AlbumArtUrl" />  
          13.                 <span>@album.Title</span>  
          14.             </a>  
          15.         </li>  
          16. }  
          17. </ul>   
          At this time, we run the project and you may get the screenshot, as shown below.
           
           
          We mentioned that the categories of the albums should display on the layout page by AJAX. Let's start to complete it.
           
          Go back to the _Layout.cshtml and add JavaScript code to make it. Load the categories asynchronously.
          1. <script type="text/javascript">  
          2.     $(function () {  
          3.         $.ajax({  
          4.             url: "/store/genremenu",  
          5.             method: "get",  
          6.             dataType: "json",  
          7.             success: function (res) {  
          8.                 for (var i = 0; i < res.length; i++) {  
          9.                     $("#categories").append('<li><a href="javascript:;">' + res[i].name + '</a></li>');  
          10.                 }  
          11.             }  
          12.         });  
          13. });  
          14. </script>   
          The request URL is the same as MVC MusicStore, so we need to add a StoreModule class to deal with the request.
            1. using Nancy;  
            2. using NancyMusicStore.Common;  
            3. using NancyMusicStore.Models;  
            4. using NancyMusicStore.ViewModels;  
            5. using System.Collections.Generic;  
            6. using System.Data;  
            7. using System.Linq;  
            8. namespace NancyMusicStore.Modules {  
            9.     public class StoreModule: NancyModule {  
            10.         public StoreModule(): base("/store") {  
            11.             Get["/genremenu"] = _ => {  
            12.                 return Response.AsJson(GetGenreList());  
            13.             };  
            14.         }  
            15.   
            16.         private IList < Genre > GetGenreList() {  
            17.             string cmd = "public.get_all_genres";  
            18.             return DBHelper.Query < Genre > (cmd, nullnulltruenull, CommandType.StoredProcedure);  
            19.         }  
            20.     }  

              Now, when we visit the index page, it will send an asynchronous request to the http://yourdomain.com/store/genremenu and load the categories.
               
              The index page of NancyMusic changes and resembles what is shown below.
               
               
              What Next?
               
              In my next article, I will complete the albums' information and show you how to manage the albums.


              Similar Articles