Mocking Entity Framework DbContext with Moq

When we have to test methods that involves Entity Framework, a typical choice that we have to face is use integration tests, with an effective database, or unit tests.

If we choice the first option, with a database like SQL LocalDB, we’ll have performance problems because the cost of the database creation and the data inserts in the test startup is very high, and in order to guarantee the initial conditions we’ll have to do it for each test.

What we can do is use a mock framework that help us to mockup the entity framework context; it would be an in-memory db context, like the in-memory db context of .NET Core, that we have seen in this post.

The factory

In pratice, mocking a class means substitute the real implementation of a method with our custom behaviour; what we can do for every method of the class is setup returns values of the method; therefore we don’t need the real implementation of the class, we have mocked the methods.

In our case, we can setup the EF DbSet with an in-memory list and the methods that use the context will no longer need the real database, they will use our lists provided with the mock.

Anyway, before to do it we have another problem; how we can provide our in-memory db context to the methods that we need to test?

In the real world, it’s likely that the context will be instantiated with the using statement in every method, like this:


public async Task<List<Person>> GetPersons(string query)
{
using (var db = new Context())
{
.....
}
}

This is a problem, because we are not able to inject our mocked context in the class.

But we can solve it with a Factory service, a singleton service that returns instances of the db context:


public class ContextFactory
{
private Type _dbContextType;
private DbContext _dbContext;

public void Register<TDbContext>(TDbContext dbContext) where TDbContext : DbContext, new()
{
_dbContextType = typeof(TDbContext);
_dbContext = dbContext;
}

public TDbContext Get<TDbContext>() where TDbContext : DbContext, new()
{
if (_dbContext == null || _dbContextType != typeof(TDbContext))
{
return new TDbContext();
}

return (TDbContext)_dbContext;
}
}

We have two methods, with the Register method we can setup a specific db context implementation; with the Get method we can get an instance of a db context, that is the registered implementation if we have one, otherwise the default implementation.

We can now inject this service as a dependency and use it:


public class PersonService
{
private readonly ContextFactory _contextFactory;

public PersonService(ContextFactory contextFactory)
{
_contextFactory = contextFactory;
}

public async Task<List<Person>> GetPersons(string query)
{
using (var db = _contextFactory.Get<Context>())
{
return await db.Persons.Where(p => p.TaxCode.Contains(query) || p.Firstname.Contains(query) || p.Surname.Contains(query)).ToListAsync();
}
}
}

Now we are ready to mock the EF context.

The mock

The framework that I use for this purphose is moq and I can install it with nuget:

install-package moq

It’s likely that you use async methods of entity framework; if yes, in order to mock we need to create an in-memory DbAsyncQueryProvider, and you can find the implementation here.

The Unit Testing used for this example is NUnit and I can configure the mocked context in the setup method; the first step is prepare a list of queryable objects:


[SetUp]
public void Setup()
{
var persons = new List<Person>() {
new Person() { TaxCode = "taxcode1", Firstname = "firstname1", Surname = "surname1" },
new Person() { TaxCode = "taxcode2", Firstname = "firstname2", Surname = "surname2" }
};
var queryable = persons.AsQueryable();
}

Now I’m ready to setup the mock:


MockSet = new Mock<DbSet<Person>>();

MockSet.As<IQueryable<Person>>().Setup(m => m.Expression).Returns(queryable.Expression);
MockSet.As<IQueryable<Person>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
MockSet.As<IQueryable<Person>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator);

MockSet.As<IQueryable<Person>>().Setup(m => m.Provider).Returns(new AsyncQueryProvider<Person>(queryable.Provider));
MockSet.As<IDbAsyncEnumerable<Person>>().Setup(m => m.GetAsyncEnumerator()).Returns(new AsyncEnumerator<Person>(queryable.GetEnumerator()));

In order to mock an IQueryable, I have to setup returns values for Expression method, ElementType and GetEnumerator; every time these methods will be invoked in the queries executions, the values that I setup in the Returns expression will be returned.

I need to do the same operations for Provider method and GetAsyncEnumerator, but, since async methods are involved, I need to use the custom classes AsyncQueryProvider and AsyncEnumerator of the in-memory DbAsyncQueryProvider.

The mock for the Add and Remove operations are simplier:


MockSet.Setup(m => m.Add(It.IsAny<Person>())).Callback((Person person) => persons.Add(person));
MockSet.Setup(m => m.Remove(It.IsAny<Person>())).Callback((Person person) => persons.Remove(person));

Since Add and Remove methods returns nothing, we use Callback methods instead of Returns.

The last step is setup the factory service context with the mocked version:


MockContext = new Mock<Context>();
MockContext.Setup(m => m.Persons).Returns(MockSet.Object);

var contextFactory = new ContextFactory();
contextFactory.Register(MockContext.Object);
PersonService = new PersonService(contextFactory);

First of all I setup the DbSet of the mocked context with the mocked DbSet.

Then I register the mocked context in the factory service and then I pass the factory service as a dependency of the service to be test.

With these lines of code I have mocked the entity framework context with an in-memory instance and leveraging the context factory I was able to inject the mocked context to the service.

You can find the source code here.

 

Advertisements
Mocking Entity Framework DbContext with Moq

Manage tables data with AngularJS Part 3: configuring the fields typologies

The last part of this argument is configuring the field typologies of the tables to manage.

What you need to do is specify for one or more fields of the table a couple of informations, like the typology of the field (text, number, radio, dropdown) and perhaps a list of values.

Also we might want to pass to the field external values instead of predefined values.

In order to do that and to improve the functionalities of the application, you need to implement some new features.

FieldConfiguration table

The first step is adding a new field configuration table:

[Table("FieldConfigurations")]
public class FieldConfiguration
{
public Guid Id { get; set; }
[Required]
public string Entity { get; set; }
[Required]
public string Field { get; set; }
[Required]
public string Tipology { get; set; }
public string Values { get; set; }
}

With this table we can specify for a field the tipology (text, number, radio, dropdown) and an optional list of values.

We need also update the database and implement a Web API to retrieve the datas from the new table:

public class FieldConfigurationsController : ODataController
{
private Context db = new Context();

// GET: odata/FieldConfigurations
[EnableQuery]
public IQueryable<FieldConfiguration> GetFieldConfigurations()
{
return db.FieldConfigurations;
}

// GET: odata/FieldConfigurations(5)
[EnableQuery]
public SingleResult<FieldConfiguration> GetFieldConfiguration([FromODataUri] Guid key)
{
return SingleResult.Create(db.FieldConfigurations.Where(fieldConfiguration => fieldConfiguration.Id == key));
}

// PUT: odata/FieldConfigurations(5)
public async Task<IHttpActionResult> Put([FromODataUri] Guid key, Delta<FieldConfiguration> patch)
{
Validate(patch.GetEntity());

if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

FieldConfiguration fieldConfiguration = await db.FieldConfigurations.FindAsync(key);
if (fieldConfiguration == null)
{
return NotFound();
}

patch.Put(fieldConfiguration);

try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!FieldConfigurationExists(key))
{
return NotFound();
}
else
{
throw;
}
}

return Updated(fieldConfiguration);
}

// POST: odata/FieldConfigurations
public async Task<IHttpActionResult> Post(FieldConfiguration fieldConfiguration)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

fieldConfiguration.Id = Guid.NewGuid();
db.FieldConfigurations.Add(fieldConfiguration);

try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (FieldConfigurationExists(fieldConfiguration.Id))
{
return Conflict();
}
else
{
throw;
}
}

return Created(fieldConfiguration);
}

// PATCH: odata/FieldConfigurations(5)
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] Guid key, Delta<FieldConfiguration> patch)
{
Validate(patch.GetEntity());

if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

FieldConfiguration fieldConfiguration = await db.FieldConfigurations.FindAsync(key);
if (fieldConfiguration == null)
{
return NotFound();
}

patch.Patch(fieldConfiguration);

try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!FieldConfigurationExists(key))
{
return NotFound();
}
else
{
throw;
}
}

return Updated(fieldConfiguration);
}

// DELETE: odata/FieldConfigurations(5)
public async Task<IHttpActionResult> Delete([FromODataUri] Guid key)
{
FieldConfiguration fieldConfiguration = await db.FieldConfigurations.FindAsync(key);
if (fieldConfiguration == null)
{
return NotFound();
}

db.FieldConfigurations.Remove(fieldConfiguration);
await db.SaveChangesAsync();

return StatusCode(HttpStatusCode.NoContent);
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}

private bool FieldConfigurationExists(Guid key)
{
return db.FieldConfigurations.Count(e => e.Id == key) > 0;
}
}

For this implementation only reading operations are needed, but still leave the other methods.

FieldConfigurations service

We need an Angular service to consume the Web API and we use the Angular $resource module.

In addition to the CRUD methods we implement additional methods like getField, getFieldConfigurationTipology and getFieldValues, that will help to retrieve specific informations about a field.

module AngularTablesDataManagerApp.Services {
import ngr = ng.resource;
import commons = AngularTablesDataManagerApp.Commons;
import models = AngularTablesDataManagerApp.Models;
import services = AngularTablesDataManagerApp.Services;

export interface IFieldConfigurationsResourceClass extends ngr.IResourceClass<ngr.IResource<models.IFieldConfiguration>> {
create(zip: models.IFieldConfiguration): ngr.IResource<models.IFieldConfiguration>;
}

export class FieldConfigurationsService {
private resource: IFieldConfigurationsResourceClass;
private $q: ng.IQService;
private $filter: ng.IFilterService;
private metadataService: services.MetadataService;
private entitySet: string = 'FieldConfigurations';
private fieldConfigurations: Array<models.IFieldConfiguration>;

constructor($resource: ngr.IResourceService, $q: ng.IQService, $filter: ng.IFilterService, metadataService: services.MetadataService) {
this.$q = $q;
this.$filter = $filter;
this.metadataService = metadataService;

this.resource = <IFieldConfigurationsResourceClass>$resource('/odata/' + this.entitySet + "(guid':key')", { key: '@Id' }, {
get: { method: 'GET' },
create: { method: 'POST', isArray: false, url: '/odata/' + this.entitySet },
save: { method: 'PUT' },
query: { method: 'GET', isArray: false, url: '/odata/' + this.entitySet },
delete: { method: 'DELETE' }
});
}

public create(fieldConfiguration: models.IFieldConfiguration) {
return this.resource.create(fieldConfiguration);
}

public save(fieldConfiguration: models.IFieldConfiguration) {
if (fieldConfiguration.Id == commons.Constants.GuidEmpty) {
return this.resource.create(fieldConfiguration);
}
else {
return this.resource.save(fieldConfiguration);
}
}

public delete(fieldConfiguration: models.IFieldConfiguration) {
return this.resource.delete({ key: fieldConfiguration.Id });
}

public getFieldConfigurationTipology(entityName: string, fieldName: string): ng.IPromise<string> {
var defer: ng.IDeferred<string> = this.$q.defer();

this.getFieldConfiguration(entityName, fieldName).then((data: models.IFieldConfiguration) => {
if (data != null) {
defer.resolve(data.Tipology);
}
else {
defer.resolve('');
}
}, (error) => {
defer.reject(error);
});

return defer.promise;
}

public getFieldValues(entityName: string, fieldName: string): ng.IPromise<Array<string>> {
var defer: ng.IDeferred<Array<string>> = this.$q.defer();
var vm = this;

this.getFieldConfiguration(entityName, fieldName).then((data: models.IFieldConfiguration) => {
if (data != null && data.Values != null && data.Values != "") {
defer.resolve(data.Values.split(";"));
}
else {
defer.resolve(new Array<string>());
}

}, (error) => {
defer.reject(error);
});

if (this.fieldConfigurations) {

}
else {

}

return defer.promise;
}

private getFieldConfiguration(entityName: string, fieldName: string): ng.IPromise<models.IFieldConfiguration> {
var defer: ng.IDeferred<models.IFieldConfiguration> = this.$q.defer();
var vm = this;

if (this.fieldConfigurations == null) {
vm.resource.query().$promise.then((data: any) => {
vm.fieldConfigurations = data["value"];
defer.resolve(vm.getField(entityName, fieldName));
}, (error) => {
defer.reject(error);
});
}
else {
defer.resolve(vm.getField(entityName, fieldName));
}

return defer.promise;
}

private getField(entityName: string, fieldName: string): models.IFieldConfiguration {
var fieldConfigurations: Array<models.IFieldConfiguration> = this.$filter('filter')(this.fieldConfigurations, { 'Entity': entityName, 'Field': fieldName }, true);
if (fieldConfigurations.length > 0) {
return fieldConfigurations[0];
}
else {
return null;
}
}

static factory() {
return (r: ngr.IResourceService, $q: ng.IQService, $filter: ng.IFilterService, MetadataService: services.MetadataService) => new FieldConfigurationsService(r, $q, $filter, MetadataService);
}
}

AngularTablesDataManager.module.factory('FieldConfigurationsService', ['$resource', '$q', '$filter', 'MetadataService', FieldConfigurationsService.factory()]);
}

The getFieldConfiguration method check if the configurations aren’t already loaded and if not invoke the Web API.

Field directive

Now you can implement the directive that will manage the fields visualization.

The directive accept some parameters, like the tipology of the field, the property object that you want to show and the possible values of the field:

module AngularTablesDataManagerApp.Directives {
import models = AngularTablesDataManagerApp.Models;
import services = AngularTablesDataManagerApp.Services;

interface IFieldDirectiveScope extends ng.IScope {
entityName: string;
contentUrl: string;
tipology: models.MetadataProperty;
property: models.RowProperty;
fieldItems: Array<models.FieldItem>;
values: Array<models.FieldItem>;
}

export class FieldDirective implements ng.IDirective {
fieldConfigurationsService: services.FieldConfigurationsService;
public restrict = 'E';
public scope = {
entityName: '=',
tipology: '=',
property: '=',
fieldItems: '='
};
public template = '<ng-include src="contentUrl" />';
public link = (scope: IFieldDirectiveScope, element: JQuery, attrs: IArguments) => {
scope.values = new Array<models.FieldItem>();

if (scope.fieldItems != null) {
for (var i = 0; i < scope.fieldItems.length; i++) {
scope.values.push(scope.fieldItems[i]);
}
}

var tipology: string = scope.tipology.Type;
this.fieldConfigurationsService.getFieldConfigurationTipology(scope.entityName, scope.tipology.Name).then((data: string) => {
if (data != "") {
tipology = data;
this.fieldConfigurationsService.getFieldValues(scope.entityName, scope.tipology.Name).then((data: Array<string>) => {
for (var i = 0; i < data.length; i++) {
var item: models.FieldItem = new models.FieldItem(data[i], data[i]);
scope.values.push(item);
}

this.openView(scope, tipology);
});
}
else {
this.openView(scope, tipology);
}
});
}

public constructor(fieldConfigurationService: services.FieldConfigurationsService) {
this.fieldConfigurationsService = fieldConfigurationService;
}

private openView(scope: IFieldDirectiveScope, tipology: string) {
if (tipology.toLowerCase().indexOf('int') != -1) {
scope.contentUrl = 'app/directives/InputNumber.html';
}
else if (tipology.toLowerCase().indexOf('radio') != -1) {
scope.contentUrl = 'app/directives/InputRadio.html'
}
else if (tipology.toLowerCase().indexOf('select') != -1) {
scope.contentUrl = 'app/directives/InputSelect.html';
}
else {
scope.contentUrl = 'app/directives/InputText.html';
}
}
}

AngularTablesDataManager.module.directive('field', ['FieldConfigurationsService', (fieldConfigurationService: services.FieldConfigurationsService) => new Directives.FieldDirective(fieldConfigurationService)]);
}

In the link function you need to call the service to retrieve the tipology of the field and to check if some values are defined.

An openView method check the field tipology and open the specific view; for example, the InputSelect view will look like this:

<select class="form-control" ng-model="property.Value">
<option ng-repeat="value in values | orderBy: 'Value'" value="{{value.Id}}" ng-selected="value.Id == property.Value">{{value.Value}}</option>
</select>

 

Grid directive

You need to extend the GridController and adding some methods to retrive the necessary informations and pass that to the Field directive:

interface IGridDirectiveScope extends ng.IScope {
entityName: string;
list: models.Grid;
item: models.Row;
rowModel: models.Row;
newItem: boolean;
fieldItems: Array<models.FieldItems>;

New(): void;
Save(item: IGridItem): void;
Delete(item: IGridItem): void;
Close(): void;
GetEntityName(): string;
GetFieldItems(fieldName: string): Array<models.FieldItem>;
IsVisiblePropertyInGrid(property: models.RowProperty): boolean;
IsVisiblePropertyInDetail(fieldName: string): boolean;
}

class GridController {
$scope: IGridDirectiveScope;
$filter: ng.IFilterService;

constructor($scope: IGridDirectiveScope, $filter: ng.IFilterService) {
this.$scope = $scope;
this.$filter = $filter;
}

public Edit(item: models.Row) {
this.$scope.item = item;
this.$scope.newItem = false;
}

public New() {
this.$scope.item = new models.Row(angular.copy(this.$scope.rowModel.Entity), this.$scope.rowModel.Name, angular.copy(this.$scope.rowModel.Properties));
this.$scope.newItem = true;
}

public Save(item: models.Row) {
var obj: IGridItem = { item: item };

this.$scope.Save(obj);
}

public Delete(item: models.Row) {
this.$scope.item = null;
var obj: IGridItem = { item: item };

this.$scope.Delete(obj);
}

public Close() {
if (!this.$scope.newItem) {
for (var i = 0; i < this.$scope.item.Properties.length; i++) {
this.$scope.item.Properties[i].Value = (<any>this.$scope.item.Entity)[this.$scope.item.Properties[i].Name];
}
}

this.$scope.item = null;
}

public GetEntityName() {
return this.$scope.entityName;
}

public GetFieldItems(fieldName: string) {
if (this.$filter('filter')(this.$scope.fieldItems, { 'FieldName': fieldName }, true).length > 0) {
return this.$filter('filter')(this.$scope.fieldItems, { 'FieldName': fieldName }, true)[0].FieldItems;
}
else {
return null;
}
}

public IsVisiblePropertyInGrid(property: models.RowProperty) {
return this.$filter('filter')(this.$scope.list.Columns, { 'Name': property.Name }, true)[0].ShowedInGrid;
}

public IsVisiblePropertyInDetail(property: models.RowProperty) {
return this.$filter('filter')(this.$scope.list.Columns, { 'Name': property.Name }, true)[0].ShowedInDetail;
}
}

A particular aspect is the fieldItems property, that is an array of objects; if you want to pass an array of values to the field directive, what we need to do is load these values in the caller of the directive and pass these as a parameter.

Now you need to change the GridItem view and use the FieldDirective:

<div class="row bootstrap-admin-no-edges-padding" style="margin-top: 10px;">
<div class="col-md-12">
<input type="button" id="btn-save" value="Save" class="btn btn-primary" ng-click="Save()" ng-disabled="form.$invalid" />
<input type="button" id="btn-delete" value="Delete" class="btn btn-danger" ng-click="Delete()" ng-show="!isNew" />
<input type="button" id="btn-close" value="Close" class="btn btn-default" ng-click="Close()" /></div>
</div>
<div class="row bootstrap-admin-no-edges-padding" style="margin-top: 10px;">
<div class="col-md-12">
<form name="form" role="form">
<div class="panel panel-default">
<div class="panel-heading">
<div class="text-muted bootstrap-admin-box-title">
{{item.Name}}</div>
</div>
<div class="panel-body">
<div class="form-group" ng-repeat="property in item.Properties | filter: IsVisibleProperty">
<label for="{{property.Name}}">{{property.Name}}:</label>
<field entity-name="GetEntityName()" tipology="GetMetadataProperty(property.Name)" property="property" field-items="GetFieldItems(property.Name)" /></div>
</div>
<div class="col-md-2"></div>
</div>
</form></div>
</div>

Cities controller

The last step is changing the main controller to retrive the new parameters that the GridDirective needed:

module AngularTablesDataManagerApp.Controllers {
import ngr = ng.resource;
import commons = AngularTablesDataManagerApp.Commons;
import models = AngularTablesDataManagerApp.Models;
import services = AngularTablesDataManagerApp.Services;

export class CitiesController {
entityName: string;
grid: models.Grid;
fieldItems: Array<models.FieldItems>;
rowModel: models.Row;
toaster: ngtoaster.IToasterService;

private citiesService: services.CitiesService;
private zipsService: services.ZipsService;
private constant: commons.Constants;

constructor(toaster: ngtoaster.IToasterService, CitiesService: services.CitiesService, ZipsService: services.ZipsService) {
this.citiesService = CitiesService;
this.zipsService = ZipsService;
this.constant = commons.Constants;
this.toaster = toaster;
this.entityName = this.citiesService.entityName;

this.grid = new models.Grid();
this.fieldItems = new Array<models.FieldItems>();
this.grid.Title = 'Cities';
this.Load();
}

private Load() {
var columns: Array<models.Column> = new Array<models.Column>();
var column: models.Column = new models.Column('Name', true, true);
columns.push(column);
column = new models.Column('IdZip', false, true);
columns.push(column);
var vm = this;

this.zipsService.getAll().then((data) => {
var zips: models.FieldItems = new models.FieldItems('IdZip');

for (var i = 0; i < data.length; i++) {
zips.FieldItems.push(new models.FieldItem(data[i].Id, data[i].Code.toString()));
}

vm.fieldItems.push(zips);

vm.citiesService.getMetadata(columns).then((data) => {
vm.grid.Columns = data;
vm.rowModel = this.citiesService.createGridData(data);

vm.citiesService.getGridData(data).then((data) => {
vm.grid.Rows = data;
vm.toaster.success('Cities loaded successfully.');
return;
}, (error) => {
vm.toaster.error('Error loading cities', error.message);
});

}, (error) => {
vm.toaster.error('Error loading cities metadata', error.data.message);
});

}, (error) => {
vm.toaster.error('Error loading zips metadata', error.data.message);
});

}

public Save(item: models.Row) {
var vm = this;
var isNew: boolean = false;

if (item.Entity.Id == commons.Constants.GuidEmpty)
isNew = true;

this.citiesService.saveGridData(item).then((data: models.Row) => {
if (isNew)
vm.grid.Rows.push(data);

this.toaster.success("City saved successfully.");
}, (error: any) => {
this.toaster.error("Error saving city", error.data.message);
});
}

public Delete(item: models.Row) {
var vm = this;
this.citiesService.deleteGridData(item).then((data: any) => {
var index = vm.grid.Rows.indexOf(item);
vm.grid.Rows.splice(index, 1);

this.toaster.success("City deleted successfully.");
}, (error: any) => {
this.toaster.error("Error deleting city", error.data.message);
});
}
}

AngularTablesDataManager.module.controller('CitiesController', CitiesController);
}

The items to pass to the GridController are an array of FieldItems; an object FieldItems has a FieldName that is the field to witch to associate the values and an array of FieldItem that are the effective values.

Using the ZipService we build an array of FieldItem associated to the IdZip field, and we’re passing that to the GridDirective.

The view is modified like this:

<grid list="vm.grid" entity-name="vm.entityName" row-model="vm.rowModel" order="Name" save="vm.Save(item)" delete="vm.Delete(item)" field-items="vm.fieldItems"></grid>

 

You can find the source code here.

 

 

Manage tables data with AngularJS Part 3: configuring the fields typologies

Manage tables data with AngularJS Part 1: tables metadata

During the deployment of an AngularJS app, we often develop controllers and views to manage data of basic tables,  such as zip, city, country and so on.

We need to offer to the users the CRUD operations of these tables; the functionalities and the structure of these controllers/view are very similar and you need to implement a lot of similar code to do that.

What you can do is implement the AngularJS directives that are able to show the content of basic tables and manage the CRUD operations.

The start point is a simple AngularJS SPA that consume ASP.NET Web API; the application using Entity Framework 6 code first and the only entity is city.

OData controller

Once the table is configured, what we need to do is implement an OData controller that will expose the CRUD operations of the Cities table and the metadata.

For this example we implement and endpoint that support OData Version 3:

public class CitiesController : ODataController
{
private Context db = new Context();

// GET: odata/Cities
[EnableQuery]
public IQueryable<City> GetCities()
{
return db.Cities;
}

// GET: odata/Cities(5)
[EnableQuery]
public SingleResult<City> GetCity([FromODataUri] Guid key)
{
return SingleResult.Create(db.Cities.Where(city => city.Id == key));
}

// PUT: odata/Cities(5)
public async Task<IHttpActionResult> Put([FromODataUri] Guid key, Delta<City> patch)
{
Validate(patch.GetEntity());

if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

City city = await db.Cities.FindAsync(key);
if (city == null)
{
return NotFound();
}

patch.Put(city);

try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CityExists(key))
{
return NotFound();
}
else
{
throw;
}
}

return Updated(city);
}

// POST: odata/Cities
public async Task<IHttpActionResult> Post(City city)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

db.Cities.Add(city);

try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (CityExists(city.Id))
{
return Conflict();
}
else
{
throw;
}
}

return Created(city);
}

// PATCH: odata/Cities(5)
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] Guid key, Delta<City> patch)
{
Validate(patch.GetEntity());

if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

City city = await db.Cities.FindAsync(key);
if (city == null)
{
return NotFound();
}

patch.Patch(city);

try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CityExists(key))
{
return NotFound();
}
else
{
throw;
}
}

return Updated(city);
}

// DELETE: odata/Cities(5)
public async Task<IHttpActionResult> Delete([FromODataUri] Guid key)
{
City city = await db.Cities.FindAsync(key);
if (city == null)
{
return NotFound();
}

db.Cities.Remove(city);
await db.SaveChangesAsync();

return StatusCode(HttpStatusCode.NoContent);
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}

private bool CityExists(Guid key)
{
return db.Cities.Count(e => e.Id == key) > 0;
}
}

Then, you need to configure the OData Endpoint in the WebApiConfig.cs:

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<City>("Cities");
config.Routes.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
}
}

OData controller is helpful to retrieve the metadata and you can using $metadata keyword:

http://host_name/odata/$metadata

We’ll receive a response like this:

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
<edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<Schema Namespace="AngularTablesDataManager.DataLayer" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
<EntityType Name="City">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Guid" Nullable="false" />
<Property Name="Name" Type="Edm.String" />
</EntityType>
<EntityType Name="Zip">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Guid" Nullable="false" />
<Property Name="Code" Type="Edm.Int16" Nullable="false" />
</EntityType>
</Schema>
<Schema Namespace="Default" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
<EntityContainer Name="Container" m:IsDefaultEntityContainer="true">
<EntitySet Name="Cities" EntityType="AngularTablesDataManager.DataLayer.City" />
<EntitySet Name="Zips" EntityType="AngularTablesDataManager.DataLayer.Zip" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

AngularJS service

The next step is the implementation of the AngularJS service that parse the metadata; first of all, a class that define the structure of metadata is required:

export class MetadataProperty {
Name: string;
Type: string;
Nullable: boolean;
}

Then, the MetadataService service:

export class MetadataService {
private $http: ng.IHttpService;
private $q: ng.IQService;

constructor($http: ng.IHttpService, $q: ng.IQService) {
this.$http = $http;
this.$q = $q;
}

public getMetadata(entityName: string): ng.IPromise<Array<models.MetadataProperty>> {
var defer: ng.IDeferred<any> = this.$q.defer();

var req = {
method: 'GET',
url: '/odata/$metadata'
};

this.$http(req).then(function (result) {
var data: string = result.data.toString();
var xmlDoc: XMLDocument = $.parseXML(data);
var xml: JQuery = $(xmlDoc);
var properties: Array<models.MetadataProperty> = new Array<models.MetadataProperty>();

xml.find('EntityType').find('Property').each(function () {
var metadataProperty: models.MetadataProperty = new models.MetadataProperty();
metadataProperty.Name = $(this).attr('Name');
metadataProperty.Type = $(this).attr('Type');
metadataProperty.Nullable = ($(this).attr('Nullable') != null) && ($(this).attr('Nullable').toLowerCase() == 'true');

properties.push(metadataProperty);
});

return defer.resolve(properties);
});

return defer.promise;
}

static factory() {
return ($http: ng.IHttpService, $q: ng.IQService) => new MetadataService($http, $q);
}
}

To consume the OData Controller you can use the $resource factory, that allows to perform CRUD operations easily, and extend the service with an additional method called getMetadata:

const entityName: string = 'Cities';
import ngr = ng.resource;
import commons = AngularTablesDataManagerApp.Commons;
import models = AngularTablesDataManagerApp.Models;
import services = AngularTablesDataManagerApp.Services;

export interface ICitiesResourceClass extends ngr.IResourceClass<ngr.IResource<models.ICity>> {
create(order: models.ICity): ngr.IResource<models.ICity>;
}

export class CitiesService implements models.IService {
private resource: ICitiesResourceClass;
private $q: ng.IQService;
private metadataService: services.MetadataService;

constructor($resource: ngr.IResourceService, $q: ng.IQService, MetadataService: services.MetadataService) {
this.$q = $q;
this.metadataService = MetadataService;

this.resource = <ICitiesResourceClass>$resource('/odata/' + entityName + '/:id', { id: '@Id' }, {
get: { method: "GET" },
create: { method: "POST" },
save: { method: "PUT" },
query: { method: "GET", isArray: false },
delete: { method: "DELETE" }
});
}

public create(order: models.ICity) {
return this.resource.create(order);
}

public save(order: models.ICity) {
if (order.Id == commons.Constants.GuidEmpty) {
return this.resource.create(order);
}
else {
return this.resource.save(order);
}
}

public delete(order: models.ICity) {
return this.resource.remove(order);
}

public getAll() {
var datas: ngr.IResourceArray<ngr.IResource<models.ICity>>
var defer: ng.IDeferred<any> = this.$q.defer();

this.resource.query().$promise.then((data: any) => {
datas = data["value"];

return defer.resolve(datas);
}, (error) => {
return defer.reject(datas);
});

return defer.promise;
}

public getMetadata(): ng.IPromise<Array<models.MetadataProperty>> {
return this.metadataService.getMetadata(entityName);
}

static factory() {
return (r: ngr.IResourceService, $q: ng.IQService, MetadataService: services.MetadataService) => new CitiesService(r, $q, MetadataService);
}
}

This method can be called from a controller; we’ll obtain an array of MetadataProperty objects that represents the structure of the Cities table.

import ngr = ng.resource;
import commons = AngularTablesDataManagerApp.Commons;
import models = AngularTablesDataManagerApp.Models;
import services = AngularTablesDataManagerApp.Services;

export class CitiesController {
cities: ngr.IResourceArray<ngr.IResource<models.ICity>>;
city: models.ICity;
title: string;
toaster: ngtoaster.IToasterService;

private citiesService: services.CitiesService;
private constant: commons.Constants;
private metadataProperties: Array<models.MetadataProperty>;

constructor(toaster: ngtoaster.IToasterService, CitiesService: services.CitiesService) {
this.citiesService = CitiesService;
this.constant = commons.Constants;

this.toaster = toaster;
this.title = 'Cities';

this.Load();
}

private Load() {
this.citiesService.getMetadata().then((data) => {
this.metadataProperties = data;
}, (error) => {
this.toaster.error('Error loading cities medatad', error.data.message);
});

this.citiesService.getAll().then((data) => {
this.cities = data;
this.toaster.success('Cities loaded successfully.');
return;
}, (error) => {
this.toaster.error('Error loading cities', error.message);
});
}
}

AngularTablesDataManager.module.controller('CitiesController', CitiesController);

 

You can find the source code here.

 

Manage tables data with AngularJS Part 1: tables metadata

ASP.NET SignalR and TypeScript in an AngularJS SPA

SignalR is a javascript library for helping developers to add real-time functionalities to web applications.

This example is a Angular SPA developed using Typescript,  to take advantage of Object Oriented features in the client-side code development.

This application has a simple orders list, that every user can manage; every time a user make a change to an order or create a new one, every user window will be automatically refreshed with the new changes.

The application using SQL database, Entity Framework Code First, ASP.NET Web API, Angular 1.5, TypeScript and Bootstrap as front-end framework.

The first steps are server side implementations:

  • Entity Framework entity
  • Web API for CRUD operations
  • SignalR Hub

The EF Order table entity:

[Table("Orders")]
public class Order
{
public Guid Id { get; set; }
public string Article { get; set; }
public decimal Amount { get; set; }
public string Customer { get; set; }
public DateTime CreationDate { get; set; }
public string Notes { get; set; }
}

After creating the context class and performed the migrations commands, you can proceed with the implementation of the Web API Controller; ASP.NET scaffolding is very useful for a standard implementation:

public class OrdersController : ApiController
{
private Context db = new Context();

// GET: api/Orders
public IQueryable<Order> GetOrders()
{
return db.Orders.OrderByDescending(o => o.CreationDate);
}

// GET: api/Orders/5
[ResponseType(typeof(Order))]
public async Task<IHttpActionResult> GetOrder(Guid id)
{
Order order = await db.Orders.FindAsync(id);
if (order == null)
{
return NotFound();
}

return Ok(order);
}

// PUT: api/Orders/5
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> PutOrder(Guid id, Order order)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

if (id != order.Id)
{
return BadRequest();
}

db.Entry(order).State = EntityState.Modified;

try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!OrderExists(id))
{
return NotFound();
}
else
{
throw;
}
}

return StatusCode(HttpStatusCode.NoContent);
}

// POST: api/Orders
[ResponseType(typeof(Order))]
public async Task<IHttpActionResult> PostOrder(Order order)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

order.Id = Guid.NewGuid();
db.Orders.Add(order);

try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (OrderExists(order.Id))
{
return Conflict();
}
else
{
throw;
}
}

return CreatedAtRoute("DefaultApi", new { id = order.Id }, order);
}

// DELETE: api/Orders/5
[ResponseType(typeof(Order))]
public async Task<IHttpActionResult> DeleteOrder(Guid id)
{
Order order = await db.Orders.FindAsync(id);
if (order == null)
{
return NotFound();
}

db.Orders.Remove(order);
await db.SaveChangesAsync();

return Ok(order);
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}

private bool OrderExists(Guid id)
{
return db.Orders.Count(e => e.Id == id) > 0;
}
}

Then, the implementation relating the SignalR Hub; you have to add the Startup.cs file if not present and register the SignalR middleware:

using Owin;
using Microsoft.Owin;

[assembly: OwinStartup(typeof(AngularSignalR.Startup))]
namespace AngularSignalR
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}

Then, the SignalR Hub:

public class NotificationsHub : Hub
{
public async Task OrderChanges()
{
await Clients.Others.NotifyOrderChanges();
}
}

The OrderChanges method is called from every client making a change to an order; NotifyOrderChanges is the event fired to any other client connected.

Now it’s the time to starting with the AngularJS application; the key points are:

  • The factory that interact with Web API service
  • The factory that manage SignalR notifications
  • The main controller

One useful service in AngularJS framework is $resource, a factory that allows you to interact with RESTful service easily.

The dependency of the ngResource module should be declarated in the main app module, and then is possibile to use $resource service; this is the implementation of the OrdersService factory:

module AngularSignalRApp.Services {

import ngr = ng.resource;

export interface IOrdersResourceClass extends ngr.IResourceClass<ngr.IResource<AngularSignalRApp.Models.IOrder>> {
create(order: AngularSignalRApp.Models.IOrder);
}

export class OrdersService {
private resource: IOrdersResourceClass;

constructor($resource: ngr.IResourceService) {
this.resource = <IOrdersResourceClass> $resource('/api/orders/:id', { id: '@Id' }, {
get: { method: "GET" },
create: { method: "POST" },
save: { method: "PUT" },
query: { method: "GET", isArray: true },
delete: { method: "DELETE" }
});
}

public create(order: AngularSignalRApp.Models.IOrder) {
return this.resource.create(order);
}

public save(order: AngularSignalRApp.Models.IOrder) {
if (order.Id == GuidEmpty) {
return this.resource.create(order);
}
else {
return this.resource.save(order);
}
}

public delete(order: AngularSignalRApp.Models.IOrder) {
return this.resource.remove(order);
}

public getAll() {
return this.resource.query();
}

static factory() {
return (r) => new OrdersService(r);
}
}

AngularSignalR.module.factory('OrdersService', ['$resource', OrdersService.factory()]);
}

Another factory is NotificationService, that expose the methods to interact with SignalR Hub:

module AngularSignalRApp.Services {

export class NotificationsService {

private connection: HubConnection;
private proxy: HubProxy;
private callback;

constructor() {
this.connection = $.hubConnection();
this.proxy = this.connection.createHubProxy('NotificationsHub');

this.proxy.on('NotifyOrderChanges', () => {
this.callback();
});

this.connection.start();
}

public NotifyOrderChanges() {
this.proxy.invoke('OrderChanges');
}

public OnOrderChanges(callback) {
if (callback) {
this.callback = callback;
}
}

static factory() {
return () => new NotificationsService();
}
}

AngularSignalR.module.factory('NotificationsService', [NotificationsService.factory()]);
}

The final step is the main controller, that uses OrdersService to manage CRUD operations on the orders and NotificationsService to tell with the SignalR Hub:

module AngularSignalRApp.Controllers {

import ngr = ng.resource;

export class OrdersController {
orders: ngr.IResourceArray<ngr.IResource<AngularSignalRApp.Models.IOrder>>;
order: AngularSignalRApp.Models.IOrder;
title: string;
toaster: ngtoaster.IToasterService;

private filter: ng.IFilterService;
private modalService: ng.ui.bootstrap.IModalService;
private ordersService: AngularSignalRApp.Services.OrdersService;
private notificationsService: AngularSignalRApp.Services.NotificationsService;

public static $inject = ['$filter', '$uibModal', 'toaster', 'OrdersService', 'NotificationsService'];

constructor(filter: ng.IFilterService, modalService: ng.ui.bootstrap.IModalService, toaster: ngtoaster.IToasterService,
ordersService: AngularSignalRApp.Services.OrdersService, notificationsService: AngularSignalRApp.Services.NotificationsService) {
this.filter = filter;
this.modalService = modalService;
this.ordersService = ordersService;
this.notificationsService = notificationsService;

this.notificationsService.OnOrderChanges(() => {
this.Load();
});

this.toaster = toaster;
this.title = "Orders";

this.Load();
}

public New() {
this.order = {
Id: GuidEmpty,
Article: "",
Amount: 0,
CreationDate: new Date(),
Customer: "",
Notes: ""
};
}

public Edit(order) {
this.order = order;
}

public Delete() {

var vm = this;
var modalInstance = vm.modalService.open({
animation: true,
templateUrl: "/App/Views/Shared/ConfirmDeleteModal.html",
controller: "ModalsController as vm",
size: "modal-sm"
});

modalInstance.result.then(function () {
vm.ordersService.delete(vm.order).$promise.then((data) => {
var orderToDelete = vm.filter('filter')(vm.orders, { Id: vm.order.Id })[0];
var index = vm.orders.indexOf(orderToDelete);
vm.orders.splice(index, 1);
vm.order = null;

vm.notificationsService.NotifyOrderChanges();
vm.toaster.success("Order deleted successfully.");
}, (error) => {
vm.toaster.error("Error deleting order.", error.data.message);
});
});
}

public Save() {
this.ordersService.save(this.order).$promise.then((data) => {
if (this.order.Id == GuidEmpty) {
this.order = data;
this.orders.push(data);
}

this.notificationsService.NotifyOrderChanges();
this.toaster.success("Order saved successfully.");

}, (error) => {
this.toaster.error("Error saving order", error.data.message);
});
}

public Close() {
this.order = null;
}

private Load() {
this.ordersService.getAll().$promise.then((data) => {
this.orders = data;
this.toaster.success("Orders loaded successfully.");
return;
}, (error) => {
this.toaster.error("Error loading orders", error.data.message);
});
}
}

AngularSignalR.module.controller('OrdersController', OrdersController);
}

The comunication with the SignalR Hub occur in the New, Save and Delete methods,
and when the NotifyOrderChanges event is fired.

You can find the project here.

 

 

ASP.NET SignalR and TypeScript in an AngularJS SPA

Improve performance of a recursive method with Redis

Recursive method are very useful to process for example, parent/child relations and build organization trees.

But for complex trees is very frequently to encounter performance problems, specially if the tree is build for every request.

In my case, the hierarchical tree is composed on two sql table (node and relations); the problem was that with a large number of nodes and  relations, the performance slow down drammatically.

In my case,  the solution that I’ve adopted is to use Azure Redis Cache to store the nodes of the tree and StackExchange.Redis as client library.

First of all, I use EF Code First and I’ve two tables, node and relations; migrations are enabled and the Seed Method of Configuration class is overridden to populate the database with test datas:

internal sealed class Configuration : DbMigrationsConfiguration<OrganizationTree.DataLayer.Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}

protected override void Seed(OrganizationTree.DataLayer.Context context)
{
...
context.SaveChanges();
}
}

The RedisCacheRepository is responsible to managing access to redis cache:

public class RedisCacheRepository<TObject> : ICacheRepository<TObject> where TObject : class
{
IDatabase _cache;

public RedisCacheRepository(IDatabase cache)
{
_cache = cache;
}

public void InsertOrUpdate(string key, TObject obj)
{
string val = JsonConvert.SerializeObject(obj);
_cache.StringSet(key, val);
}

public async Task InsertOrUpdateAsync(string key, TObject obj)
{
string val = JsonConvert.SerializeObject(obj);
await _cache.StringSetAsync(key, val);
}

public void InsertOrUpdate(string key, IEnumerable<TObject> objects)
{
var items = objects.Select(obj => (RedisValue)JsonConvert.SerializeObject(obj)).ToArray();
_cache.SetAdd(key, items);
}

public async Task InsertOrUpdateAsync(string key, IEnumerable<TObject> objects)
{
var items = objects.Select(obj => (RedisValue)JsonConvert.SerializeObject(obj)).ToArray();
await _cache.SetAddAsync(key, items);
}

public TObject Get(string id)
{
TObject obj = null;
string cachedObject = _cache.StringGet(id);
if (cachedObject != null)
{
obj = JsonConvert.DeserializeObject<TObject>(cachedObject);
}

return obj;
}

public async Task<TObject> GetAsync(string id)
{
TObject obj = null;
string cachedObject = await _cache.StringGetAsync(id);
if (cachedObject != null)
{
obj = JsonConvert.DeserializeObject<TObject>(cachedObject);
}

return obj;
}

public IEnumerable<TObject> GetList(string id)
{
IEnumerable<TObject> objs = new List<TObject>();
var items = _cache.SetMembers(id);

if (items.Count() > 0)
{
objs = items.Select(item => JsonConvert.DeserializeObject<TObject>(item));
}

return objs;
}

public async Task<IEnumerable<TObject>> GetListAsync(string id)
{
IEnumerable<TObject> objs = new List<TObject>();
var items = await _cache.SetMembersAsync(id);

if (items.Count() > 0)
{
objs = items.Select(item => JsonConvert.DeserializeObject<TObject>(item));
}

return objs;
}

public void Invalidate(string id)
{
_cache.KeyDelete(id);
}

public async Task InvalidateAsync(string id)
{
await _cache.KeyDeleteAsync(id);
}
}

The class TreeNodeService is responsible to build the organization tree and accept in the constructor two parameters, the EF context and the cache database.

public TreeNodeService(DbContext dbContext, IDatabase cache)
{
_nodeRepository = new Repository<Node>(dbContext);
_relationRepository = new Repository<Relation>(dbContext);
_cacheRepository = new RedisCacheRepository<TreeNode>(cache);
}

The LoadTree method load the entire tree structure, while GetChilds method load the childs of a specific node. This method checks if the node is already cached, if it’s not it retrives from database. The GetChildsFromDatabase method load the childs, which are placed in the cache if not present.

public async Task<TreeNode> LoadTree(Guid idTopNode)
{
return await GetTree(idTopNode);
}

public async Task<IEnumerable<TreeNode>> GetChilds(TreeNode treeTopNode)
{
IEnumerable<TreeNode> childNodes = await _cacheRepository.GetListAsync(treeTopNode.Node.Id.ToString());
if (childNodes.Count() > 0)
{
return await Task.Run(() => GetChildsFromCache(treeTopNode).ToList());
}
else
{
IEnumerable<TreeNode> childsTopNode = await Task.Run(() => GetChildsFromDatabase(treeTopNode).ToList());
await _cacheRepository.InsertOrUpdateAsync(treeTopNode.Node.Id.ToString(), childsTopNode);
return childsTopNode;
}
}

private async Task<TreeNode> GetTree(Guid idTopNode)
{
Node topNode = _nodeRepository.FirstOrDefault(n => n.Id == idTopNode);
TreeNode treeTopNode = new TreeNode();

if (topNode != null)
{
treeTopNode.Node = topNode;
treeTopNode.Childs = await GetChilds(treeTopNode);
}

return treeTopNode;
}

private IEnumerable<TreeNode> GetChildsFromDatabase(TreeNode parentTreeNode)
{
List<Relation> relations = _relationRepository.Find(r => r.IdParent == parentTreeNode.Node.Id).ToList();

foreach (Relation rel in relations)
{
TreeNode childTreeNode = new TreeNode();
childTreeNode.Node = rel.Child;
childTreeNode.Parent = parentTreeNode;

childTreeNode.Childs = GetChildsFromDatabase(childTreeNode).ToList();

if (childTreeNode.Childs.Count() > 0)
{
_cacheRepository.InsertOrUpdateAsync(childTreeNode.Node.Id.ToString(), childTreeNode.Childs);
}

yield return childTreeNode;
}
}

private IEnumerable<TreeNode> GetChildsFromCache(TreeNode parentTreeNode)
{
parentTreeNode.Childs = _cacheRepository.GetList(parentTreeNode.Node.Id.ToString());

foreach (TreeNode childTreeNode in parentTreeNode.Childs)
{
childTreeNode.Childs = GetChildsFromCache(childTreeNode).ToList();
yield return childTreeNode;
}
}

For business requirements, this class stores in the cache tree nodes separately, but to maximize performances it’s possible to purge the complete tree with a single key.

You can find the project here.

Improve performance of a recursive method with Redis