How to pass in Uri complex objects without using custom ModelBinders or any serialization?

Do you wonder why I publish such an article?
Well, recently it happened to me to ask to a legacy information system in which I was to find information of users only with their name and their first name…
Yes yes using their name first name:).

I had to send to the WebApi a list of name/first name, the whole paginated, how did I make it?

At the beginning I had the idea to create my search model object in JSON which I would have to serialize while passing it in the URL, then, I had the idea to create an object with a string property containing the list of the names/firstnames the whole “decorated with separators” then split it in the WebAPI with a custom ModelBinder. Finally I decided I’d rather understand how to pass in the Uri more or less complex objects without using of ModelBinder or serialisation?.

Here what I did:

I tested 2 scenarios, the first with a list of “Person” and a second with a little more complex object “ListOfPersons”.
Before starting, as a reminder, I just want to remind you how we pass in a Uri a string collection.

namespace QueryStringDemo.Models
{
    public class Person
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }
    }
}
namespace QueryStringDemo.Models
{
    public class ListOfPersons
    {
        public int? PageNumber { get; set; } 
        public int? PageSize { get; set; }
        public List Persons { get; set; }
    }
}
using QueryStringDemo.Models;
using System.Collections.Generic;
using System.Web.Http;

namespace QueryStringDemo.Controllers
{
    [RoutePrefix("api/demo")]
    public class DemoController : ApiController
    {
        [Route("simplelist")]
        [HttpGet]
        public IHttpActionResult Get([FromUri]List strings)
        {
            return Ok();
        }

        [Route("simplelistOfpersons")]
        [HttpGet]
        public IHttpActionResult Get([FromUri]List listOfPersons)
        {
            return Ok();
        }

        [Route("complexlistOfpersons")]
        [HttpGet]
        public IHttpActionResult Get([FromUri]ListOfPersons listOfPersons)
        {
            return Ok();
        }
    }
}

1- Scenario 1, reminder, string collection :

Uri :

scenario1-url

Result in code :

scenario1-code

Note that you have to use the attribute [FromUri], it provides deserialization to the target object from a complex object that comes from the queryString.

2- Scenario 2, collection of person object :

Uri :

scenario2-url

Result in code :

scenario2-code-1

Note that the object is in fact an array, an array like this

?[index].PropertyName=Value

3- Scenario 3, a more complex collection of person object :

Uri :

scenario3-url

Result in code :

scenario3-code

Note that it works also with an array,
our object looks like this :

?PropertyName1=value&PropertyName2=value&PropertyName3[index].SubPropertyName1=value&PropertyName3[index].SubPropertyName2=value

Finally we have demystified how queryString native serialization/deserialization works, was it so complicated ? 🙂

Entity Framework Core 2 – Global filters on queries

 

Entity Framework Core 2 was released on August 14th. It brought new features.

On this article I will explain one of them : Global filters on queries

They are useful in essentially two scenarios:

  • For multitenant apps
  • For soft deletes

But they are useful for many other scenarios, like in a product list query, we want also to display anytime only products that are not free: (cost > 0)

public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
   public TenantId {get; set;}
   public void Configure(EntityTypeBuilder<Product> builder)
   {
      builder.HasKey(x => x.ProductID);
      builder.HasQueryFilter(o => o.Cost > 0); // non free products
      builder.HasQueryFilter(o => o.TenantId > this.TenantId ); // manage tenants products
      builder.HasQueryFilter(o => !o.IsDeleted ); // manage non deleted products with soft delete
      builder.ToTable("Product");
   }
}

It’s possible to ignore this filters on some queries using IgnoreQueryFilters() extension method:

public IQueryable<Product> GetTopProducts()
{
   return _context.Products
   .IgnoreQueryFilters()
   .Take(50);
}

Nice isn’it ? 🙂

Angular 5, what’s new?

After a release in March 2017 of Angular 4, it is already time to welcome Angular 5 pending Angular 6 scheduled for March 2018. For those who do not know, Angular is the name of a tool set to provided by Google that serves to give happiness to javascript application developers. Happiness because the tool is complete, the community is large and it allows us to focus with pleasure on customer issues.

Among all the changes, we invite you to discover the most significant ones.

Build optimization

As of version 5.0.0, production builds created with Angular CLI will apply the default optimization.

The optimization tool included in Angular CLI has two main objectives. The first is to improve the tree shaking (removing parts of the application that are not needed). The second is to remove the Angular decorators that only the compiler needs.

Each of these actions reduces the size of the build and speeds up the startup of applications.

Angular Universal State Transfer API and DOM Support

Small reminder, Angular Universal allows to have a server-side pre-rendering Angular applications, which allows to take into account the search engines that do not manage javascript.

With Angular 5.0.0, it’s easier to share data between the server version and the client version. This eliminates the need for a duplicate HTTP call to retrieve data when the client version regains control.

Improved transpiler

Improvement of the incremental build, which makes it possible to accelerate the rebuilds notably for the production builds and those with AOT (Ahead-of-Time compilation).

Ability to remove the whitespaces, which until then were kept in templates. It is possible to configure it either at the level of the decorator of each component or at the global level via the file tsconfig.json. The default behavior is “true”, but it is envisaged that in the future it will be “false”.

At the component level :

@Component(
{ 
    templateUrl: 'about.component.html', 
    preserveWhitespaces: false 
} 
export class AboutComponent {}

In the tsconfig.json file :

{ 
   "extends": "../tsconfig.json", 
   "compilerOptions": { 
   "outDir": "../out-tsc/app", 
   "baseUrl": "./", 
   "module": "es2015", 
   "types": [] 
   }, 
   "angularCompilerOptions": { "preserveWhitespaces": false }, 
   "exclude": [ "test.ts", "**/*.spec.ts" ] 
}

Replacing the ReflectiveInjector with the StaticInjector

Again, this eliminates even more polyfills, reducing the size of applications for most developers.

Before :

ReflectiveInjector.resolveAndCreate(providers);

After :

Injector.create(providers);

Improved Zone Speed

Zone is faster by default and it is now possible to bypass zone completely for applications that need performance.

To bypass zone, here is the procedure to follow :

platformBrowserDynamic().bootstrapModule(AppModule, {ngZone: 'noop'}).then( ref => {} );

exportAs

We can now give several names to our components and directives, which is very convenient in case of renaming, it avoids breaking the existing code.

This possibility has been used in the case of prefix change of Angular Material.

Exemple :

@Component(
{ 
   moduleId: module.id, 
   selector: 'a[mat-button], a[mat-raised-button], a[mat-icon-button], a[mat-fab], a[mat-mini-fab]', 
   exportAs: 'matButton, matAnchor', . . . 
}

HttpClient

The old @angular/http module is now officially deprecated and replaced by @angular/common/http, the new HttpClient introduced in 4.3. You can probably expect that @angular/http will be removed in Angular 6.0.

HttpClient has been slightly improved with Angular 5.0, as we are now able to directly use object literals as headers or parameters, whereas we had to use the classes HttpHeaders and HttpParams.

Exemple :

const headers = new HttpHeaders().set('Authorization', 'secret');
const params = new HttpParams().set('page', '1');
return this.http.get('/api/users', { headers, params });

simplified into :

const headers = { 'Authorization': 'secret' };
const params = { 'page': '1' };
return this.http.get('/api/users', { headers, params });

Forms

Forms have a tiny but really useful addition to their API: the ability to decide when the validity and value of a field or form is updated. This is something we already had in AngularJS 1.x, but not yet in Angular.

To do so, the FormControl allows to use an options object as the second parameter, to define the synchronous and asynchronous validators, and also the updateOn option. Its value can be:

  • change, it’s the default: the value and validity are updated on every change;
  • blur, the value and validity are then updated only when the field lose the focus;
  • submit, the value and validity are then updated only when the parent form is submitted.

Exemples :

this.passwordCtrl = new FormControl('', {
 validators: Validators.required,
 updateOn: 'blur'
});
<input [(ngModel)]="user.login" [ngModelOptions]="{ updateOn: 'blur' }">
<form [ngFormOptions]="{ updateOn: 'submit' }">

RxJS 5.5

improved import

Before :

import 'rxjs/add/operator/map'; 
import 'rxjs/add/operator/filter';

After :

import { map, filter } from 'rxjs/operators';

New events for the Router

  • ChildActivationStart
  • ChildActivationEnd

These new events can be used to control the display of a spinner or to measure the performance of a resolver.

Here’s an example of how to start and stop a spinner :

class MyComponent { 
   constructor(public router: Router, spinner: Spinner) { 
      router.events.subscribe(e => { 
         if (e instanceof ChildActivationStart) { 
            spinner.start(e.route); 
         } else if (e instanceof ChildActivationEnd) { 
           spinner.end(e.route); 
         }  
       ); 
     } 
}

I18n

The messages extracted from your application now include the interpolations used in the template.

Before :

<source>
 Welcome to Ponyracer
 <x id="INTERPOLATION"/>
 <x id="INTERPOLATION_1"/>!
</source>

After :

<source>
 Welcome to Ponyracer
 <x id="INTERPOLATION" equiv-text="{{ user.firstName }}"/>
 <x id="INTERPOLATION_1" equiv-text="{{ user.lastName }}"/>!
</source>

A notable change in i18n is that the i18n comments are now deprecated. In Angular 4, you could use:

<!--i18n: @@home.justText -->
 I don't output an element, just text
<!--/i18n-->

Starting with Angular 5, you are encouraged to use an already possible alternative with ng-container:

<ng-container i18n="@@home.justText">
 I don't output an element, just text
</ng-container>

Other breaking changes

Using a different locale than the default one (en-US) now requires to load additional locale data:

import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';

registerLocaleData(localeFr);

All the i18n pipes now take a locale as their last parameter, allowing to dynamically override it:

@Component({
 selector: 'ns-locale',
 template: `
 <p>The locale is {{ locale }}</p>
 <!-- will display 'en-US' -->

<p>{{ 1234.56 | number:'1.0-3':'fr-FR' }}</p>
 <!-- will display '1 234,56' --> 
 `
})
class DefaultLocaleComponentOverridden {
 constructor(@Inject(LOCALE_ID) public locale: string) { }
}

The currency pipe now takes a string as it second parameter, allowing to chose between ‘symbol’ (default), ‘symbol-narrow’ or ‘code’. For example, with canadian dollars:

<p>{{ 10.6 | currency:'CAD' }}</p>
<!-- will display 'CA$10.60' -->

<p>{{ 10.6 | currency:'CAD':'symbol-narrow' }}</p>
<!-- will display '$10.60' -->

<p>{{ 10.6 | currency:'CAD':'code':'.3' }}</p>
<!-- will display 'CAD10.600' -->

if you want to keep the “old” pipes for now, as they have been kept in a new module DeprecatedI18NPipesModule, that you can import if you want to still use them:

@NgModule({
 imports: [CommonModule, DeprecatedI18NPipesModule],
 // ...
})
export class AppModule {

If you have something like this :

platformBrowserDynamic([
 MyCustomProviderA,
 MyCustomProviderB // depends on MyCustomProviderA
]).bootstrapModule(AppModule);

It must now be in 5.0 :

platformBrowserDynamic([
 { provide: MyCustomProviderA, deps: [] },
 { provide: MyCustomProviderB, deps: [MyCustomProviderA] }
]).bootstrapModule(AppModule);

That’s it 🙂