IsWithinDistance не удалось перевести

Я пытаюсь вернуть список предметов, которые находятся, скажем, в 50 милях от заданного местоположения.

Моя таблица (упрощенная) выглядит следующим образом:

  • Id
  • Долгота
  • Широта
  • Положение дел
  • Активный

У меня есть первоначальный запрос:

var query = db.MyTable.Where(o=> o.Status == "New" && o.Active == true);

query = query.Where(o => new Point(o.Longitude, o.Latitude)
                  .IsWithinDistance(new Point(_currentLongitude, _currentLatitude), 50));

var result = query.ToList()

Однако - похоже, это не работает, и я получаю сообщение об ошибке, как показано ниже - есть идеи, как это решить? или если есть лучший способ получить ближайшие предметы?

.Where(p =› new Point(p.Longitude, p.Latitude) .IsWithinDistance(geom: __p_3,
Distance: ___maxDistance_4))' не удалось перевести.

Либо перепишите запрос в форме, которую можно перевести, либо явно переключитесь на оценку клиента, вставив вызов AsEnumerable(), AsAsyncEnumerable(), ToList() или ToListAsync(). См. https://go.microsoft.com/fwlink/?linkid=2101038. Чтобы получить больше информации.

Запуск.cs:

services.AddDbContext<AppDbContext>(options =>
{
                
 options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
   x => x.UseNetTopologySuite());
});

person Steve    schedule 09.12.2020    source источник
comment
Вам нужно сделать именно так, как предлагает сообщение об ошибке: Либо перепишите запрос в форме, которую можно перевести, либо явно переключитесь на оценку клиента, вставив вызов либо AsEnumerable(), AsAsyncEnumerable(), ToList(), или ToListAsync(). См. go.microsoft.com/fwlink/?linkid=2101038 для получения дополнительной информации< /я>   -  person Robert Harvey    schedule 10.12.2020
comment
@RobertHarvey - это встроенная функция в Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite. См. Пространственные данные EF Core и Пространственные данные в SQL Server EF Core Provider   -  person Matt Johnson-Pint    schedule 10.12.2020
comment
@Steve. Вы добавили .UseNetTopologySuite как показано здесь ? (Кроме того, я предполагаю, что SQL Server, пожалуйста, подтвердите или исправьте.)   -  person Matt Johnson-Pint    schedule 10.12.2020
comment
@MattJohnson-Пинт: пока   -  person Robert Harvey    schedule 10.12.2020
comment
Насколько я могу судить, создание нового Point из широты/долготы на данный момент не поддерживается. Возможно, вам придется хранить данные как тип Point в вашей модели.   -  person ESG    schedule 10.12.2020
comment
@RobertHarvey привет - да, пробовал var xx = query = (IQueryable<Profile>)query.Where(o => new Point(o.Longitude, o.Latitude) .IsWithinDistance(new Point(_profile.Longitude, _profile.Latitude), _maxDistance)).ToList();, но все та же ошибка   -  person Steve    schedule 10.12.2020
comment
@MattJohnson-Pint Да - MS SQL 2019 Express, и да, уже добавлено в startup.cs (отредактировал ответ, чтобы показать, что я включил)   -  person Steve    schedule 10.12.2020
comment
не могли бы вы проверить первую часть списка скрытых, а затем запустить второй фильтр db.MyTable.Where(o=> o.Status == "New" && o.Active == true).ToList();.   -  person coder_b    schedule 10.12.2020
comment
@coder_b - это нанесет ущерб цели пространственных запросов/индексов на стороне SQL и может привести к ненужному перемещению большого количества данных клиенту. Конечно, все зависит от того, сколько данных мы имеем в виду, но я бы не рекомендовал такой подход.   -  person Matt Johnson-Pint    schedule 10.12.2020
comment
Я полностью согласен, я проверял, что IsWithinDistance работает должным образом с отключенным набором данных. интересно, исключают ли какие-либо проблемы, связанные с конфигурациями и т. д.   -  person coder_b    schedule 10.12.2020


Ответы (1)


Чтобы эта функция работала, вам необходимо сохранить эти координаты как Point в поле SQL geography. Вы можете легко добавить это в существующую модель в качестве вычисляемого столбца для нового свойства.

// on your entity
public Point Coordinates { get; }

// in your db context's OnModelCreating
  modelBuilder.Entity<YourEntity>()
    .Property(x => x.Coordinates)
    .IsRequired()
    .HasComputedColumnSql("geography::Point(Latitude, Longitude, 4326)");

Примечание. SRID 4326 — это широко используемая система координат широта/долгота, поддерживаемая SQL Server. Подробнее об этом здесь.

Создайте свою модель и разверните ее в своей базе данных.

Теперь, когда у вас есть пространственное поле и свойство, вы можете сделать запрос следующим образом:

var point = new Point(_currentLongitude, _currentLatitude) { SRID = 4326 };
var distanceInMeters = 50 * 1609.344;  // 50 miles to meters conversion
var results = db.YourEntity
                .Where(x => x.Coordinates.IsWithinDistance(point, distanceInMeters))
                .ToList();

SRID 4326 использует метры для расстояния, поэтому обязательно конвертируйте, как показано выше, если вы используете мили.

Кроме того, если у вас много данных, вы также захотите добавить пространственный индекс в этот столбец, но EF Core пока не поддерживает это напрямую, поэтому вам придется делать это непосредственно в SQL.

person Matt Johnson-Pint    schedule 09.12.2020
comment
Обратите внимание, что подход с вычисляемым столбцом является необязательным. Вы всегда можете иметь свойство Point отдельно, но тогда у вас не будет отдельных свойств широты и долготы. - person Matt Johnson-Pint; 10.12.2020
comment
Привет, спасибо за быстрый ответ, я попробую это. Есть ли какая-либо польза от наличия/отсутствия вычисляемого столбца для точки? - person Steve; 10.12.2020
comment
Поле Point (sql geography) — это необходимые данные для работы пространственных запросов. Вы можете просто заполнить Point и оставить отдельные поля широты/долготы выключенными. Наличие обоих в основном просто удобство. - person Matt Johnson-Pint; 10.12.2020