DotNetNext / SqlSugar

.Net aot ORM Fastest ORM Simple Easy VB.NET Sqlite orm Oracle ORM Mysql Orm 虚谷数据库 postgresql ORm SqlServer oRm 达梦 ORM 人大金仓 ORM 神通ORM C# ORM , C# ORM .NET ORM NET5 ORM .NET6 ORM ClickHouse orm QuestDb ,TDengine ORM,OceanBase orm,GaussDB orm ,Tidb orm Object/Relational Mapping
https://www.donet5.com/Home/Doc
MIT License
5.24k stars 1.33k forks source link

PostgresSQL 无法映射自定义数据库类型 #1142

Closed ZeekoZhu closed 1 year ago

ZeekoZhu commented 1 year ago

例如 pgvector 提供的 vector 类型,需要通过 pgsql 的 TypeMapping 添加自定义的 handler,但是却无法设置 SugarParameter.CustomDbType 为 null ,从而导致 Npgsql 无法使用自定义类型

image

image

DotNetNext commented 1 year ago

https://www.donet5.com/Home/Doc?typeId=2542 sqlsugar有自定义类型

ZeekoZhu commented 1 year ago

https://www.donet5.com/Home/Doc?typeId=2542 sqlsugar有自定义类型

我的使用场景跟这篇文档有些不同, pgvector 是 pgsql 的一个插件,提供了一个新的数据库类型 vector,我需要将一个 dotnet class 映射到 vector 类型。创建 ISugarDataConverter 是必须的,否则 SqlSugar 无法正确设置参数类型,默认是 String,我需要将其设置为 vector 类型。

除了参数外,在读取数据的时候也需要配置 Npgsql 的 TypeHandler,否则 Npgsql 驱动将无法识别 vector 类型。

但是现在问题出现了,在 ISugarDataConverter.ParameterConverter 中,必须要将 CustomDbType 设置成一个非 Null 的值,否则最终生成的 NpgsqlParamter.NpgsqlDbType 将是 string。但是 Npgsql 的自定义 TypeHandler 只会处理 NpgsqlDbType 字段为 Null 的参数。

我查阅了 SqlSugar 的代码,并没有办法将最终生成的 NpgsqlParamter.NpgsqlDbType 设置成 null 。

DotNetNext commented 1 year ago

image

你研究一下原生怎么写的,这个东西不可能等于 NULL

ZeekoZhu commented 1 year ago

找到了一个方法:

    var parameter = new SugarParameter(name, null)
    {
      DbType = DbType.Object, // 调用 NpgsqlParameter.DbType 的 setter 将 NpgsqlDbType  设置成 null
      Value = value // 重新设置 Value,避免 SqlSugar 自动将 value 转换成 JSON string
    };
DotNetNext commented 1 year ago

能用就行

ZeekoZhu commented 1 year ago
    var other = new PgVector(new[] { 1.0f, 2.0f, 3.0f });
    var item = _db.Queryable<VectorItem>().Select(x => VectorOperator.Distance(x.Vector, other)).First();

我发现在查询的时候,Expression 中的参数无法被 SqlSugar 的自定义类型 Converter 处理,导致 SugarParameter 的 DbType 被设置为 AsciiString

ZeekoZhu commented 1 year ago

类似的情况还有下面这种:

    var item = _db.Queryable<VectorItem>()
      .Select(x => SqlFunc.AggregateAvg(x.Vector))
      .First();

报错:

System.ArgumentNullException : Value cannot be null. (Parameter 'con')
   at System.Reflection.Emit.DynamicILGenerator.Emit(OpCode opcode, ConstructorInfo con)
   at SqlSugar.IDataReaderEntityBuilder`1.CreateBuilder(Type type)
   at SqlSugar.DbBindAccessory.<>c__DisplayClass4_0`1.<GetEntityList>b__0()
   at SqlSugar.ReflectionInoCore`1.GetOrCreate(String cacheKey, Func`1 create)
   at SqlSugar.ReflectionInoCacheService.GetOrCreate[V](String cacheKey, Func`1 create, Int32 cacheDurationInSeconds)
   at SqlSugar.DbBindAccessory.GetEntityList[T](SqlSugarProvider context, IDataReader dataReader)
   at SqlSugar.DbBindProvider.DataReaderToList[T](Type type, IDataReader dataReader)
   at SqlSugar.QueryableProvider`1.GetData[TResult](Boolean isComplexModel, Type entityType, IDataReader dataReader)
   at SqlSugar.QueryableProvider`1.GetData[TResult](KeyValuePair`2 sqlObj)
   at SqlSugar.QueryableProvider`1._ToList[TResult]()
   at SqlSugar.QueryableProvider`1.ToList()
   at SqlSugar.QueryableProvider`1.First()

我推测 SqlSugar 将我的自定义类型 PgVector 当作实体类型读取了,可是我已经在 Npgsql 设置过自定义类型了,这时 DataReader 返回的应该就是 PgVector 实例,不需要 SqlSugar 转换了

ZeekoZhu commented 1 year ago

我推测 SqlSugar 将我的自定义类型 PgVector 当作实体类型读取了,可是我已经在 Npgsql 设置过自定义类型了,这时 DataReader 返回的应该就是 PgVector 实例,不需要 SqlSugar 转换了

尝试使用实体类型作为 Select 的值

    var item = _db.Queryable<VectorItem>()
      .Select(x => new VectorItem { Vector = SqlFunc.AggregateAvg(x.Vector), Id = 0 })
      .First();

结果生成的 sql 中缺少了聚合列。。。

image

DotNetNext commented 1 year ago

单个字段识别不了自定义类型 new class(){ 有特性的属性=SqlFunc.AggregateAvg(x.Vector)}

这样就能识别

DotNetNext commented 1 year ago

我来验证一下

DotNetNext commented 1 year ago
var item = _db.Queryable<VectorItem>()
  .Select(x => new VectorItem { Vector = SqlFunc.AggregateAvg(x.Vector), Id = 0 })
  .First();

你这个应该把ID去掉,如果2个字段需要GROUP BY

ZeekoZhu commented 1 year ago

感谢,已经没有问题了