Posts tagged "ASP.NET"

2 posts

← Back to all posts

Jan 14, 2020

A Brief Comparison Between Newtonsoft.Json and System.Text.Json

TL;DR – System.Text.Json is a new JSON library for .NET with different design goals from its predecessor, Newtonsoft.Json. If you’re already using Newtonsoft.Json in an existing project, you likely don’t need to switch. If you absolutely need high JSON serialization/deserialization performance, go with System.Text.Json.

System.Text.Json was released last year and since it’s integrated into ASP.NET Core 3, I thought we should briefly compare it to the most downloaded NuGet package around, Newtonsoft.Json.

I think it’s important to understand the reasoning behind writing a whole new JSON library when we already have Newtonsoft.Json. System.Text.Json was designed first and foremost with high performance in mind - the .NET team (which includes James Newton-King, the guy who wrote Newtonsoft.Json) found they couldn’t refactor Newtonsoft.Json to meet some of these goals without making breaking changes to existing code.

In addition, System.Text.Json strictly adheres to the JSON spec, RFC 8259 - things you previously could do with Newtonsoft.Json (because it wasn’t spec compliant) aren’t allowed in System.Text.Json.

For example, Newtonsoft.Json will deserialize:

  • Property names that have a different case
  • Property names in JSON that have single/double/no quotes
  • Null values for non-nullable fields properties (null -> an int property is allowed)

System.Text.Json only supports (out of the box):

  • Deserializing JSON properties by names with the same case
  • Property names with double quotes
  • Deserializing JSON properties to their like-typed C# counterparts (int -> int only, not null -> int property)

There’s a lot of differences in behavior that you can find in this article, but the question needing answered is, do you need to switch from Newtonsoft.Json to System.Text.Json?

The answer is almost certainly no. If you switch, there are a lot of subtle differences that may cause runtime errors. Newtonsoft.Json is still a nice abstraction for .NET - System.Text.Json is much closer to the JSON metal.

Other links:

Read more →

Nov 21, 2019

Paging in ASP.NET Web API

Paging is a useful concept in any API.  Here’s an example of one that I use pretty frequently when making APIs in ASP.NET Web API.

You can download an example project (complete with unit tests!) here: https://github.com/schneidenbach/AspNetPagingExample

The steps we’re going to take to complete this task:

  • Setup our paging model.
  • Define our entity and API models.
  • Create our easy-to-use paging method that will magically turn an IQueryable into a paged set of data.

This example in particular requires AutoMapper, which will allow us to easily map our entities to our returned API models.  I also use Entity Framework to store and retrieve data, though that is not the focus of this particular project.  For the record – you should ALWAYS use API models (or DTOs, if you prefer) to do work through controllers.  Taiseer Joudeh, a Microsoft MVP for ASP.NET, calls it the “model factory pattern” and that’s how I typically refer to it.  That, or just API models. (Another way to think of API models: they hold the same purpose as a view model, but they’re not called that cause there’s no views.)  It’s a little bit more code up front, but will keep your code clean and enforce separation of concerns in the long run.  This has several advantages:

  • Lets you control what data is returned to the client.
  • Helps avoid binding to undocumented properties on PUTs/POSTs, which can be a pretty big security concern.
  • Maintains a separation of concerns. (This object returns data to the client, this object is a database entity, etc.)

1. Create and define a paging model.

public class PagedResults<T>
{
    /// <summary>
    /// The page number this page represents. 
    /// </summary>
    public int PageNumber { get; set; } 
    
    /// <summary> 
    /// The size of this page. 
    /// </summary> 
    public int PageSize { get; set; } 
    
    /// <summary> 
    /// The total number of pages available. 
    /// </summary> 
    public int TotalNumberOfPages { get; set; } 
    
    /// <summary> 
    /// The total number of records available. 
    /// </summary> 
    public int TotalNumberOfRecords { get; set; } 
    
    /// <summary> 
    /// The URL to the next page - if null, there are no more pages. 
    /// </summary> 
    public string NextPageUrl { get; set; } 
    
    /// <summary> 
    /// The records this page represents. 
    /// </summary> 
    public IEnumerable<T> Results { get; set; } 
}

We have our PageNumber and PageSize, which should match exactly what you requested (if you requested page 1 and page size 10, you should have a PageNumber of 1 and a PageSize of 10. These fields are included in the event you don’t want to require a page number or page size in your paged API.)

You have some Results, which represents the actual objects being returned.

There is a TotalNumberOfRecords and TotalNumberOfPages, which returns totals for the returned objects. If there are 100 total records and you’re requesting 15 records per page, you should expect that TotalNumberOfPages will return 7 pages.

Finally, one of the most useful properties in this model is NextPageUrl. NextPageUrl makes it very easy to get the next page in the set by providing the URL to that next resource for you.

2. Define your entities and your API models (you do use separate models for returning data, right?)

public class Employee
{
	public int Id { get; set; }
	public string FirstName { get; set; }
	public string LastName { get; set; }
	public string SocialSecurityNumber { get; set; } //you probably don't want this returned by default, making the EmployeeModel useful
	public ICollection < Todo > TodoList { get; set; } = new List < Todo > ();
}

public class EmployeeModel
{
	public int Id { get; set; }
	public string FirstName { get; set; }
	public string LastName { get; set; }
	public ICollection<TodoModel> TodoList { get; set; }
}

public class Todo
{
	public int Id { get; set; }
	public string Name { get; set; }
	public string Description { get; set; }
	public Employee Employee { get; set; }
}

public class TodoModel
{
	public int Id { get; set; }
	public string Name { get; set; }
	public string Description { get; set; }
}

public class EntityContext : DbContext
{
	public virtual DbSet<Employee> Employees { get; set; }
	public virtual DbSet<Todo> Todos { get; set; }
}

Note that in this example we are using Entity Framework for data storage.

3. Map them together using AutoMapper.

AutoMapper allows us to easily create the EmployeeModel from the Employee without writing and maintaining factory methods. All properties with the same name from Employee will be set on EmployeeModel. A lot of awesomeness in one little library.

Mapper.CreateMap<Employee, EmployeeModel>();
Mapper.CreateMap<Todo, TodoModel>();

4. Create the paged set of data.

I like to use the following CreatePagedResults method below on my controller base class – it does all of the heavy lifting for you.

public class EmployeesController : BaseController
{
    public EntityContext EntityContext { get; }
 
    protected EmployeesController(EntityContext context)
    {
        EntityContext = context;
    }
 
    public async Task<IHttpActionResult> Get(int? page = null, int pageSize = 10, string orderBy = nameof(Employee.Id), bool ascending = true)
    {
        if (page == null)
            return Ok(await EntityContext.Employees.ToListAsync());
 
        var employees = await CreatePagedResults<Employee, EmployeeModel>
            (EntityContext.Employees, page.Value, pageSize, orderBy, ascending);
        return Ok(employees);
    }
 
    /// <summary>
    /// Creates a paged set of results.
    /// </summary>
    /// <typeparam name="T">The type of the source IQueryable.</typeparam>
    /// <typeparam name="TReturn">The type of the returned paged results.</typeparam>
    /// <param name="queryable">The source IQueryable.</param>
    /// <param name="page">The page number you want to retrieve.</param>
    /// <param name="pageSize">The size of the page.</param>
    /// <param name="orderBy">The field or property to order by.</param>
    /// <param name="ascending">Indicates whether or not the order should be ascending (true) or descending (false.)</param>
    /// <returns>Returns a paged set of results.</returns>
    protected async Task<PagedResults<TReturn>> CreatePagedResults<T, TReturn>(
        IQueryable<T> queryable,
        int page,
        int pageSize,
        string orderBy,
        bool ascending)
    {
        var skipAmount = pageSize * (page - 1);
 
        var projection = queryable
            .OrderByPropertyOrField(orderBy, ascending)
            .Skip(skipAmount)
            .Take(pageSize).ProjectTo<TReturn>();
 
        var totalNumberOfRecords = await queryable.CountAsync();
        var results = await projection.ToListAsync();
 
        var mod = totalNumberOfRecords % pageSize;
        var totalPageCount = (totalNumberOfRecords / pageSize) + (mod == 0 ? 0 : 1);
 
            var nextPageUrl =
            page == totalPageCount
                ? null
                : Url?.Link("DefaultApi", new {
                    page = page + 1,
                    pageSize,
                    orderBy,
                    ascending
                });
 
        return new PagedResults<TReturn>
        {
            Results = results,
            PageNumber = page,
            PageSize = results.Count,
            TotalNumberOfPages = totalPageCount,
            TotalNumberOfRecords = totalNumberOfRecords,
            NextPageUrl = nextPageUrl
        };
    }
}

A couple of important things to note:

  • The Url.Link method assumes that you have the default Web API route called DefaultApi setup in your RouteConfig.  If you don’t, you might have to tweak this example to work for you.
  • This example uses an extension method called OrderByPropertyOrField which (if you haven’t guessed) orders the IQueryable by the specified property, with a boolean to determine whether or not the order by should be ascending or descending. This string points to a property or field name of the entity type represented by IQueryable. The extension method is below:
public static class Extensions
{
    /// <summary>
    /// Order the IQueryable by the given property or field.
    /// </summary>
 
    /// <typeparam name="T">The type of the IQueryable being ordered.</typeparam>
    /// <param name="queryable">The IQueryable being ordered.</param>
    /// <param name="propertyOrFieldName">The name of the property or field to order by.</param>
    /// <param name="ascending">Indicates whether or not the order should be ascending (true) or descending (false.)</param>
    /// <returns>Returns an IQueryable ordered by the specified field.</returns>
    public static IQueryable<T> OrderByPropertyOrField<T>(this IQueryable<T> queryable, string propertyOrFieldName, bool ascending = true)
    {
        var elementType = typeof (T);
        var orderByMethodName = ascending ? "OrderBy" : "OrderByDescending";
 
        var parameterExpression = Expression.Parameter(elementType);
        var propertyOrFieldExpression = Expression.PropertyOrField(parameterExpression, propertyOrFieldName);
        var selector = Expression.Lambda(propertyOrFieldExpression, parameterExpression);
 
        var orderByExpression = Expression.Call(typeof (Queryable), orderByMethodName,
            new[] {elementType, propertyOrFieldExpression.Type}, queryable.Expression, selector);
 
        return queryable.Provider.CreateQuery<T>(orderByExpression);
    }
}

Download the completed project here: https://github.com/schneidenbach/AspNetPagingExample

Read more →