SyntaxHighlighter

Thursday, December 17, 2015

Merging DLL project Settings with WebApp web.config

Recently I was working on MVC Web API project and needed to store key / value pairs. I decided to use <applicationsettings> because it is strongly typed and compiler auto-generates Settings class with each key as property. But I wanted to create Settings in referenced DLL project with external XML file to hold configuration items. As DLL can’t have their own .config file, I have to merge settings to Web application web.config file. This post describes steps to merge settings DLL project settings with Web application web.config file.

Creating new Settings was easy. Open DLL project Properties, click on “Settings” tab, click Create new settings link. New file will be created under Properties folder named: Settings.settings and App.config will be updated as follows:

<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      <section name="{Project Namespace}.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    </sectionGroup>
  </configSections>
  <applicationSettings>
    <{Project Namespace}.Properties.Settings>
      <setting name="MyFirstSetting" serializeAs="String">
        <value>1234</value>
      </setting>
    </{Project Namespace}.Properties.Settings>
  </applicationSettings>
</configuration>

Step 1 – Externalize setting file in DLL project


This can done by updating App.config file as below.
...
<applicationSettings>
    <{Project Namespace}.Properties.Settings configSource="serviceSettings.config" />
</applicationSettings>
Create new xml file “serviceSettings.config” (same folder as App.config) and move all settings to this new file.
<{Project Namespace}.Properties.Settings>
  <setting name="MyFirstSetting" serializeAs="String">
    <value>1234</value>
  </setting>
</{Project Namespace}.Properties.Settings>
At this point you have created external settings file.

Step 2 – Merge with web.config


Open web.config and copy following fragment from App.config to web.config under <configuration> >> <configSections> node.
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      <section name="{Project Namespace}.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
And add these line under <configuration> node.
<applicationSettings>
  <{Project Namespace}.Properties.Settings configSource="serviceSettings.config" />
</applicationSettings>
And copy “serviceSettings.config” file from DLL project to Web application (same folder as web.config)

Finally you are done. You can now access strongly typed Settings in your DLL project.
{Project Namespace}.Properties.Settings.Default.MyFirstSetting
To automate copy operation, you can write post build command to move serviceSettings.config file from DLL project to Web application project folder.
  
Hope this helps someone.

Thanks,
-Javed

Wednesday, December 2, 2015

SharePoint + TypeScript + Angular + Gulp + Visual Studio 2015 + more

I have been playing with Angular, Node and Gulp frameworks for a while mostly with WebStorm IDE and recently started using TypeScript and I love it. To learn and understand how these framework will integrate with SharePoint solution, I created simple Web Part farm project which does nothing at the moment, except few text boxes binding with Angular. WebStorm does not support SharePoint Solution, but luckily the recent release of Visual Studio 2015 has built-in support for Node, Task Runner and Bower. You can also add Task Runner support in Visual Studio 2013 by installing this plugin.

This post is about my learning experience integrating all these technologies together with SharePoint, Visual Studio and Gulp workflows. Here is the list of tools/frameworks I used:

Node – JavaScript runtime engine
Angular – JavaScript framework
jQuery – JavaScript framework
Gulp – Automation and build workflow (install global only)
Typescript – Built-in support in VS.NET 2015. Other version requires WebEssentials
TSD – DefinitelyType definition for Typescript repository.
Bower – Front-end optimized framework/libraries package manager. VS.NET 2015 has built-in support for Bower.
NPM – Node package manager (comes with Node)
Git – required by NPM and Bower

You can open link for instructions to install on your environment.


Step 1 – Create SharePoint Project


Create empty SharePoint 2013 project and added SharePoint Project Item: EmpDir_Module. This is the folder where we will be generating compiled/bundled/minified typescript file and other JavaScript files which we will be deploying to SharePoint. You can create dummy placeholder files and include it in Elements.xml or once files are copied (by Step 6) you can use "Include in Project" and update Elements.xml.


Step 2 – Setup


I assume all tools are installed and ready to go. First we need to initialize *.json files which contains references to files dependencies with versions installed, which can be used by other developers to bring in all dependencies.

Open Command Prompt, navigate to root Project directory and run following commands. I tried running this command from Visual Studio Package Manager Console but it hanged. These commands will ask series of questions to initialize *.json file.

$ npm init
- this command creates package.json file
$ bower init
- this command creates bower.json file
$ tsd init
- this command creates tsd.json and folder “typings”


Step 3 – Install Dependencies


Once we have all the *.json files, we can begin installing dependencies for our project. We can run these command either from external command prompt of Visual Studio Package Manager Console window.

$ npm install --save-dev gulp
- install gulp for the project and save as DEV dependency in package.json
$ npm install --save-dev gulp-concat gulp-print gulp-rename gulp-sequence gulp-typescript -uglify run-sequence
- install gulp plugins and save as DEV dependency in package.json

* Note : npm installs all components under “node_modules” folder in root.

$ bower install jquery angular --save
- install jquery and angular framework and save dependency in bower.json

* Note : bower installs all components under “bower_components” folder in root.

$ tsd install jquery angular --save
- install jquery and angular DefinitelyTyped definitions and save dependency in tsd.json

* Note : tsd installs all components under “typings” folder in root.

* Note : --save switch will record dependency as minimum required and if latest version available, will download the latest build. If you want to stick to any version, you can use --save-exact switch.


Step 4 – Write code


Before you start writing Typescript files, it’s better to decide the project folder structure. There is awesome Angular style guide compiled by John Papa here. As we are not going to deploy *.ts (typescript files) to SharePoint, I prefer to keep those files away in a separate folder. Here is how my project folder structure looks like:


I have created folder: “App” which contains typescripts source and compiled JavaScript files under “TScripts” and “compiled” folders. I have not included “bower_components”, “node_modules” and its contents to the project, as we are not deploying these files. We do need files from “bower_components” and will see how to deploy those as Module. I added “typings” folder, as I sometimes open *.d.ts files to see method signature or interface description. In Visual Studio you can select any method on typescript file and hit F12 will take you to method definition in *.d.ts file. Pretty handy.

I will not discuss about writing angular in Typescript, as that is a huge topic in itself. Pluralsight has several trainings on this subject.

Typescript file naming convention is for a reason we will see when we go to Gulp section. The naming convention will come handy during bundling and order of files.


Step 5 – Write Gulp Workflow


This is where all magic happens. Create a new JavaScript file: “gulpfile.js” in the root and copy/paste content below:
/// <binding BeforeBuild='default' />
/**
 * Created by Javed on 8/15/2015.
 */
'use strict';

var gulp = require('gulp'),
    gprint = require('gulp-print'),
    runSequence = require('run-sequence'),
    ts = require('gulp-typescript'),
    bundle = require('gulp-concat'),
    minify = require('gulp-uglify'),
    rename = require('gulp-rename');

var paths = {
    "TSSrc": 'App/TScripts/',
    "JSCompiled": 'App/compiled/',
    "JSDest": 'EmpDir_Module',
    "BowerSrc": "bower_components/"
}

var config = {
    "TypeScriptFiles": [
        paths.TSSrc + '**.ts'
    ],
    "BowerFiles": [
        paths.BowerSrc + 'jquery/dist/jquery.min.js',
        paths.BowerSrc + 'angular/angular.min.js'
    ],
    "BundleSrc": [
        paths.JSCompiled + '*.module.js',
        paths.JSCompiled + '*.config.js',
        paths.JSCompiled + '*.run.js',
        paths.JSCompiled + '*.constant.js',
        paths.JSCompiled + '*.directive.js',
        paths.JSCompiled + '*.service.js',
        paths.JSCompiled + '*.controller.js'
    ],
    "BundleFileName": "ed.bundle.js",
    "MinifyFileName": "ed.bundle.min.js"
}

gulp.task('compile', function () {
    var tsc = gulp
        .src(config.TypeScriptFiles)
        .pipe(gprint())
        .pipe(ts({ "noImplicitAny": true, "removeComments": true }));

    return tsc.js.pipe(gulp.dest(paths.JSCompiled));
});

gulp.task('bower', function () {
    return gulp
        .src(config.BowerFiles)
        .pipe(gprint())
        .pipe(gulp.dest(paths.JSDest));
});

gulp.task('prod_build', function() {
    return gulp
        .src(config.BundleSrc)
        .pipe(bundle(config.BundleFileName))
        .pipe(gprint())
        //.pipe(rename(config.MinifyFileName))
        //.pipe(minify())
        .pipe(gulp.dest(paths.JSDest));
});

gulp.task('default', function () {
    runSequence('compile', 'bower', 'prod_build');
});

You can read more on Gulp here. I will explain in brief about gulpfile.js and each task.

As gulpfile.js is plain JavaScript file, you can define your own variable and functions. I have defined 2 variables “paths” and “config” objects with property for source, destination or file name which we will be using in gulp tasks. I found this approach to be better than hard coding paths in tasks definition itself.

Compile task:
  1. Grab all *.ts files under “App/TScripts” folder
  2. Compile to JavaScript
  3. Copy output to “App/compiled” folder
Bower task:
  1. Grab bower_component files like jquery, angular
  2. Copy to SharePoint module folder “EmpDir_Module”
Prod_Build task:
  1. Grab all compiled JavaScript from “App/compiled” folder in same order as defined in config.BundleSrc array
  2. Conact all files and save “ed.bundle.js”
  3. Rename file to “ed.bundle.min.js”
  4. Minify/uglify this file
  5. Copy to SharePoint module folder “EmpDir_Module”   
Default task:
  1. Run all tasks in sequence.
* Note : When default task in defined executing “gulp” command with no parameter runs default task. You can run individual task by executing “gulp bower” from command line or VS Package Manager Console.


Step 6 – Attaching Gulp task with Visual Studio build event


The last step. Open Task Runner from View > Other Windows > Task Runner Explorer. You should see Gulpfile.js node with all task names. Right click on any task and select Bindings to attach to any build event. See the screenshot below, where default task is attached to Before Build event.


* Note : While in development mode, you can write new task "dev_build" and instead of copying to Module folder, you can map Style Library or any other library (where you are deploying your .js files) and copy to this mapped location. No need to deploy WSP package :)

This concludes the long post. Hope this help someone.

Thanks,
-Javed