Aurelia, NancyFX and Visual Studio Code

Having been a C, C++ and C# developer for several years, I’ve never really gotten into JavaScript for large projects. After all, you have to admit that having to do:

(function(){
    ...
})();

In order to work around JS scoping mess sucks. With ES6 (ES2015) and ES7 it seems that JS is finally cleaning up these (and other) legacy issues.

Some of the newer frameworks taking advantage the new JavaScript show promise and we may finally be able to build full stack web sites with reduced frustration. I’ve had a chance to explore Angular 2.0, React and Aurelia. Aurelia seems the cleanest thus far though React and Angular have a larger following. I figured there’d be enough documentation on how to set React/Angular elsewhere so I’d do one on Aurelia instead.

Also, while you can do lots of this stuff using the full blown Visual Studio, I’ve opted to use Visual Studio Code on MacOS. Everything here should work fine on Windows though here might be a few more setup steps to get the various tools used here properly running.

Tools

This article uses:
* Vistual Studio Code
* npm (part of NodeJS)
* DNX
* jspm
* yoeman
* grunt

Setup

You’re going to have to download and install the following to get going:

Once installed, you can add the following to your ~/.bash_profile

code () { VSCODE_CWD="$PWD" open -n -b "com.microsoft.VSCode" --args $* ;}

This is the MacOS equivalent to being able to launch VSCode by adding it to your windows PATH. In this case, by typing code . The rest of the setup is done in the command line.

Git Repo

Next, we can install the following as we need them but you can just as easily do so when you are ready to use the commands. On MacOS, you will have to prefix these with a sudo.

npm install jspm -g
npm install grunt -g
npm install yo -g

We’re now ready to set up a blank asp.net project. I usually set up a repo on GitHub or BitBucket first. Then go to your root Projects folder where you like to keep all your source and set up your project directory:

## Setup the folder
cd ~/Projects
mkdir aurelianancy
cd aurelianancy

## Initialize git
## Point to your git repo here. Mine's at github.com/nsainaney/aurelianancy.git 
git init
git remote add origin https://github.com/nsainaney/aurelianancy.git

## Setup a gitignore file
curl https://raw.githubusercontent.com/github/gitignore/master/Global/VisualStudioCode.gitignore -o .gitignore

## Launch VSCode
code .

Don't forget the period – it tells VSCode to launch with current directory as your workspace.

Nancy Setup

Next, we'll set up the Asp.Net Nancy project with yomen. Execute yo aspnet and you should see the following. Select Nancy ASP.NET Application and give it a name. I named mine AureliaNancy

     _-----_
    |       |    .--------------------------.
    |--(o)--|    |      Welcome to the      |
   `---------´   |   marvellous ASP.NET 5   |
    ( _´U`_ )    |        generator!        |
    /___A___\    '--------------------------'
     |  ~  |     
   __'.___.'__   
 ´   `  |° ´ Y ` 

? What type of application do you want to create? 
  Empty Application 
  Console Application 
  Web Application 
  Web Application Basic [without Membership and Authorization] 
  Web API Application 
❯ Nancy ASP.NET Application 
  Class Library 
  Unit test project 

You should now see an AureliaNancy folder in VSCode

Aurelia Folder Setup

Moving on, I'd like to organize my code with the following folder heirarchy:

AureliaNancy
|-- api
|--- HomeModule.cs
|-- wwwroot
|--- index.html
|- Startup.cs
|- project.json

Where the api folder will have the NancyModules and wwwroot will have my aurelia web files. We'll set up the routing later but first, let's set up the wwroot folder and I'm going to do this by also setting up Aurelia. For this, we use jspm install aurelia-framework with default values for everything except for the server baseURL:

cd AureliaNancy\
jspm install aurelia-framework

warn Running jspm globally, it is advisable to locally install jspm via npm install jspm --save-dev.

Package.json file does not exist, create it? [yes]:
Would you like jspm to prefix the jspm package.json properties under jspm? [yes]:
Enter server baseURL (public folder path) [./]:./wwwroot
Enter jspm packages folder [wwwroot/jspm_packages]:
Enter config file path [wwwroot/config.js]:
Configuration file wwwroot/config.js doesn't exist, create it? [yes]:
Enter client baseURL (public folder URL) [/]:
Do you wish to use a transpiler? [yes]:
Which ES6 transpiler would you like to use, Babel, TypeScript or Traceur? [babel]:

Then run jspm install aurelia-bootstrapper

At this point, you may want to add jspm_packages to your .gitignore. We can now create a static file index.html in the wwwroot folder

<html>
    <head>
    </head>
    <body>
        <h1>This is where Aurelia stuff will live</h1>
    </body>
</html>

OWIN Routing

Now we have to set up OWIN and the proper routing. For this, we first add dependancies to the project.json. We’ll need to add Microsoft.AspNet.StaticFiles and Microsoft.Framework.Runtime

{
    "dependencies": {
        "Microsoft.AspNet.Server.Kestrel": "1.0.0-beta7",
        "Microsoft.AspNet.Owin": "1.0.0-beta7",
        "Microsoft.AspNet.StaticFiles": "1.0.0-beta7",
        "Microsoft.Framework.Runtime": "1.0.0-beta6",
        "Nancy": "1.2.0"
    },
    "commands": {
        "kestrel": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5000"
    },
    "frameworks": {
        "dnx451": {}
    }
}

VSCode may prompt you to Restore nuget packages in order to download these dependancies. You can also just execute dnu restore in a command shell.

Now to get our routing set up in our Startup.cs:

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.StaticFiles;
using Nancy.Owin;

public class Startup
{        
    string WebRoot { get; set; }
    public Startup(IHostingEnvironment env)
    {
        // We can't use Server.MapPath so let's stash the WebRoot
        WebRoot = env.WebRootPath;
    }

    public void Configure(IApplicationBuilder app)
    {   
        var options = new FileServerOptions();
        options.EnableDefaultFiles = true;
        options.FileProvider = new PhysicalFileProvider(WebRoot + "/wwwroot");
        options.StaticFileOptions.FileProvider = options.FileProvider;
        options.StaticFileOptions.ServeUnknownFileTypes = true;
        options.DefaultFilesOptions.DefaultFileNames = new[] {"index.html"};
        app.UseFileServer(options);

        app.Map("/api", api=>{
            api.UseOwin(x => x.UseNancy());
        });            
    }
}

Now, if you launch the web server with dnx kestrel or Cmd-Shift-P kestrel, you should be able to navigate to http://localhost:5000 to view the static index.html and http://localhost:5000/api to see the “Hello World” from the HomeModule.

This is a great place to stop. You can now either set up AngularJS, React or Aurelia in the wwwroot folder.

References

Advertisements

Saving a Silverlight RIA (or WCF RIA) filtered result set

Silverlight RIA (now WCF RIA) services allows developers to create n-tier applications on top of ASP.Net. There are numerous videos and articles on this new technology. This article assumes you know all about RIA Services, DomainDataSources and how FilterDescriptors can be used with ControlParameters to query data from the server side.

I recently needed to save the filtered result set and didn’t find this to be straight forward. I did come up with the solution described here.

Let’s assume you have a simple Contacts database and the user is able to filter by FirstName, LastName etc. Now let’s assume you need to be able to save the filtered result-set (or alternatively bulk modify the filtered result-set). The challenge is to retrieve the Contact id’s with the client side filters applied.

If we were to implement a save function as follows:

IQueryable SaveContacts()
{
    var query = from contact in
                Context.Contacts select contact.Id

    // use the id's in query
}

This would return all the contacts in the database and not the filtered set. Thankfully, the DomainService class implements a virtual function where we can intercept the client side filter expression:

public virtual IEnumerable Query(QueryDescription queryDescription, out int
totalCount);

We however have to merge this expression with the Context.Contacts IQueryable. This didn’t prove to be straight forward as you have to know a little bit about Expressions. I overrode the function like so to save the query for later retrieval:

IQueryable savedQuery;

public override System.Collections.IEnumerable Query(QueryDescription queryDescription,
                                                      out int totalCount)
{
    if (queryDescription.Method.Name == "SaveContacts")
	savedQuery = queryDescription.Query as IQueryable;

    return base.Query(queryDescription, out totalCount);
}

We now have the query expression which contains the “Where” clause generated by the client side FilterDescriptors. We have to merge this query expression into the Context.Contacts query to get the filtered query. The following implementation of SaveContacts demonstrates how to accomplish this:

// The FindWhereMedhod recursively looks for the "Where" clause in the query
// expression
MethodCallExpression FindWhereMethod(MethodCallExpression expression)
{
    if (expression == null)
        return null;

    if (expression.Method.Name == "Where")
        return expression;

    foreach(Expression sub in expression.Arguments)
    {
        MethodCallExpression result = FindWhereMethod(sub as MethodCallExpression);
        if (result != null)
            return result;
    }
    return null;
}

public IQueryable SaveContacts()
{
    IQueryable q = GetContacts();

    MethodCallExpression method = savedQuery.Expression as MethodCallExpression;
    MethodCallExpression whereMethod = FindWhereMethod(method);

    IQueryable contactIds;

    if (whereMethod != null)
    {
        ParameterExpression pe = Expression.Parameter(typeof(Contact), "contact");
        UnaryExpression unaryExpr = whereMethod.Arguments[1] as UnaryExpression;
        MethodCallExpression whereCallExpression = Expression.Call( typeof(Queryable),
                                                                    "Where",
                                                                    new Type[] { q.ElementType },
                                                                    q.Expression, unaryExpr.Operand);
        IQueryable merged = q.Provider.CreateQuery(whereCallExpression) as IQueryable;

        contactIds = from c in merged
                    select c.Id;
    }
    else
    {
        contactIds = from c in q
                     select c.Id;
    }
    // You now have a query merged with the client side contact id's. You can now do
    // whatever you'd like with the contact id's including save them via SqlCommands

    DbCommand command = Context.GetCommand(contactIds);
    string connectionStr = ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;

    using (TransactionScope scope = new TransactionScope())
    using (SqlConnection connection = new SqlConnection(connectionStr))
    {
        connection.Open();
        try
        {
            // There are other queries here that necessitate the transaction scope however,
            // these have been removed for simplicity

            string insertCmd = "insert into ContactResultSet (ContactId) select Id from ({0}) as subQuery";
            insertCmd = string.Format(insertCmd, command.CommandText);
            SqlCommand cmd = new SqlCommand(insertCmd, connection);

            // We have to copy over the parameters
            foreach (SqlParameter param in command.Parameters)
                cmd.Parameters.AddWithValue(param.ParameterName, param.Value);

            cmd.ExecuteNonQuery();
            scope.Complete();
        }
        finally
        {
            connection.Close();
        }
    }
}