将 ServiceStack.Net 从 4.0.39 升级到 4.5.6 后,OrmLite 模型未填充
OrmLite Model not populating after upgrading ServiceStack.Net from 4.0.39 to 4.5.6
将 ServiceStack.Net 从 4.0.39 升级到 4.5.6 后,我们发现有些情况下模型未被填充,但它确实包含正确数量的记录。每条记录的属性都是默认值。恢复到 4.0.39 时一切正常。
我们想知道是否有需要更新的更改?我们没有注意到文档中的任何内容,用法看起来与示例相似。
如果不知道答案,下一个最简单的找出正在发生的事情的方法是什么? (看起来可能需要下载源代码并编译和引用新的 4.5.6 OrmLite,但是有没有更简单的选择?)
- 它适用于其他非常相似的情况(# of params 和 table names 都是不同的)。我们没有注意到案例之间的任何差异。有效和无效的案例按顺序执行。改变顺序不会改变结果。
- 在数据库客户端 (psql) 中执行 sql returns 值,因此有要填充的值。
- 没有抛出任何错误。
- 正在使用 Postgres 方言。
这是一个简单的例子。
https://gist.github.com/anonymous/bb514062a7d97b0ff4b0a546ef315765
using System;
using NUnit.Framework;
using ServiceStack;
using ServiceStack.Configuration;
using ServiceStack.DataAnnotations;
using ServiceStack.OrmLite;
namespace MyCompany.Tests
{
public class OrmLiteModelArrayTests
{
[Alias("color")]
public class ColorModel
{
public string Color { get; set; }
public string Value { get; set; }
}
public class ColorJsonModel
{
public int Id { get; set; }
public string ColorJson { get; set; }
}
[Test]
public void test_model_with_array_to_json()
{
OrmLiteConfig.DialectProvider = PostgreSqlDialect.Provider;
var testingConn = ConfigUtils.GetConnectionString("testing");
using (var db = testingConn.OpenDbConnection())
{
db.DropAndCreateTable();
db.Insert(new ColorModel { Color = "red", Value = "#f00" });
db.Insert(new ColorModel { Color = "green", Value = "#0f0" });
db.Insert(new ColorModel { Color = "blue", Value = "#00f" });
db.Insert(new ColorModel { Color = "cyan", Value = "#0ff" });
db.Insert(new ColorModel { Color = "magenta", Value = "#f0f" });
db.Insert(new ColorModel { Color = "yellow", Value = "#ff0" });
db.Insert(new ColorModel { Color = "black", Value = "#000" });
const string sql = @"SELECT 1::integer AS id
, json_agg(color.*) AS color_json
FROM color;";
var results = db.Select<ColorJsonModel>(sql);
Assert.That(results.Count, Is.EqualTo(1));
foreach (var result in results)
{
Console.WriteLine("{0}".Fmt(result.ColorJson));
Assert.That(result.Id, Is.EqualTo(1));
Assert.That(result.ColorJson, Is.Not.Null);
}
}
}
[Test]
public void test_model_with_array_and_json()
{
OrmLiteConfig.DialectProvider = PostgreSqlDialect.Provider;
var testingConn = ConfigUtils.GetConnectionString("testing");
using (var db = testingConn.OpenDbConnection())
{
db.DropAndCreateTable<ColorModel>();
db.Insert(new ColorModel { Color = "red", Value = "#f00" });
db.Insert(new ColorModel { Color = "green", Value = "#0f0" });
db.Insert(new ColorModel { Color = "blue", Value = "#00f" });
db.Insert(new ColorModel { Color = "cyan", Value = "#0ff" });
db.Insert(new ColorModel { Color = "magenta", Value = "#f0f" });
db.Insert(new ColorModel { Color = "yellow", Value = "#ff0" });
db.Insert(new ColorModel { Color = "black", Value = "#000" });
// SQL contains array and json aggs.
// We usually have ARRAY fields defined in the db, but when
// retrieved we json-ize them. In otherwords the array exists in the tables/views.
// We use SELECT.* which would contain the ARRAY field.
// Array fields are not used in any of our models and should not cause the other
// fields in the model to not be populated.
const string sql = @"SELECT 1::integer AS id
, json_agg(color.*) AS color_json
, array_agg(color.*) AS color_array
FROM color;";
var results = db.Select<ColorJsonModel>(sql);
Assert.That(results.Count, Is.EqualTo(1));
foreach (var result in results)
{
Console.WriteLine("{0}".Fmt(result.ColorJson));
Assert.That(result.Id, Is.EqualTo(1));
Assert.That(result.ColorJson, Is.Not.Null);
}
}
}
}
}
更新:
更奇怪的是,我们发现了一个示例,其中总共返回了 7 条记录,但 7 条记录中有 6 条具有默认空值,最后一条(第 7 条)记录被完全填充。
更新 2:
问题是处理 SELECT *
中包含但未在 C# 模型中使用的 ARRAY 字段。
描述中的示例代码无法编译,但我已经使用最新版本的 OrmLite 测试了最接近的工作版本,并且按预期工作:
[Alias("my_table")]
public class MyModel
{
public int MyId { get; set; }
public int BlahId { get; set; }
public string Name { get; set; }
}
using (var db = OpenDbConnection())
{
db.DropAndCreateTable<MyModel>();
db.Insert(new MyModel { MyId = 1, BlahId = 2, Name = "foo" });
db.Insert(new MyModel { MyId = 2, BlahId = 3, Name = "bar" });
var results = db.GetMyModels(1, 2);
Assert.That(results.Count, Is.EqualTo(1));
Assert.That(results[0].MyId, Is.EqualTo(1));
Assert.That(results[0].BlahId, Is.EqualTo(2));
Assert.That(results[0].Name, Is.EqualTo("foo"));
}
public static class CustomSqlExtensions
{
public static List<MyModel> GetMyModels(this IDbConnection db, int myId, int blahId)
{
const string sql = @"SELECT * FROM my_table WHERE my_id = @myId AND blah_id = @blahId";
return db.Select<MyModel>(sql, new { myId, blahId });
}
}
虽然它不会在这里产生影响,但在使用自定义 SQL 时,您应该像 SqlList
一样使用 Custom SQL APIs,例如:
return db.SqlList<MyModel>(sql, new { myId, blahId });
const string sql = @"SELECT 1::integer AS id
, json_agg(color.*) AS color_json
, array_agg(color.*) AS color_array
FROM color;";
var results = db.Select<ColorJsonModel>(sql);
在 Npgsql 中使用未知的 PostgreSQL 类型
是由于 Npgsql 不知道 array_agg
类型是什么所以会抛出异常:
字段 'color_array' 具有 Npgsql 当前未知的类型 (OID 101487)
尝试读取行数据集及其 GetValues()
批次 API 时。我添加了一个 fallback in this commit,如果 GetValues()
批处理 API 失败,它将回退到单独的读取读取,这将解决这个问题。
但是这会抛出一个不是最佳的异常,您可以告诉 OrmLite 避免使用批处理 GetValues()
API 完全通过告诉它始终使用单独的字段提取:
OrmLiteConfig.DeoptimizeReader = true;
这将避免尝试使用 GetValues() 并避免其异常。
将 ServiceStack.Net 从 4.0.39 升级到 4.5.6 后,我们发现有些情况下模型未被填充,但它确实包含正确数量的记录。每条记录的属性都是默认值。恢复到 4.0.39 时一切正常。
我们想知道是否有需要更新的更改?我们没有注意到文档中的任何内容,用法看起来与示例相似。
如果不知道答案,下一个最简单的找出正在发生的事情的方法是什么? (看起来可能需要下载源代码并编译和引用新的 4.5.6 OrmLite,但是有没有更简单的选择?)
- 它适用于其他非常相似的情况(# of params 和 table names 都是不同的)。我们没有注意到案例之间的任何差异。有效和无效的案例按顺序执行。改变顺序不会改变结果。
- 在数据库客户端 (psql) 中执行 sql returns 值,因此有要填充的值。
- 没有抛出任何错误。
- 正在使用 Postgres 方言。
这是一个简单的例子。 https://gist.github.com/anonymous/bb514062a7d97b0ff4b0a546ef315765
using System;
using NUnit.Framework;
using ServiceStack;
using ServiceStack.Configuration;
using ServiceStack.DataAnnotations;
using ServiceStack.OrmLite;
namespace MyCompany.Tests
{
public class OrmLiteModelArrayTests
{
[Alias("color")]
public class ColorModel
{
public string Color { get; set; }
public string Value { get; set; }
}
public class ColorJsonModel
{
public int Id { get; set; }
public string ColorJson { get; set; }
}
[Test]
public void test_model_with_array_to_json()
{
OrmLiteConfig.DialectProvider = PostgreSqlDialect.Provider;
var testingConn = ConfigUtils.GetConnectionString("testing");
using (var db = testingConn.OpenDbConnection())
{
db.DropAndCreateTable();
db.Insert(new ColorModel { Color = "red", Value = "#f00" });
db.Insert(new ColorModel { Color = "green", Value = "#0f0" });
db.Insert(new ColorModel { Color = "blue", Value = "#00f" });
db.Insert(new ColorModel { Color = "cyan", Value = "#0ff" });
db.Insert(new ColorModel { Color = "magenta", Value = "#f0f" });
db.Insert(new ColorModel { Color = "yellow", Value = "#ff0" });
db.Insert(new ColorModel { Color = "black", Value = "#000" });
const string sql = @"SELECT 1::integer AS id
, json_agg(color.*) AS color_json
FROM color;";
var results = db.Select<ColorJsonModel>(sql);
Assert.That(results.Count, Is.EqualTo(1));
foreach (var result in results)
{
Console.WriteLine("{0}".Fmt(result.ColorJson));
Assert.That(result.Id, Is.EqualTo(1));
Assert.That(result.ColorJson, Is.Not.Null);
}
}
}
[Test]
public void test_model_with_array_and_json()
{
OrmLiteConfig.DialectProvider = PostgreSqlDialect.Provider;
var testingConn = ConfigUtils.GetConnectionString("testing");
using (var db = testingConn.OpenDbConnection())
{
db.DropAndCreateTable<ColorModel>();
db.Insert(new ColorModel { Color = "red", Value = "#f00" });
db.Insert(new ColorModel { Color = "green", Value = "#0f0" });
db.Insert(new ColorModel { Color = "blue", Value = "#00f" });
db.Insert(new ColorModel { Color = "cyan", Value = "#0ff" });
db.Insert(new ColorModel { Color = "magenta", Value = "#f0f" });
db.Insert(new ColorModel { Color = "yellow", Value = "#ff0" });
db.Insert(new ColorModel { Color = "black", Value = "#000" });
// SQL contains array and json aggs.
// We usually have ARRAY fields defined in the db, but when
// retrieved we json-ize them. In otherwords the array exists in the tables/views.
// We use SELECT.* which would contain the ARRAY field.
// Array fields are not used in any of our models and should not cause the other
// fields in the model to not be populated.
const string sql = @"SELECT 1::integer AS id
, json_agg(color.*) AS color_json
, array_agg(color.*) AS color_array
FROM color;";
var results = db.Select<ColorJsonModel>(sql);
Assert.That(results.Count, Is.EqualTo(1));
foreach (var result in results)
{
Console.WriteLine("{0}".Fmt(result.ColorJson));
Assert.That(result.Id, Is.EqualTo(1));
Assert.That(result.ColorJson, Is.Not.Null);
}
}
}
}
}
更新:
更奇怪的是,我们发现了一个示例,其中总共返回了 7 条记录,但 7 条记录中有 6 条具有默认空值,最后一条(第 7 条)记录被完全填充。
更新 2:
问题是处理 SELECT *
中包含但未在 C# 模型中使用的 ARRAY 字段。
描述中的示例代码无法编译,但我已经使用最新版本的 OrmLite 测试了最接近的工作版本,并且按预期工作:
[Alias("my_table")]
public class MyModel
{
public int MyId { get; set; }
public int BlahId { get; set; }
public string Name { get; set; }
}
using (var db = OpenDbConnection())
{
db.DropAndCreateTable<MyModel>();
db.Insert(new MyModel { MyId = 1, BlahId = 2, Name = "foo" });
db.Insert(new MyModel { MyId = 2, BlahId = 3, Name = "bar" });
var results = db.GetMyModels(1, 2);
Assert.That(results.Count, Is.EqualTo(1));
Assert.That(results[0].MyId, Is.EqualTo(1));
Assert.That(results[0].BlahId, Is.EqualTo(2));
Assert.That(results[0].Name, Is.EqualTo("foo"));
}
public static class CustomSqlExtensions
{
public static List<MyModel> GetMyModels(this IDbConnection db, int myId, int blahId)
{
const string sql = @"SELECT * FROM my_table WHERE my_id = @myId AND blah_id = @blahId";
return db.Select<MyModel>(sql, new { myId, blahId });
}
}
虽然它不会在这里产生影响,但在使用自定义 SQL 时,您应该像 SqlList
一样使用 Custom SQL APIs,例如:
return db.SqlList<MyModel>(sql, new { myId, blahId });
const string sql = @"SELECT 1::integer AS id
, json_agg(color.*) AS color_json
, array_agg(color.*) AS color_array
FROM color;";
var results = db.Select<ColorJsonModel>(sql);
在 Npgsql 中使用未知的 PostgreSQL 类型
是由于 Npgsql 不知道 array_agg
类型是什么所以会抛出异常:
字段 'color_array' 具有 Npgsql 当前未知的类型 (OID 101487)
尝试读取行数据集及其 GetValues()
批次 API 时。我添加了一个 fallback in this commit,如果 GetValues()
批处理 API 失败,它将回退到单独的读取读取,这将解决这个问题。
但是这会抛出一个不是最佳的异常,您可以告诉 OrmLite 避免使用批处理 GetValues()
API 完全通过告诉它始终使用单独的字段提取:
OrmLiteConfig.DeoptimizeReader = true;
这将避免尝试使用 GetValues() 并避免其异常。