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:

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:

this is the data in our Users table:

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.