Tuesday, 19 July 2016

Run Entity Framework Core DB Scaffold with Gulp

If you're using ASP.NET Core Entity Framework (database first) you may have to rebuild your DB context and models frequently.

Instead of manually running "Scaffold-DbContext" in package manager console and updating the DBContext (as per https://docs.efproject.net/en/latest/platforms/aspnetcore/existing-db.html), you can speed things up with a Gulp task.

You can create a Gulp task and use the "node-powershell" to quickly run the scaffold command in dotnet CLI and automatically remove the hardcoded connection string from the Context class.

The script below creates a Gulp task ("build-DbContext") that will:

  1. Get the database connection string from appsettings.json
  2. Delete any previously created database models
  3. Create new models and DbContext (called ProjectDbContext), by running the "dotnet ef dbcontext scaffold" command
  4. Replace the "OnConfiguring" method with a new constructor in the "ProjectDbContext" class
This can then be quickly executed in Task Runner as you need it.


gulpfile.js (build-DbContext)


var gulp = require('gulp');
var fs = require('fs');
var shell = require('node-powershell');
var del = require('delete');
var change = require('gulp-change');
var savefile = require('gulp-savefile');

var _appSettingsPath = "./appsettings.json";

var _contextProvider = "Microsoft.EntityFrameworkCore.SqlServer"
var _contextClass = "ProjectDbContext";
var _contextOutputDir = "Models";

gulp.task('build-DbContext', function () {
    ScaffoldDbContext();
});

//Get connection string from the appsettings file
function GetConnectionString() {
    var _appsettingJSON = JSON.parse(fs.readFileSync(_appSettingsPath, "utf8").replace(/^\uFEFF/, ''));

    return _appsettingJSON.data.DefaultConnection;
}

function ScaffoldDbContext() {
    _defaultConnection = GetConnectionString();

    //Clear out old models first
    del("models/**/*");

    var _powerShell = new shell({ executionPolicy: 'Bypass', debugMsg: true });

    _powerShell.addCommand('dotnet ef dbcontext scaffold "' + _defaultConnection + '" ' + _contextProvider + ' --context "' + _contextClass + '" --output-dir "' + _contextOutputDir + '"  --data-annotations')
        .then(function () {
            return _powerShell.invoke();
        })
        .then(function (output) {
            console.log(output);
            _powerShell.dispose();
        })
        .then(function () {
            UpdateDbContext();
        })
        .catch(function (err) {
            console.log(err);
            _powerShell.dispose();
            this.emit('end');
        });

    
}

function UpdateDbContext() {
    return gulp.src(_contextOutputDir + "/" + _contextClass + ".cs")
            .pipe(change(RemoveInlineContextConfig))
            .pipe(savefile());
}

function RemoveInlineContextConfig(content, done) {
    var _constructorMethod = "public " + _contextClass + "(DbContextOptions<" + _contextClass + "> options)\n: base(options)\n{ }";

    //Replace the "OnConfiguring" method with new constructor
    var _newContent = content.replace(/protected.*OnConfiguring(((?!}).|[\r\n])*)}/g, _constructorMethod);

    done(null, _newContent);
}


Project

A basic example project looks like:


appsettings.json

Make sure you have a connection string value such as "DefaultConnection":
{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "data": {
    "DefaultConnection": "Server=.\\[INSTANCENAME];Database=[DATABASENAME];user id=[USERNAME];password=[PASSWORD];Trusted_Connection=True;"
  }
}

package.json

Add the required Gulp dependencies:
{
 "version": "1.0.0",
 "name": "avros-data",
 "private": true,
  "devDependencies": {
    "gulp": "3.9.1",
    "node-powershell": "2.0.2",
    "fs": "0.0.2",
    "delete": "0.3.2",
    "gulp-change": "1.0.0",
    "gulp-savefile" : "0.1.1"
  }
}

project.json

    "Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0",
    "Microsoft.EntityFrameworkCore": "1.0.0",
    "Microsoft.EntityFrameworkCore.Tools": {
      "version": "1.0.0-preview2-final",
      "imports": [
        "portable-net45+win8+dnxcore50",
        "portable-net45+win8"
      ]
    }

  "tools": {
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
  },


Once setup you can run the within Task Runner Eplorer

Sunday, 21 June 2015

Compile LESS with Gulp and Task Runner

A simple GULP compiling and minifying LESS files to CSS using Visual Studio Task Runner Explorer.

This example watches a folder containing LESS files and compiles them to CSS on any changes or when the project is built or cleaned.

By doing this you can have a primary LESS file, and when this or any referenced by it through “@import” statements, compiling to a single CSS file.



In this example, the primary LESS file is called “main.less”, it will be compiled to “main.css” and “main.min.css”.

/// 
var gulp = require('gulp');
var concat = require('gulp-concat');

var less = require('gulp-less');
var minifyCss = require('gulp-minify-css');
var rename = require("gulp-rename");
var watch = require('gulp-watch');
var del = require('delete');

var sass = require('gulp-ruby-sass')
var notify = require("gulp-notify")
var bower = require('gulp-bower');



var MainConfig = {
    watch: ['main/**/*.less'],          //folder to watch for changes
    src: ['main/main.less'],            //main LESS file to compile 
    targetDir: '../target-folder/css',  //folder to output compiled CSS
    targetName: 'main',                 //name of compiled CSS file
    targetMinName: 'main.min'           //name of minified CSS file
}


function BuildMain() {
    gulp.src(MainConfig.src) 
        .pipe(less())
        .on('error', swallowError)
        .pipe(gulp.dest(MainConfig.targetDir))
        .pipe(minifyCss())
        .on('error', swallowError)
        .pipe(rename({ basename: MainConfig.targetMinName, extname: ".css" }))
        .pipe(gulp.dest(MainConfig.targetDir));


    //If theres an error generating the css, delete the existing css files
    function swallowError(error) {
        var _cssName = MainConfig.targetDir + "/" + MainConfig.targetName + ".css";
        var _cssMinName = MainConfig.targetDir + "/" + MainConfig.targetMinName + ".css";

        del.sync(_cssName, { force: true });
        del.sync(_cssMinName, { force: true });

        this.emit('end');
    }
}

//Tasks to be used by Task Runner
gulp.task('project-start', function () {
    watch(MainConfig.watch, function () {
        BuildMain();
    });
});
gulp.task('project-clean', function () {
    BuildMain();
});
gulp.task('project-build', function () {
    BuildMain();
});


The script is saved as “gulpfile.js” in the root of the project

Tuesday, 7 October 2014

Bat SQL Script to restore Database

When developing a site I quite often have to restore SQL databases and this can take up a lot of time.

If you have to frequently restore SQL backups you can use this script:


@echo off

set BAKFILE=%cd%\[database-backup].bak
set DBNAME=[database-name]
SET SERVERINSTANCE=[.\SQLEXPRESS2008R2]
SET DBUSER=[DatabaseUser]

set /p RESPONSE="Restore %BAKFILE% to %DBNAME%? (Y/N)"

if NOT %RESPONSE%==y goto END

::clear any existing connections
sqlcmd -E -S %SERVERINSTANCE% -Q "ALTER DATABASE %DBNAME% SET SINGLE_USER WITH ROLLBACK IMMEDIATE"

::restore DB
set SQLCMD=-E -S %SERVERINSTANCE% -Q "RESTORE DATABASE %DBNAME% FROM DISK='%BAKFILE%'"
sqlcmd %SQLCMD%

::remove user if it already has access to the DB
sqlcmd -E -S %SERVERINSTANCE% -Q "USE %DBNAME%; EXEC sp_revokedbaccess '%DBUSER%'"

::add user to the DB and grant required access
sqlcmd -E -S %SERVERINSTANCE% -Q "USE %DBNAME%; EXEC sp_grantdbaccess '%DBUSER%', '%DBUSER%'"
sqlcmd -E -S %SERVERINSTANCE% -Q "USE %DBNAME%; EXEC sp_addrolemember 'db_owner', '%DBUSER%'"
sqlcmd -E -S %SERVERINSTANCE% -Q "USE %DBNAME%; EXEC sp_addrolemember 'db_datareader', '%DBUSER%'"
sqlcmd -E -S %SERVERINSTANCE% -Q "USE %DBNAME%; EXEC sp_addrolemember 'db_datawriter', '%DBUSER%'"

pause

:END



Update the following variables to match our needs:
  • BAKFILE – the name of the file to restore
  • DBNAME – the name of the database to overwrite
  • SERVERINSTANCE – name of the SQL server instance
  • DBUSER – Existing database user to grant access to 

Only change the parts in square brackets.

Save it as a .bat file in the same directory as the .bak file you want to restore.

When you run it you will be prompted to continue, then it will clear connections to the existing database, restore the backup and grant the required user read/write access.

Be careful if you use and make sure you keep backups, it might make things a little quicker but it's also easier to make mistake.

Thursday, 28 August 2014

Umbraco 7 - Highlighting selected items in custom tree

Custom Tree elements are fairly straight forward using the “umb-tree” directive.

If you want to highlight selected nodes in a custom tree the same way as they are in the main content tree, you’ll have to use the “treeNodeSelect” event in your controller.

Custom Tree Directive




Controller

function DialogControllerFunction($scope, dialogService, eventsService, navigationService, appState, treeService) {
    var dialogOptions = $scope.dialogOptions;
    var node = dialogOptions.currentNode;

    $scope.dialogTreeEventHandler = $({});


    $scope.dialogTreeEventHandler.bind("treeNodeSelect", function (ev, args) {
        args.event.preventDefault();
        args.event.stopPropagation();

        var _selectedNode = args.node;
        var _treeRootElement = $(args.event.currentTarget).closest(".umb-tree");

        //Clear previously selected items (remove if you want to select multiple)
        //  iterate through each child element in the current tree
        _treeRootElement.find("li[tree='child']").each(function () {
            $(this).scope().currentNode = null;
        })

        //this will highlight the current node
        args.element.currentNode = _selectedNode;
    });

}
angular.module("umbraco").controller("CustomApp.Namespace.DialogController", DialogControllerFunction);

In this example, my custom tree is a navigation dialog:



Friday, 8 August 2014

Add new Document Type icons to Umbraco 7

The process for adding new document type icons is a little more complicated in Umbraco 7.

Document type icons are now displayed as fonts (svg/woff/eot files), the icon sets are created using Icomoon.io.

To add new icons you have to create a new set, these can then be included in Umbraco by created a new plugin.

Step 1 - Create new icon set


Go to https://icomoon.io/app, here you can load in icons from other available sets or create your own.

Select any icons you want to include in your new set.



Next, click on the "Font" button at the bottom.

In the next screen:


You'll see a list of the selected icons, take note of the values below each icon, these "code points" are used in CSS to locate each icon in the final file.

Click download and unzip.


Step 2 - Add to Umbraco


To include the new icons in Umbraoo, you have to create a new plugin to load in CSS which references the font files.

Create a new folder in "App_Plugins" for your plugin, in here create a new "package.manifest" and CSS file, next copy in the downloaded font files.

My package is called "IcoElegant", here's my layout:



Package Manifest
{
    css: [
        '~/App_Plugins/NewIcons/css/icoelegant.css'
 ]
}


Change this to reference your CSS file.

CSS
@font-face {
    font-family: 'icoelegant';
    src:url('../Fonts/icoelegant.eot');
    src:url('../Fonts/icoelegant.eot?#iefix') format('embedded-opentype'),
    url('../Fonts/icoelegant.svg') format('svg'),
    url('../Fonts/icoelegant.woff') format('woff'),
    url('../Fonts/icoelegant.ttf') format('truetype');
}


[class^="icon-icoelegant-"], [class*=" icon-icoelegant-"] {font-family:'icoelegant';width:auto;height:auto;}
.icon-icoelegant-mobile:before{content:'\e000'}
.icon-icoelegant-laptop:before{content:'\e001'}
.icon-icoelegant-desktop:before{content:'\e002'}
.icon-icoelegant-tablet:before{content:'\e003'}
.icon-icoelegant-phone:before{content:'\e004'}
.icon-icoelegant-document:before{content:'\e005'}
.icon-icoelegant-documents:before{content:'\e006'}
.icon-icoelegant-search:before{content:'\e007'}
.icon-icoelegant-clipboard:before{content:'\e008'}
.icon-icoelegant-newspaper:before{content:'\e009'}
.icon-icoelegant-notebook:before{content:'\e00a'}
.icon-icoelegant-book-open:before{content:'\e00b'}
.icon-icoelegant-browser:before{content:'\e00c'}
.icon-icoelegant-calendar:before{content:'\e00d'}
.icon-icoelegant-presentation:before{content:'\e00e'}


Change the font-family name and src attributes to match your font files.

Each icon needs a prefix (starting with ".icon"), you'll also have to swap the content value for the "code point" value when exporting from Icomoon.

Save the files and refresh Umbraco, you may have to clear the ClientDependency cache "\App_Data\TEMP\ClientDependency" first, the new icons should now be available in the Document Type section of Umbraco.

Monday, 2 June 2014

Programmatically add new member to a group in Umbraco 7

If you want to assign a new member to an Umbraco member group in .net, you have to use the "MemberService.AssignRole" method.


IMember _newMember = ApplicationContext.Current.Services.MemberService.CreateMember("username", "user@email.com", "Full Name", "Member");
Services.MemberService.Save(_newMember);
Services.MemberService.AssignRole(_newMember.Id, "Group Name");


The new member has to be saved first before you can assign it to role/group.

Monday, 12 May 2014

Regular Expression Command Line Arguments Parser



Here's a simple way to extract a list of switch/flags and their parameters from a command line argument in C#.

Regex regex = new Regex(@"(?< switch> -{1,2}\S*)(?:[=:]?|\s+)(?< value> [^-\s].*?)?(?=\s+[-\/]|$)");

string commands = "--switch value1 --switch2 \"c:\\folder 1\\file1.txt\" -switch-3 value-3 --switch4 -switch5";

List< KeyValuePair< string, string> >  matches = (from match in regex.Matches(commands).Cast< match> ()

select new KeyValuePair< string, string> (match.Groups["switch"].Value, match.Groups["value"].Value)).ToList();

foreach (KeyValuePair< string, string>  _match in matches)
{
Response.Write("
switch:" + _match.Key + "  value:" + _match.Value);
}

You can pass in a command with multiple switches such as "-switch" or "--switch":

--switch1 value1 --switch2 "c:\folder 2\file2.txt" -switch-3 value-3 --switch4 -switch5

 the regex will extract the flags along with any optional values and return a list of key-value pairs.

I had looked at a couple of solutions such as http://www.codeproject.com/Articles/3111/C-NET-Command-Line-Arguments-Parser and http://lukasz-lademann.blogspot.co.uk/2013/01/c-command-line-arguments-parser.html but I think this way is a bit simpler and returns better results.

Also, this way allows optional parameters which can contain file paths, you can test here http://regexhero.net/tester/?id=2f4e252d-c89e-4134-8c7c-65c5186b3338