我正在使用ServiceStack使用第三方WebApi。许多端点根据通用模式对结果进行分页。
示例JSON:
{
"count": 23,
"pageSize": 10,
"pageNumber": 1,
"_embedded": {
"people": [
{
"id": 1,
"name": "Jean-Luc Picard"
},
{
"id": 2,
"name": "William T. Riker"
},
[...]
]
}
}
由于每个分页请求和响应DTO都具有公共属性,因此我创建了抽象类来保持这些DTO干燥。
public abstract class PaginatedRequest<TResponseDto, TEmbeddedResponseDto> : IReturn<TResponseDto>
where TResponseDto : PaginatedResponse<TEmbeddedResponseDto>
{
public int PageSize { get; set; }
public int PageNumber { get; set; }
public List<TEmbeddedResponseDto> Paginate()
{
var list = new List<TEmbeddedResponseDto>();
// Page through results, and add to list until all have been received.
return list;
}
}
public abstract class PaginatedResponse<T>
{
public int Count { get; set; }
public int PageSize { get; set; }
public int PageNumber { get; set; }
public Dictionary<string, List<T>> _embedded { get; set; }
}
这些抽象类分别由请求和响应DTO继承。
public class PersonDto
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class PeopleDto : PaginatedResponse<PersonDto> { }
[Route("/people/{id}")]
public class GetPerson : IReturn<PersonDto>
{
public int ID { get; set; }
}
[Route("/people")]
public class GetPeople : PaginatedRequest<PeopleDto, PersonDto> { }
然后可以通过创建请求DTO并调用Paginate
函数来调用它。
public List<PersonDto> GetPeople() => new GetPeople().Paginate();
有没有更好的方法来实现此功能,同时保持DRY?这个实现是否有任何特定的缺点,我没有考虑过?我知道Inheritance in DTOs is Considered Harmful,但我认为abstract
基类可以避免这些特殊问题。
在提交之后,我认为将分页从抽象类中移出可能会减轻预期的问题,即DTO应该是免费实现的。
这可以作为我的类库中的方法实现,也可以作为JsonServiceClient
上的扩展方法(未显示)实现。
public List<TEmbeddedResponseDto> GetPaginated<TResponseDto, TEmbeddedResponseDto>(PaginatedRequest<TResponseDto, TEmbeddedResponseDto> request)
where TResponseDto : PaginatedResponse<TEmbeddedResponseDto>
{
var list = new List<TEmbeddedResponseDto>();
// Page through results, and add to list until all have been received.
return list;
}
public List<PersonDto> GetPeople() => GetPaginated(new GetPeople());
答案 0 :(得分:1)
您是否看过AutoQuery,为人们创建AutoQuery服务的整个Request DTO是:
[Route("/people")]
public class QueryPeople : QueryDb<Person> {}
AutoQuery将自动为其提供Service实现,以创建一个允许您查询和翻页Person
表的服务。
您可以分享:
/people?take=100 # first 100 results
/people?skip=100&take=100 # next 100 results
请参阅Include Total,了解如何始终包含总计或将其包含在?Include=Total
的特设请求中。
您还可以使用AutoQuery UI获取AutoQuery服务的即时用户界面,该界面可让您浏览结果并向您显示所使用的AutoQuery网址。
实时AutoQuery查看器示例
如果您想手动执行此操作,请考虑使用相同的QueryResponse DTO all AutoQuery Services use。然后用OrmLite来实现它,看起来像:
public object Any(SearchPeople request)
{
var q = Db.From<Person>();
if (request.Age != null) //Query Example
q.Where(x => x.Age == request.Age.Value);
if (request.Skip != null)
q.Skip(request.Skip.Value);
if (request.Take != null)
q.Take(request.Take.Value);
return new QueryResponse<Person>
{
Offset = request.Skip ?? 0,
Results = db.Select<Person>(q),
Total = db.Count(Q),
}
}
另请阅读designing message-based APIs,其中建议使用一致的命名法并为合并者查询保留Get*
服务,例如通过主键提取并使用Search*
或Find*
使用其他参数过滤结果的服务;