JSON infinite loops in ASP.NET

If you accidentally (or purposely) have an infinite loop in an object, where it has a reference that points back to itself, when you try to return that object as JSON in ASP.NET, you get an error:

JsonSerializationException: Self referencing loop detected for property …

To avoid that, you can add a line to your Startup ConfigureServices method:

// dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson

services.AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.ReferenceLoopHandling
            = ReferenceLoopHandling.Ignore;
    });

JSON casing in ASP.NET Core

Some mallethead decided that they wanted to take UpperCamelCase names of properties and change them to lowerCamelCase when sending JSON to the client. Sure, maybe this fits standard naming conventions, but it means that you’ll end up with different property names.

public JsonResult GetFoo()
{
    return Json(new Foo { 
        Name = "Jane Doe", 
        Age = 32 
    });
}

This returns the following content:

{"name":"Jane Doe","age":32}

Personally, I want the properties to match. Here’s the change to make that happen:

// dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson

// Startup.cs: in ConfigureServices
services.AddNewtonsoftJson(options =>
    {
        options.UseMemberCasing();
    });

Now the same code produces:

{"Name":"Jane Doe","Age":32}

Project Properties

I’m working on standardizing my code using the latest and greatest .NET features. For me, that currently means adding the following to the first <PropertyGroup> section in my csproj file:

<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
  • LangVersion: Allows me to use the latest features in C#, like switch expressions, tuple patterns, and using declarations.
  • Nullable: Incorporating nullable reference types, which is new and weird, but it’s a fantastic way to greatly reduce unnecessary null-checking and NullReferenceException errors.
  • TreatWarningsAsErrors and WarningsAsErrors: Honestly, I started reading what the difference is between these, and I gave up, so I just said screw it and put both in there. This marks compiler warnings as errors, forcing me to follow my own standards.
    • My configuration in Visual Studio (in 2019: Tools/Options/Text Editor/C#/Code Style) is set pretty strict, where a lot of the typically “Refactoring Only” options are set to “Error”. Your style and preferences are of course up to you and your team.

Also, I pretty much always use an appsettings.json file, and I want it in the output directory, so I add:

<ItemGroup>
    <None Update="appsettings.json">
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
</ItemGroup>

In a console app, I’ll need to add a reference to the appropriate package:

dotnet add package Microsoft.Extensions.Configuration.Json

And the code to load it:

using Microsoft.Extensions.Configuration;

private static IConfiguration _config;

static void Main() {
    _config = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json")
        .Build();

    // ...
}

Non-buffered Output in ASP.NET

Most of the time, it makes sense to build a complete response and return it all at once. Sometimes, if you have a big result, like a large report, you’ll want to send the file a response a little at a time, to keep from storing a huge item in memory.

You can write to a file and then output that file in the response, or you can dynamically write and flush the response a little at a time, until the response completes.

public ActionResult GetFile() {
    string[] lines = { "line 1", "line 2", "line 3", "line 4" };
    Response.Clear();
    Response.ClearHeaders();
    Response.ContentType = "text/plain";
    Response.AddHeader("Content-disposition", "attachment;filename=somefile.txt");
    Response.Buffer = Response.BufferOutput = false;
    foreach (string line in lines) {
        Response.Write(line + "\r\n");
        Response.Flush();
    }
    HttpContext.ApplicationInstance.CompleteRequest();
    return null;
}

Find a strategy that makes sense – if you’re returning a text-based report, maybe flush every 100 or 1000 lines. You can always test various scenarios, and watch your memory usage in Windows and in the debugger to find the absolute best size.

Ninject in MVC5

I’ve found dozens of blogs and articles online about how to use Ninject in ASP.NET. It seems all of them are either a little outdated, or a little confusing.

So I put together a basic working application for reference. This uses Visual Studio 2017, with ASP.NET MVC 5 which will run in regular IIS.

I only know the very basics when it comes to Ninject, and I don’t even know if this is the best practice when building an app. But this does work, and does allow you to bind to different modules from different projects, which would allow you to do things like mocking database results or skipping error notifications.

If you have suggestions for how to improve this basic project (without complicating it too much), or any corrections or best practices I should consider, please let me know.

The basic idea is pretty straightforward:

  • Build interfaces and production implementations in separate projects.
  • Build a production module project which maps the two together.
  • In your live ASP.NET project, make a few tweaks to the MvcApplication class, including loading the production module.
  • Add parameters to your controller constructors, of the interface type. At runtime, they will be the real live type.
  • In other projects, like test projects, when you want to use different implementations of the interfaces, just build the new (or overriding) implementation classes, and a module that re-binds interfaces as appropriate.

Interfaces Project

// IRuleProcessor.cs

namespace HappyTarts.Services.Interfaces {
    public interface IRuleProcessor {
        string ProcessRule(string rule);
    }
}

Implementation Project

// RuleProcessor.cs

namespace HappyTarts.Services.Implementation {
    public class RuleProcessor : IRuleProcessor {
        string IRuleProcessor.ProcessRule(string rule) {
            return rule;
        }
    }
}

NinjectModules Project

// AppModule.cs

namespace HappyTarts.NinjectModules {
    public class AppModule : NinjectModule {
        public override void Load() {
            Bind<IRuleProcessor>().To<RuleProcessor>();
        }
    }
}

Web Project

// Global.asax.cs

namespace HappyTarts.Web {
    public class MvcApplication : NinjectHttpApplication {
        protected override void OnApplicationStarted() {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }

        protected override IKernel CreateKernel() {
            var kernel = new StandardKernel();
            try {
                kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
                kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

                RegisterServices(kernel);
                return kernel;
            } catch {
                kernel.Dispose();
                throw;
            }
        }

        private static void RegisterServices(IKernel kernel) {
            kernel.Load(new AppModule());
        }
    }
}

// HomeController.cs
namespace HappyTarts.Web.Controllers {
    public class HomeController : Controller {
        private readonly IRuleProcessor _ruleProcessor;

        public HomeController(IRuleProcessor ruleProcessor) {
            _ruleProcessor = ruleProcessor;
        }

        public ActionResult Index(string id) {
            ViewBag.Result = $"Result={_ruleProcessor.ProcessRule(id)}";
            return View();
        }
    }
}

Test Project

// UnitTests.cs

namespace HappyTarts.Tests {
    public class TestRuleProcessor : IRuleProcessor {
        string IRuleProcessor.ProcessRule(string rule) {
            return $"TEST {rule}";
        }
    }

    public class TestModule : AppModule {
        public override void Load() {
            base.Load();
            Rebind<IRuleProcessor>().To<TestRuleProcessor>();
        }
    }

    [TestClass]
    public class UnitTests {
        private IKernel _kernel;

        private void LoadTestModule() {
            _kernel = new StandardKernel(new TestModule());
        }

        private void LoadProdModule() {
            _kernel = new StandardKernel(new AppModule());
        }

        [TestMethod]
        public void TestTestModule() {
            LoadTestModule();
            var processor = _kernel.Get<IRuleProcessor>();
            Assert.AreEqual("TEST abc", processor.ProcessRule("abc"));
        }

        [TestMethod]
        public void TestProdModule() {
            LoadProdModule();
            var processor = _kernel.Get<IRuleProcessor>();
            Assert.AreEqual("abc", processor.ProcessRule("abc"));
        }
    }
}

MVC Disable Converting Empty String to Null

Some joker decided that empty strings passed into an MVC controller should be converted to null by default. One way to avoid this is to decorate the string properties of the model class:

using System.ComponentModel.DataAnnotations;

public class SomeModel {
    [DisplayFormat(ConvertEmptyStringToNull = false)]
    public string SomeProperty { get; set; }
}

[HttpPost]
public JsonResult DoSomething(SomeModel myModel) {
    bool isEmpty = (myModel.SomeProperty == string.Empty);
    return Json(new { IsEmpty = isEmpty });
}

Then when you pass in an empty string, it will remain an empty string in the .NET code:

jQuery.ajax({
    type: "post",
    url: "@Url.Action("DoSomething")",
    data: JSON.stringify({ SomeProperty: "" }),
    dataType: "json",
    contentType: "application/json;charset=utf-8;"
}).done(function (result) {
    console.log(result);
});

LINQ in sets of

Thanks to this Stackoverflow answer for showing how to easily return LINQ results in sets:

public static IEnumerable<T[]> InSetsOf<T>(this IEnumerable<T> source, int max) {
    List<T> toReturn = new List<T>(max);
    foreach (var item in source) {
        toReturn.Add(item);
        if (toReturn.Count == max) {
            yield return toReturn.ToArray();
            toReturn = new List<T>(max);
        }
    }
    if (toReturn.Any()) {
        yield return toReturn.ToArray();
    }
}

Left Join in Entity Framework

If you need a left join in Entity Framework, you have a couple options. First, if you’re using a real foreign key that just happens to be nullable, then you can use the regular navigation properties. But if you’re doing a left join manually, or with other factors, then you need to do things just a little differently:

Suppose we have the following database:

create table dbo.Foods (
  FoodID int not null identity primary key
 ,FoodName varchar(100) not null
);
go
insert dbo.Foods (FoodName) values ('Pizza'), ('Chicken'), ('Potatoes'), ('Broccoli');
go
create table dbo.People (
 PersonID int not null identity primary key
 ,FirstName varchar(100) not null
 ,FavoriteFoodID int null
 ,constraint FK_Person_FavoriteFoodID
  foreign key (FavoriteFoodID) references dbo.Foods (FoodID)
);
go
insert dbo.People (FirstName, FavoriteFoodID)
values ('John', 1), ('Mary', 2), ('Pat', null);
go

We can build our Entity Framework tables as follows:

[Table("Foods")]
public class Food {
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int FoodID { get; set; }
    public string FoodName { get; set; } = "";
}

[Table("People")]
public class Person {
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int PersonID { get; set; }
    public string FirstName { get; set; } = "";
    public int? FavoriteFoodID { get; set; }

    [ForeignKey(nameof(FavoriteFoodID))]
    public Food? FavoriteFood { get; set; }
}

public class MyContext : DbContext {
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
        optionsBuilder.UseSqlServer(@"server=(localdb)\MSSQLLocalDB;database=sandbox20200409;integrated security=true;");
    }

    public DbSet<Food>? Foods { get; set; }
    public DbSet<Person>? People { get; set; }
}

The navigation property FavoriteFood gives us the ability to harness Entity Framework’s intelligence to build a query:

using var context = new MyContext();
var firstQuery = (from p in context.People
                  select new
                  {
                      p.PersonID,
                      p.FirstName,
                      p.FavoriteFood!.FoodID,
                      p.FavoriteFood.FoodName
                  }).ToArray();

The generated SQL looks as we’d expect:

SELECT [p].[PersonID], [p].[FirstName], [f].[FoodID], [f].[FoodName]
FROM [People] AS [p]
LEFT JOIN [Foods] AS [f] ON [p].[FavoriteFoodID] = [f].[FoodID]

 But if that navigation property wasn’t there, then we have an alternative way of doing a left join:

var secondQuery = (from p in context.People
                   from f in context.Foods.Where(f => f.FoodID == p.FavoriteFoodID).DefaultIfEmpty()
                   select new
                   {
                       p.PersonID,
                       p.FirstName,
                       f.FoodID,
                       f.FoodName
                   }).ToArray();

This generates identical SQL to the first one. Note the DefaultIfEmpty call.

There are other ways, but I find this to be very easy to read and understand.

Lazy Loading in Entity Framework Core

Here’s all you need to do to enable lazy loading of navigation properties in Entity Framework Core – doing this allows you to call one of the navigation properties after the original query, and EF will figure out what SQL it needs to run to pull that data.

If you know you’re going to need the data, it’s generally better to pull it upfront, using Include or including the data in the LINQ select clause. But sometimes the option is nice, so I recommend allowing it, and deciding at dev time whether or not to use it.

Install-Package Microsoft.EntityFrameworkCore.Proxies
 -- or --
dotnet add package Microsoft.EntityFrameworkCore.Proxies

In the OnConfiguring method, just tack on UseLazyLoadingProxies, like:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.UseLazyLoadingProxies().UseSqlServer(_connectionString);

Make sure your navigation properties are all marked virtual (thankfully, you’ll get a friendly runtime error right away if you forget this step):

[Table("Customers")]
public class Customer {
    [Key]
    public int CustomerID { get; set; }
    public string Name { get; set; } = "";

    [InverseProperty(nameof(Customer))]
    public virtual List<Car>? Cars { get; set; }
}

[Table("Cars")]
public class Car {
    [Key]
    public int CarID { get; set; }
    public string Make { get; set; } = "";
    public int CustomerID { get; set; }

    [ForeignKey(nameof(CustomerID))]
    public virtual Customer? Customer { get; set; }
}

// Use lazy loading: easiest, but requires two database calls
async static Task Main() {
    using var context = new MyContext();

    /*
        SELECT TOP(2) [c].[CustomerID], [c].[Name]
        FROM [Customers] AS [c]
        WHERE [c].[CustomerID] = 4
    */
    var cust = await context.Customers.SingleAsync(x => x.CustomerID == 4).ConfigureAwait(false);
    Console.WriteLine(cust.Name);

    /*
        exec sp_executesql N'SELECT [c].[CarID], [c].[CustomerID], [c].[Make]
        FROM [Cars] AS [c]
        WHERE [c].[CustomerID] = @__p_0',N'@__p_0 int',@__p_0=4
    */
    foreach (var car in cust.Cars!) {
        Console.WriteLine($"{car.CarID}: {car.Make}");
    }
}

// Get the data upfront using Include - single database call, but more complex query
async static Task Main() {
    using var context = new MyContext();

    /*
        SELECT [t].[CustomerID], [t].[Name], [c0].[CarID], [c0].[CustomerID], [c0].[Make]
        FROM (
            SELECT TOP(2) [c].[CustomerID], [c].[Name]
            FROM [Customers] AS [c]
            WHERE [c].[CustomerID] = 4
        ) AS [t]
        LEFT JOIN [Cars] AS [c0] ON [t].[CustomerID] = [c0].[CustomerID]
        ORDER BY [t].[CustomerID], [c0].[CarID]
    */
    var cust = await context.Customers.Include(c => c.Cars)
        .SingleAsync(x => x.CustomerID == 4).ConfigureAwait(false);
    Console.WriteLine(cust.Name);

    foreach (var car in cust.Cars!) {
        Console.WriteLine($"{car.CarID}: {car.Make}");
    }
}