ServiceStack - [参考]还是[忽略]?
ServiceStack - [Reference] or [Ignore]?
我们有一个 DTO - Employee - 有许多(> 20)个相关的 DTO 和 DTO 集合。由于 "size of returned JSON" 原因,我们将这些关系标记为 [忽略]。然后由客户来填充他们想要使用其他 REST 调用的任何相关 DTO。
我们已经尝试了一些方法来满足客户希望获得一些相关员工信息但不是全部的愿望:
我们创建了一个新的 DTO - EmployeeLite - 它具有使用 "RelatedTableNameRelatedFieldName" 方法定义的请求最多的字段,并使用了 QueryBase 重载并且运行良好。
我们还尝试将 属性 添加到请求 DTO - "References" - 这是客户端想要填充的相关 DTO 的逗号分隔列表。然后我们迭代响应并使用相关的 DTO 或列表填充每个员工。迭代大型列表时的性能问题。
我们想知道是否有针对我们正在尝试做的事情的建议方法?
感谢您提出任何建议。
更新:
这是我们请求 DTO 的一部分:
[Route("/employees", "GET")]
public class FindEmployeesRequest : QueryDb<Employee> {
public int? ID { get; set; }
public int[] IDs { get; set; }
public string UserID { get; set; }
public string LastNameStartsWith { get; set; }
public DateTime[] DateOfBirthBetween { get; set; }
public DateTime[] HireDateBetween { get; set; }
public bool? IsActive { get; set; }
}
服务没有代码(使用 QueryDb 自动生成),所以我添加了一些代码来尝试 "merge" 方法:
public object Get(FindEmployeesRequest request) {
var query = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<Employee> response = AutoQuery.Execute(request, query);
if (response.Total > 0) {
List<Clerkship> clerkships = Db.Select<Clerkship>();
response.Results.Merge(clerkships);
}
return response;
}
失败 Could not find Child Reference for 'Clerkship' on Parent 'Employee'
因为在 Employee 中我们有:
[Ignore]
public List<Clerkship> Clerkships { get; set; }
我们这样做是因为我们不希望每个请求都 "Clerkships"。如果我将 [Ignore]
更改为 [Reference]
我不需要服务中的上述代码 - 列表会自动出现。所以似乎 .Merge
只适用于我们不想做的 [Reference]
。
我不确定如何在 AutoQuery 服务中使用 "Custom Load References" 方法。而且,AFAIKT,"Custom Fields" 方法不能用于相关的 DTO,只能用于基础 table.
中的字段
更新 2:
LoadSelect
和 include[]
对我们来说效果很好。我们现在正在尝试涵盖在查询字符串中使用 ?fields=
但客户端未请求相关 DTO 的 ID 字段的情况:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
.
.
.
[References(typeof(Department))]
public int DepartmentID { get; set; }
.
.
.
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
.
.
.
}
所以,对于请求
/employees?fields=id,departmentid
我们会得到部门的回复。但是对于请求
/employees?fields=id
我们不会在响应中得到部门。
我们正在尝试 "quietly fix" 通过修改 query.SelectExpression
并在执行 Db.LoadSelect
之前将 , "Employee"."DepartmentID"
添加到 SELECT
来为请求者做到这一点。调试显示 query.SelectExpression
正在被修改,但是根据 SQL Profiler,"Employee"."DepartmentID"
没有被选中。
我们还应该做些什么来将 "Employee"."DepartmentID"
添加到 SELECT 中吗?
谢谢。
更新 3:
员工 table 具有三个 1:1 关系 - EmployeeType、Department 和 Title:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
[References(typeof(EmployeeType))]
public int EmployeeTypeID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
[References(typeof(Title))]
public int TitleID { get; set; }
.
.
.
}
public class EmployeeType {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
}
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
[Reference]
public List<Title> Titles { get; set; }
}
public class Title {
[PrimaryKey]
public int ID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
public string Name { get; set; }
}
4.0.55 的最新更新允许:
/employees?fields=employeetype,department,title
我取回了所有员工 table 字段加上三个相关的 DTO - 有一件奇怪的事情 - 员工的 ID 字段填充了员工的 TitleID 值(我想我们以前见过这个?)。
此请求修复了该异常:
/employees?fields=id,employeetypeid,employeetype,departmentid,department,titleid,title
但我丢失了所有其他员工字段。
这听起来像是一个 "have your cake and eat it too" 请求,但有没有办法获取所有的员工字段和选择性相关的 DTO?类似于:
/employees?fields=*,employeetype,department,title
AutoQuery Customizable Fields
不确定这是否相关,但 AutoQuery 内置了对 Customizing which fields to return 和 ?fields=Field1,Field2
选项的支持。
Merge disconnected POCO Results
由于您没有提供任何源代码,因此不清楚您要实现的目标或现有解决方案的低效率所在,但您不想做任何 N+1
SELECT查询。如果你是,看看你如何 merge disconnected POCO results 在一起,这将使你可以根据使用 OrmLite 引用定义的关系合并来自单独查询的结果,例如下面的示例使用 2 个不同的查询来连接客户及其订单:
//Select Customers who've had orders with Quantities of 10 or more
List<Customer> customers = db.Select<Customer>(q =>
q.Join<Order>()
.Where<Order>(o => o.Qty >= 10)
.SelectDistinct());
//Select Orders with Quantities of 10 or more
List<Order> orders = db.Select<Order>(o => o.Qty >= 10);
customers.Merge(orders); // Merge disconnected Orders with their related Customers
Custom Load References
您可以通过在调用 OrmLite 的 Load*
API 时指定它们来有选择地控制 OrmLite 应该加载哪些引用,例如:
var customerWithAddress = db.LoadSingleById<Customer>(customer.Id,
include: new[] { "PrimaryAddress" });
在 AutoQuery 中使用自定义加载引用
您可以通过在自定义 AutoQuery 实现中使用 Db.Select
而不是 Db.LoadSelect
来自定义 AutoQuery 请求,使其不 return 任何引用,例如:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.Select(q),
Total = (int)Db.Count(q),
};
return response;
}
同样,如果您只想选择性地加载 1 个或多个引用,您可以将 LoadSelect 更改为传入一个 include:
数组,其中仅包含您想要包含的引用字段,例如:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.LoadSelect(q, include:new []{ "Clerkships" }),
Total = (int)Db.Count(q),
};
return response;
}
我们有一个 DTO - Employee - 有许多(> 20)个相关的 DTO 和 DTO 集合。由于 "size of returned JSON" 原因,我们将这些关系标记为 [忽略]。然后由客户来填充他们想要使用其他 REST 调用的任何相关 DTO。
我们已经尝试了一些方法来满足客户希望获得一些相关员工信息但不是全部的愿望:
我们创建了一个新的 DTO - EmployeeLite - 它具有使用 "RelatedTableNameRelatedFieldName" 方法定义的请求最多的字段,并使用了 QueryBase 重载并且运行良好。
我们还尝试将 属性 添加到请求 DTO - "References" - 这是客户端想要填充的相关 DTO 的逗号分隔列表。然后我们迭代响应并使用相关的 DTO 或列表填充每个员工。迭代大型列表时的性能问题。
我们想知道是否有针对我们正在尝试做的事情的建议方法?
感谢您提出任何建议。
更新:
这是我们请求 DTO 的一部分:
[Route("/employees", "GET")]
public class FindEmployeesRequest : QueryDb<Employee> {
public int? ID { get; set; }
public int[] IDs { get; set; }
public string UserID { get; set; }
public string LastNameStartsWith { get; set; }
public DateTime[] DateOfBirthBetween { get; set; }
public DateTime[] HireDateBetween { get; set; }
public bool? IsActive { get; set; }
}
服务没有代码(使用 QueryDb 自动生成),所以我添加了一些代码来尝试 "merge" 方法:
public object Get(FindEmployeesRequest request) {
var query = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<Employee> response = AutoQuery.Execute(request, query);
if (response.Total > 0) {
List<Clerkship> clerkships = Db.Select<Clerkship>();
response.Results.Merge(clerkships);
}
return response;
}
失败 Could not find Child Reference for 'Clerkship' on Parent 'Employee'
因为在 Employee 中我们有:
[Ignore]
public List<Clerkship> Clerkships { get; set; }
我们这样做是因为我们不希望每个请求都 "Clerkships"。如果我将 [Ignore]
更改为 [Reference]
我不需要服务中的上述代码 - 列表会自动出现。所以似乎 .Merge
只适用于我们不想做的 [Reference]
。
我不确定如何在 AutoQuery 服务中使用 "Custom Load References" 方法。而且,AFAIKT,"Custom Fields" 方法不能用于相关的 DTO,只能用于基础 table.
中的字段更新 2:
LoadSelect
和 include[]
对我们来说效果很好。我们现在正在尝试涵盖在查询字符串中使用 ?fields=
但客户端未请求相关 DTO 的 ID 字段的情况:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
.
.
.
[References(typeof(Department))]
public int DepartmentID { get; set; }
.
.
.
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
.
.
.
}
所以,对于请求
/employees?fields=id,departmentid
我们会得到部门的回复。但是对于请求
/employees?fields=id
我们不会在响应中得到部门。
我们正在尝试 "quietly fix" 通过修改 query.SelectExpression
并在执行 Db.LoadSelect
之前将 , "Employee"."DepartmentID"
添加到 SELECT
来为请求者做到这一点。调试显示 query.SelectExpression
正在被修改,但是根据 SQL Profiler,"Employee"."DepartmentID"
没有被选中。
我们还应该做些什么来将 "Employee"."DepartmentID"
添加到 SELECT 中吗?
谢谢。
更新 3:
员工 table 具有三个 1:1 关系 - EmployeeType、Department 和 Title:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
[References(typeof(EmployeeType))]
public int EmployeeTypeID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
[References(typeof(Title))]
public int TitleID { get; set; }
.
.
.
}
public class EmployeeType {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
}
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
[Reference]
public List<Title> Titles { get; set; }
}
public class Title {
[PrimaryKey]
public int ID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
public string Name { get; set; }
}
4.0.55 的最新更新允许:
/employees?fields=employeetype,department,title
我取回了所有员工 table 字段加上三个相关的 DTO - 有一件奇怪的事情 - 员工的 ID 字段填充了员工的 TitleID 值(我想我们以前见过这个?)。
此请求修复了该异常:
/employees?fields=id,employeetypeid,employeetype,departmentid,department,titleid,title
但我丢失了所有其他员工字段。
这听起来像是一个 "have your cake and eat it too" 请求,但有没有办法获取所有的员工字段和选择性相关的 DTO?类似于:
/employees?fields=*,employeetype,department,title
AutoQuery Customizable Fields
不确定这是否相关,但 AutoQuery 内置了对 Customizing which fields to return 和 ?fields=Field1,Field2
选项的支持。
Merge disconnected POCO Results
由于您没有提供任何源代码,因此不清楚您要实现的目标或现有解决方案的低效率所在,但您不想做任何 N+1
SELECT查询。如果你是,看看你如何 merge disconnected POCO results 在一起,这将使你可以根据使用 OrmLite 引用定义的关系合并来自单独查询的结果,例如下面的示例使用 2 个不同的查询来连接客户及其订单:
//Select Customers who've had orders with Quantities of 10 or more
List<Customer> customers = db.Select<Customer>(q =>
q.Join<Order>()
.Where<Order>(o => o.Qty >= 10)
.SelectDistinct());
//Select Orders with Quantities of 10 or more
List<Order> orders = db.Select<Order>(o => o.Qty >= 10);
customers.Merge(orders); // Merge disconnected Orders with their related Customers
Custom Load References
您可以通过在调用 OrmLite 的 Load*
API 时指定它们来有选择地控制 OrmLite 应该加载哪些引用,例如:
var customerWithAddress = db.LoadSingleById<Customer>(customer.Id,
include: new[] { "PrimaryAddress" });
在 AutoQuery 中使用自定义加载引用
您可以通过在自定义 AutoQuery 实现中使用 Db.Select
而不是 Db.LoadSelect
来自定义 AutoQuery 请求,使其不 return 任何引用,例如:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.Select(q),
Total = (int)Db.Count(q),
};
return response;
}
同样,如果您只想选择性地加载 1 个或多个引用,您可以将 LoadSelect 更改为传入一个 include:
数组,其中仅包含您想要包含的引用字段,例如:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.LoadSelect(q, include:new []{ "Clerkships" }),
Total = (int)Db.Count(q),
};
return response;
}