如何从 EF Core 的 FromSqlRaw 获取空间值

How do I get a spatial value from EF Core's FromSqlRaw

最近我遇到了一个问题,我只需要对我的数据库使用 FromSqlRaw 来执行 Postgresql 扩展之一的功能。此函数 returns 是一个条目,我想以某种方式获取此条目的值。但是,由于某些原因我不能这样做:

var buffer = DataContext.LayerDatas.FromSqlRaw($"select ST_Buffer(ld.\"Geom\", 100, 'join=mitre') " +
            $"from public.\"Layers\" ld where ld.\"LId\" = {layerId} and ld.\"OId\" = {objectId}").FirstOrDefault();

从 SqlRaw 获取计算值以便在我的代码中使用它的正确方法是什么?

编辑: 当我通过 DBeaver 执行查询时,它会给我以下信息:

我想在我的 C# 代码中获取它。

但是 c# 应用程序中的代码给出了以下异常:

42601 syntax error (at or near "d"), 

它显示我的查询为:

{select ld."Geom", ST_Buffer(ld."Geom", 100, 'join=mitre') from public."Layers" ld where ld."LId" = f21e400c-9e6d-4c28-b14c-1f0ced5b6ebb and ld."OId" = 3126}

您使用了字符串插值来生成导致查询无效的查询字符串。

... where ld."LId" = f21e400c-9e6d-4c28-b14c-1f0ced5b6ebb 

代码创建了一个 SQL 字符串,而不是参数化查询,其中包含导致无效查询的注入值。

FromSqlRaw works with normal strings and explicitly specified parameters. In fact, the docs have a pretty big warning about this.

在 FromSqlRaw 中使用参数

要使用 FromSqlRaw,必须 不能使用 插值。参数可以按位置传递:

var sql="select ST_Buffer(ld.Geom, 100, 'join=mitre') 
from public.Layers ld 
where ld.LId = {0} and ld.OId = {1}";

var buffer = DataContext.LayerDatas.FromSqlInterpolated(sql,layerId,objectId)
                        .FirstOrDefault();

C# 允许多行字符串,因此无需使用串联来提高查询的可读性。

通过 FromSqlInterpolated

使用插值

如果你想使用插值传递参数你需要FromSqlInterpolated :

var buffer = DataContext.LayerDatas.FromSqlInterpolated(
$"select ST_Buffer(ld.Geom, 100, 'join=mitre') 
from public.Layers ld 
where ld.LId = {layerId} and ld.OId = {objectId}"
).FirstOrDefault();

请注意,该字符串是 一个没有串联的长字符串。这会产生 FromSqlInterpolated.

预期的 FormattableString class

读取 GIS 类型

ST_Buffer 是 PostgreSQL and MySQL which returns a spatial type. In SQL Server it's STBuffer 使用的标准 GIS 函数。

空间类型 are supported in EF Core through the NetTopologySuite 包和文档中提到的 database-specific 包。

对于 PostgreSQL,正确的包是 Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite. Its use is explained in Spatial Mapping with NetTopologySuite

要与 EF Core 一起使用,安装正确的包后,需要在配置 DbContext 时注册它,例如:

options.UseSqlServer(
    @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=WideWorldImporters",
    x => x.UseNetTopologySuite());

之后,空间类型和属性可以像其他类型一样使用:

// Find the nearest city
var nearestCity = db.Cities
    .OrderBy(c => c.Location.Distance(currentLocation))
    .FirstOrDefault();