Since version 2.2, EF Core supports mapping to spatial data using types from the NetTopologySuite. In this post, I will demonstrate how to use this feature to query entities based on their geolocation.

Say we have the following User class:

public class User
{
  public string Username { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public Point Location { get; set; }
}

User's location is represented with the Point class from the NetTopologySuite.Geometries namespace from the NTS. NTS library can be installed via NuGet:

Install-Package NetTopologySuite

We can instantiate a user the following way:

User u = new User
{
  Username = "user1",
  FirstName = "Maureen",
  LastName = "Miles",
  Location = new Point(16.3738, 48.2082) {
    SRID = 4326
  }
};

To use the geographical longitude and latitude I did the following:

  • Set the SRID (spatial reference system id) to 4326, which is the spatial reference system used by Google maps (WGS84).
  • Pass the longitude as the first parameter (x) and the latitude as the second parameter (y) to the Point constructor.

To use spatial data in EF Core, we need to install the following package:

Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite

Some other databases are supported as well:

  • SQLite: Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite
  • PostgreSQL: Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite

We also need to use UseNetTopologySuite option in our EF context. Our context will look like this:

public class MyContext : DbContext
{
  public DbSet<User> Users { get; set; }

  protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  {
    optionsBuilder.UseSqlServer(
      @"my connection string",
      x => x.UseNetTopologySuite());
  }

  protected override void OnModelCreating(ModelBuilder modelBuilder)
  {
    modelBuilder.Entity<User>().HasKey(x => x.Username);
  }
}

When we run an EF migration to create our database schema, the Users table will look like this:

Users table schema.

As you can see, spatial properties are mapped to geography columns in SQL Server by default.

Now, let's say we have users in the following locations:

Locations of our users.

this is the data in our Users table:

Users table data.

Let's take e.g. Berlin as our referent location

and try to query our data based on some geolocational features.


Find all the users within 700km:

//Berlin coordinates
Point myLocation = new Point(13.4050, 52.5200)
{
  SRID = 4326
};

var ctx = new MyContext();

double radiusMeters = 700000;
User[] usersWithinRadius = ctx.Users.Where(x => x.Location.Distance(myLocation) <= radiusMeters).ToArray();
                                                                                                          
foreach (User u in usersWithinRadius)
  Console.WriteLine($"{u.Username}\t{u.FirstName} {u.LastName}");

For the above snippet, we will receive the following output:

amsterdam   Elvis Yoxall
brussels    Winslow Wescott
budapest    Dex Hamilton
prague      Scotty Neil
vienna      Maureen Miles
warsaw      Seraphina Rodgers

And this is the actual SQL that got generated by the EF:

SELECT [x].[Username], [x].[FirstName], [x].[LastName], [x].[Location]
FROM [Users] AS [x]
WHERE [x].[Location].STDistance(0xE6100000010CC3F5285C8F424A408FC2F5285CCF2A40) <= 700000

Find the closest 3 users:

//Berlin coordinates
Point myLocation = new Point(13.4050, 52.5200)
{
  SRID = 4326
};

var ctx = new MyContext();

User[] closestUsers = ctx.Users
  .OrderBy(x => x.Location.Distance(myLocation))
  .Take(3)
  .ToArray();

foreach (User u in closestUsers)
  Console.WriteLine($"{u.Username}\t{u.FirstName} {u.LastName}");

For the above snipped, we sill receive the following output:

prague    Scotty Neil
warsaw    Seraphina Rodgers
vienna    Maureen Miles

And this is the actual SQL that got generated by the EF:

SELECT TOP(3) [x].[Username], [x].[FirstName], [x].[LastName], [x].[Location]
FROM [Users] AS [x]
ORDER BY [x].[Location].STDistance(0xE6100000010CC3F5285C8F424A408FC2F5285CCF2A40)

Entity Framework Core made spatial data manipulation easy and straightforward. It offers many other functionalities for manipulating the spatial data, besides the ones shown in this post. I highly encourage you to try and explore some of them.