Wednesday, August 31, 2016

Visual C# Technical Guru - July 2016

Another month as a judge in Microsoft TechNet Guru Awards under Visual C# category. The TechNet Guru Awards celebrate the technical articles on Microsoft TechNet.

Post in WikiNinjas Official Blog,
image
Visual C# Technical Guru - July 2016
Happy Coding.

Regards,
Jaliya

Thursday, August 25, 2016

Entity Framework : Deleting an Item from a Collection

In this post let's see how we can remove/delete an item from a collection in terms of Entity Framework.

It's always better to go with an example, consider the following scenario.

I have an entity, Department which has Contacts.
public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Contact> Contacts { get; set; }
}
 
public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Department Department { get; set; }
}
I have my DbContext as follows.
public class MyDbContext : DbContext
{
    public DbSet<Department> Departments { get; set; }
}
Here you can see that I have exposed only Departments as a DbSet, because I will only be accessing Contacts through their Department.

Now I have my DbContext initializer which seeds some data. Please note that I am inheriting from DropCreateDatabaseAlways.
public class MyDbContextInitializer : DropCreateDatabaseAlways<MyDbContext>
{
    protected override void Seed(MyDbContext context)
    {
        context.Departments.AddOrUpdate(d => d.Name, new Department()
        {
            Name = "Microsoft Visual Studio",
            Contacts = new List<Contact>()
                {
                    new Contact() { Name = "Jaliya Udagedara" },
                    new Contact() { Name = "John Smith" }
                }
        });
 
        context.SaveChanges();
 
        Department department = context.Departments.FirstOrDefault();
        Contact contact = department.Contacts.FirstOrDefault();
        department.Contacts.Remove(contact);
 
        context.SaveChanges();
    }
}
Here you can see that after adding a Department with some set of Contacts, I am removing a Contact.

Now from the Main method, let's try to see who are the Contacts for my Department (here please note that I am not following best practices (null handling etc.), as this is only for demonstration purposes).
static void Main(string[] args)
{
    Database.SetInitializer(new MyDbContextInitializer());
 
    using (MyDbContext context = new MyDbContext())
    {
        foreach (Contact contact in context.Departments.FirstOrDefault().Contacts)
        {
            Console.WriteLine(contact.Name);
        }
    }
}
Output would be as follows.
image
Contacts of Department
It’s just like we expected, initially I have added two Contacts and later I have removed one. So now there is only one Contact in the Department.

Out of curiosity, let’s check the Contacts table in the Database to see whether the Contact is deleted from the back end as well.
image
Contacts in the table
Here you can see that the Contact is still there, only the relationship has been deleted. So this approach will cause orphan records.

And this was even possible because the Department_Id foreign key allowed null. Now let’s modify the code so that the Contact should always belong to a Department (actually that’s why I initially exposed only Departments as a DbSet in MyDbContext).

I can easily do that change by modifying the Contact class as follows.
public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Department_Id { get; set; }
    [ForeignKey("Department_Id")]
    public virtual Department Department { get; set; }
}
Now let’s run the application.

This time I am thrown with an error.
image
Error
The error is, System.InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

This is a very obvious error. That is because when we are removing a Contact from the Department, as you just saw, only the relationship is getting deleted which causes Department_Id of Contact to be null. And we are no longer allowing nulls there.

Now let’s see how we can get this fixed. That is deleting both the relationship and the Contact.

We have two options.
1. Update the state of the entity we want to remove to Deleted
Department department = context.Departments.FirstOrDefault();
Contact contact = department.Contacts.FirstOrDefault();
 
// Marking entity state as deleted
context.Entry(contact).State = EntityState.Deleted;
 
context.SaveChanges();
Here we don't want to remove the Contact from the Department, we can directly update the state of the entity to Deleted. So upon context.SaveChanges(), Contact will get deleted.

2. ObjectContext.DeleteObject
Department department = context.Departments.FirstOrDefault();
Contact contact = department.Contacts.FirstOrDefault();
 
// ObjectContext.DeleteObject
((IObjectContextAdapter)context).ObjectContext.DeleteObject(contact); 

context.SaveChanges();
Here also we can skip department.Contacts.Remove(contact), we can directly call DeleteObject(). Again upon context.SaveChanges(), Contact will get deleted.

Using any of these approaches, we can remove/delete a Contact from the Department. And most importantly that's without exposing Contacts as DbSet in MyDbContext.
image
Contacts of Department
image
Contacts in the table
I am uploading the full sample code to OneDrive.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Monday, August 15, 2016

Angular 2 with TypeScript on top of ASP.NET Core using Visual Studio 2015

In this post let’s see how we can setup a very basic Angular 2 application with TypeScript on top of ASP.NET Core using Visual Studio 2015.

This post is totally based on the official sample used in 5 MIN QUICKSTART using Angular 2 for TypeScript which is available in following URL.
I will not be going into deeper descriptions here because above mentioned article almost explains everything. I will be adopting the code used in the same article and will be doing modifications on the go to match my requirements.

Without further ado, let’s start by firing up Visual Studio 2015 and creating an ASP.NET Core Web Application. Please note that I have installed Node.js and npm.
image
ASP.NET Core Web Application
image
Empty Template
Now open up the startup.cs and modify the code as follows.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
 
namespace AspNetCoreAngular2TypeScriptDemo
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }
 
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();
 
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
 
            app.UseDefaultFiles();
            app.UseStaticFiles();
        }
    }
}
You may need to install "Microsoft.AspNetCore.StaticFiles" dependency and add necessary usings. Here I have specified app.UseDefaultFiles() to server a default file and app.UseStaticFiles() to enable static file serving.

Right click on the project and add a new JSON file named “typings.json”.

typings.json
{
  "globalDependencies": {
    "core-js": "registry:dt/core-js#0.0.0+20160602141332",
    "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
    "node": "registry:dt/node#6.0.0+20160807145350"
  }
}
A file named typings.json is needed to install typings, otherwise npm will throw a warning saying no typings.json is found.

Right click on the project and add npm Configuration File.
image
npm Configuration File
package.json
{
  "version": "1.0.0",
  "name": "AspNetCoreAngular2TypeScriptDemo",
  "scripts": {
    "postinstall": "typings install",
    "typings": "typings"
  },
  "private": true,
  "dependencies": {
    "@angular/common": "2.0.0-rc.5",
    "@angular/compiler": "2.0.0-rc.5",
    "@angular/core": "2.0.0-rc.5",
    "@angular/forms": "0.3.0",
    "@angular/http": "2.0.0-rc.5",
    "@angular/platform-browser": "2.0.0-rc.5",
    "@angular/platform-browser-dynamic": "2.0.0-rc.5",
    "@angular/router": "3.0.0-rc.1",
    "@angular/router-deprecated": "2.0.0-rc.2",
    "@angular/upgrade": "2.0.0-rc.5",
    "systemjs": "0.19.36",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.8",
    "rxjs": "5.0.0-beta.6",
    "zone.js": "^0.6.12",
    "bootstrap": "^3.3.7"
  },
  "devDependencies": {
    "gulp": "^3.9.1",
    "del": "^2.2.1"
  }
}
Here I did some modifications to make it simpler. You can see that in postinstall (after all the packages are installed), I have set to run typings install command. Now when you restore/install npm packages, you can see that upon all the packages are completed installing, npm is running typings install. It will create a folder named typings which will contain TypeScript definition files. To see the npm progress, open up output window and Select output from Bower/npm.

Right click on the project and add TypeScript JSON Configuration File.
image
TypeScript JSON Configuration File
tsconfig.json
{
  "compilerOptions": {
    "noImplicitAny": true,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "es5",
    "experimentalDecorators": true
  },
  "compileOnSave": true
}
I have compileOnSave enabled, so every time I save my TypeScript files, TypeScript to JavaScript compilation will be done.

Right click on the project and add Gulp Configuration File.
image
Gulp Configuration File
gulpfile.js
var gulp = require('gulp');
var del = require('del');
 
var paths = {
    nodeModules: 'node_modules/**/*.js'
};
 
gulp.task('clean', function () {
    return del(['wwwroot/scripts/**/*']);
});
 
gulp.task('build', function () {
    gulp.src(paths.nodeModules).pipe(gulp.dest('wwwroot/scripts'))
});
I have 2 tasks, build task to copy all the node_modules to a folder named scripts inside wwwroot and clean task to delete all content inside scripts folder. This is just for the demonstration, may be you can have a task to clean up JavaScript files which gets created upon compiling TypeScript files.

Run the build task by right clicking on gulpfile.js -> Task Runner Explorer.
image
Task Runner Explorer
Upon task completion, you can see a new folder named scripts created under wwwroot. You can see all your node_modules copied into scripts folder.

Create a folder inside wwwroot named app and create following three TypeScript files, app.component.ts, app.module.ts and main.ts  over there.

app.component.ts
import { Component } from '@angular/core';
@Component({
    selector: 'my-app',
    template: '<h1>Welcome to Angular 2 with TypeScript</h1>'
})
export class AppComponent { }
app.module.ts
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
 
import { AppComponent }  from './app.component';
 
@NgModule({
    imports: [BrowserModule],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }
main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
As in the original example, we are using systemjs to dynamically load modules. Add a JavaScript file named systemjs.config.js inside wwwroot folder.
image
systemjs.config.js
systemjs.config.js
(function (global) {
    // map tells the System loader where to look for things
    var map = {
        'app': 'app', // 'dist',
        '@angular': 'scripts/@angular',
        'rxjs': 'scripts/rxjs'
    };
    // packages tells the System loader how to load when no filename and/or no extension
    var packages = {
        'app': { main: 'main.js', defaultExtension: 'js' },
        'rxjs': { defaultExtension: 'js' }
    };
    var ngPackageNames = [
      'common',
      'compiler',
      'core',
      'forms',
      'http',
      'platform-browser',
      'platform-browser-dynamic',
      'router',
      'router-deprecated',
      'upgrade',
    ];
    // Individual files (~300 requests):
    function packIndex(pkgName) {
        packages['@angular/' + pkgName] = { main: 'index.js', defaultExtension: 'js' };
    }
    // Bundled (~40 requests):
    function packUmd(pkgName) {
        packages['@angular/' + pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
    }
    // Most environments should use UMD; some (Karma) need the individual index files
    var setPackageConfig = System.packageWithIndex ? packIndex : packUmd;
    // Add package entries for angular packages
    ngPackageNames.forEach(setPackageConfig);
    var config = {
        map: map,
        packages: packages
    };
    System.config(config);
})(this);
Add index.html file inside wwwroot folder.

index.html
<html>
<head>
    <title>AspNetCoreAngular2TypeScriptDemo</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
 
    <!-- 1. Load libraries -->
    <!-- Polyfill(s) for older browsers -->
 
    <script src="scripts/core-js/client/shim.min.js"></script>
    <script src="scripts/zone.js/dist/zone.js"></script>
    <script src="scripts/reflect-metadata/Reflect.js"></script>
    <script src="scripts/systemjs/dist/system.src.js"></script>
 
    <!-- 2. Configure SystemJS -->
    <script src="systemjs.config.js"></script>
    <script>
        System.import('app').catch(function(err){ console.error(err); });
    </script>
</head>
 
<!-- 3. Display the application -->
<body>
    <my-app>Loading...</my-app>
</body>
</html>

Now we are all good. Compile and run the project.
image
Output
It's working.

I have pushed the project into a GitHub repository and please do play around.

Happy Coding.

Regards,
Jaliya