ServiceStack.Northwind这个项目中提供了三表关联操作和缓存方式操作数据的示例。
主要的服务文件
CustomersService.cs :查询客户列表
OrdersService.cs : 查询一组订单以及和该组订单相关的订单项,其中每个订单包含客户信息和具体商品详情,共涉及到三个表。
CustomerDetailsService.cs 一个客户及其订单,被前一个服务(OrdersService)调用。
CachedServices.cs 使用缓存方式,对上面三个服务进行封装,缓存使用的MemoryCacheClient。
ServiceStack的缓存支持多种形式存储,包括Memory,Redis,MemoryCached,SQLAlchemy等,但是仅用于缓存自身的服务,并不是通用功能的缓存,所以使用范围很有限。
CustomersService.cs的代码,获取所有用户的列表,这个功能很简单,不加说明了。
public CustomersResponse Get(Customers request) { var customers = Db.Select<Customer>(); return new CustomersResponse { Customers = customers }; }
OrdersService.cs 的代码,用来获取多组订单信息,注意是多组,使用了.net集合处理的一些功能。这个服务先获取一组或一个订单,再根据订单获取订单详情信息,获取订单详情的时候根据订单ID对订单详情数据进行分组。
public class OrdersService : ServiceStack.ServiceInterface.Service { //指定分页式每页几条记录 private const int PageCount = 8; public object Get(Orders request) { //获取一组或一个订单 var orders = request.CustomerId.IsNullOrEmpty() ? Db.Select<Order>(order => order.OrderByDescending(o => o.OrderDate)) .Skip((request.Page.GetValueOrDefault(1) - 1)*PageCount) .Take(PageCount) .ToList() : Db.Select<Order>(order => order.Where(o => o.CustomerId == request.CustomerId).OrderByDescending(o => o.CustomerId)); if (orders.Count == 0) return new OrdersResponse(); //根据订单获取订单详情信息,并据订单ID对订单详情数据进行分组 var orderDetails = Db.Select<OrderDetail>(detail => Sql.In(detail.OrderId, orders.ConvertAll(x => x.Id))); var orderDetailsLookup = orderDetails.ToLookup(o => o.OrderId); var customerOrders = orders.ConvertAll(o => new CustomerOrder { Order = o, OrderDetails = orderDetailsLookup[o.Id].ToList() }); return new OrdersResponse { Results = customerOrders }; } }
分步说明:1 订单列表
获取订单信息,判断传入的 CustomerId 是否为空,如果CustomerId为空,执行分页方式查询订单的列表,分页的语法和EF中是相同的;如果有CustomerId,就会执行获取一条订单的操作。
var orders = request.CustomerId.IsNullOrEmpty() ? Db.Select<Order>(order => order.OrderByDescending(o => o.OrderDate)) .Skip((request.Page.GetValueOrDefault(1) - 1)*PageCount) .Take(PageCount) .ToList() : Db.Select<Order>(order => order.Where(o => o.CustomerId == request.CustomerId).OrderByDescending(o => o.CustomerId));
根据获取的OrderId的列表,获取订单详情列表。
var orderDetails = Db.Select<OrderDetail>(detail => Sql.In(detail.OrderId, orders.ConvertAll(x => x.Id)));
其中
orders.ConvertAll(x => x.Id)
的内容如下:
这个表达式
detail => Sql.In(detail.OrderId, orders.ConvertAll(x => x.Id))
实现的效果和SQL中的In子句对应,不过传入的参数有一点差异,第一个参数detail.OrderId指定字段名,第二个参数需要是一个整形值ID的列表,如上图跟踪时的oid的结构。
分步说明:2 分组的订单详情列表
在这个示例中 orderDetails 变量查询后获得了44项数据。下一步根据OrderId对获得的这44项数据进行分组。
var orderDetailsLookup = orderDetails.ToLookup(o => o.OrderId);
跟踪的结构如下图:
可以看到根据8个OrderId, 将44个订单详情数据分组成8组,并且是可以根据OrderId为索引,获取其中某一组的信息(在下一步我们可以看到这个过程)。
(ToLookup是.net集合中内置的一个函数,对集合中数据根据某一个字段进行分组)
分步说明:3 将订单列表和分组的详情列表组合
现在我们获得了8个订单项,还有44个根据订单的ID分组好的详情列表,下面我们把每一条订单的数据和对应的订单详情组合起来。
通过ConvertAll函数,逐项添加CustomerOrder,每个CustomerOrder包含有一个订单数据,和一组和该项订单相关的订单详情项。
var customerOrders = orders.ConvertAll(o => new CustomerOrder { Order = o, OrderDetails = orderDetailsLookup[o.Id].ToList() });
跟踪的结果如下:
有8个订单项,每个订单包含有数量不等的订单详情项。也就是一个订单记录对应了多个购买的商品。
源代码下载: http://down.51cto.com/data/1976805