How to: Retrieve Related Entities
This article is relevant to entity models that utilize the deprecated Visual Studio integration of Telerik Data Access. The current documentation of the Data Access framework is available here.
By default, the ServiceStack layers generated with Service Wizard allow you to retrieve the data only for the persistent properties of a given entity. This means that, the navigational properties are neither exposed in the DTO classes, nor any queries are issued for related objects.
This article will show you how to extend the generated service for a given entity, in order to retrieve its objects with all of their navigational properties.
When your ServiceStack layer exposes multiple related entities, make sure to avoid circular references between the corresponding DTO classes.
Suppose you have a model based on the Northwind sample database, and your ServiceStack layer exposes service methods for the following entities: Order, OrderDetail and Customer. To extend the service of the Order entity, to retrieve the OrderDetail objects, and the Customer object related to each order, you can execute the following steps:
-
In the DTOs project, add a class file called OrderDTO.partial.cs(.vb). In it file you will extend OrderDTO in a partial class. For example:
using System; using System.Collections.Generic; namespace EntitiesModelServicesDTOs.CoreDTOs { public partial class OrderDTO { public CustomerDTO Customer { get; set; } public IList<OrderDetailDTO> OrderDetails { get; set; } } }
Namespace CoreDTOs Partial Public Class OrderDTO Public Property Customer() As CustomerDTO Public Property OrderDetails() As List(Of OrderDetailDTO) End Class End Namespace
-
In the DTOs project, add a class file called OrderServiceMessages.partial.cs(.vb). In it you will add request and response service messages that will be used in the new service method. For example:
using System; using ServiceStack; using EntitiesModelServicesDTOs.CoreDTOs; using System.Collections.Generic; namespace EntitiesModelServicesDTOs.OrderServiceMessages { public partial class GetOrdersWithNavigation : IReturn<GetOrdersWithNavigationResponse> { } public partial class GetOrdersWithNavigationResponse { public IList<OrderDTO> Orders { get; set; } } }
Imports ServiceStack Imports EntitiesModelServicesDTOs.CoreDTOs Namespace OrderServiceMessages Partial Public Class GetOrdersWithNavigation Implements IReturn(Of IList(Of OrderDTO)) End Class Partial Public Class GetOrdersWithNavigationResponse Public Property Orders As IList(Of OrderDTO) End Class End Namespace
-
In the project which holds the code base of the ServiceStack layer, add a new class file called OrderRepository.partial.cs(.vb). In it you will extend the generated repository related to the Order entity. The code in this step will override the GetAll() method inherited from the base repository class, in order to apply the fetch strategy, which specifies the navigational properties that should be loaded. For example:
using ServiceStack.Model; using System.Collections.Generic; namespace ServiceStack.Demo.Repositories { public partial class OrderRepository { public IEnumerable<Order> GetAllWithNavigation() { this.fetchStrategy.LoadWith<Order>(o => o.OrderDetails); this.fetchStrategy.LoadWith<Order>(o => o.Customer); this.context.FetchStrategy = fetchStrategy; return base.GetAll(); } } }
Imports ServiceStack.Model Namespace Repositories Partial Public Class OrderRepository Public Function GetAllWithNavigation() As IEnumerable(Of Order) Me.fetchStrategy.LoadWith(Of Order)(Function(o) o.OrderDetails) Me.fetchStrategy.LoadWith(Of Order)(Function(o) o.Customer) Me.context.FetchStrategy = fetchStrategy Return MyBase.GetAll() End Function End Class End Namespace
-
In the project which holds the code base of the ServiceStack layer, add a new class file called OrderService.partial.cs(.vb). In it you will extend the service for the Order entity and its implementation. Depending on whether your code is on C# or on VB, you can choose from the following options:
-
For C# - In the OrderService.partial.cs file, add a partial interface, which extends the generated IOrderServiceImplementation with the signature of the new service method:
using System; using System.Linq; using System.Collections.Generic; using ServiceStack.Model; using EntitiesModelServicesDTOs.CoreDTOs; using EntitiesModelServicesDTOs.OrderServiceMessages; namespace ServiceStack.Demo { public partial interface IOrderServiceImplementation { GetOrdersWithNavigationResponse Get(GetOrdersWithNavigation request); } }
-
For VB - Open the OrderService.vb file and in the body of the ICategoryServiceImplementation interface, add the signature of the new service method:
Public Interface IOrderServiceImplementation Property ServiceReference() As Service Function [Get](request As QueryOrders) As QueryResponse(Of OrderDTO) Function [Get](request As GetOrderById) As GetOrderByIdResponse Function Post(request As AddOrder) As HttpResult Sub Put(request As UpdateOrder) Sub Delete(request As DeleteOrder) ' 'The definition of the new service method. Function [Get](request As GetOrdersWithNavigation) As GetOrdersWithNavigationResponse End Interface
On VB, if you re-generate the service layer, you will have to repeat this step.
-
-
In the OrderService.partial.cs(.vb), extend the OrderServiceImplementation class with the actual implementation of the new service method:
public partial class OrderServiceImplementation { public GetOrdersWithNavigationResponse Get(GetOrdersWithNavigation request) { List<ServiceStack.Model.Order> orders = this.repository .GetAllWithNavigation() .ToList(); GetOrdersWithNavigationResponse responseOrders = new GetOrdersWithNavigationResponse(); List<OrderDTO> orderDtos = new List<OrderDTO>(); foreach (ServiceStack.Model.Order order in orders) { List<OrderDetailDTO> details = new List<OrderDetailDTO>(); foreach (ServiceStack.Model.OrderDetail detail in order.OrderDetails) { OrderDetailDTO detailDTO = detail.ConvertTo<OrderDetailDTO>(); details.Add(detailDTO); } OrderDTO orderDTO = order.ConvertTo<OrderDTO>(); orderDTO.OrderDetails = details; orderDtos.Add(orderDTO); } responseOrders.Orders = orderDtos; return responseOrders; } }
Imports EntitiesModelServicesDTOs.OrderServiceMessages Imports EntitiesModelServicesDTOs.CoreDTOs Partial Public Class OrderServiceImplementation Public Function [Get](ByVal request As GetOrdersWithNavigation) As GetOrdersWithNavigationResponse _ Implements IOrderServiceImplementation.Get Dim orders As List(Of ServiceStack.Model.Order) = Me._repository _ .GetAllWithNavigation() _ .ToList() Dim responseOrders As New GetOrdersWithNavigationResponse Dim orderDtos As New List(Of OrderDTO)() For Each order As ServiceStack.Model.Order In orders Dim details As New List(Of OrderDetailDTO)() For Each detail As ServiceStack.Model.OrderDetail In order.OrderDetails Dim detailDTO As OrderDetailDTO = detail.ConvertTo(Of OrderDetailDTO)() details.Add(detailDTO) Next detail Dim _orderDTO As OrderDTO = order.ConvertTo(Of OrderDTO)() _orderDTO.OrderDetails = details orderDtos.Add(_orderDTO) Next order responseOrders.Orders = orderDtos Return responseOrders End Function End Class
-
In the OrderService.partial.cs(.vb) file, extend the OrderService class with the call to the new service method:
public partial class OrderService { public GetOrdersWithNavigationResponse Get(GetOrdersWithNavigation request) { return this.orderSvcImp.Get(request); } }
Partial Public Class OrderService Public Function [Get](ByVal request As GetOrdersWithNavigation) As GetOrdersWithNavigationResponse Return Me._orderSvcImp.Get(request) End Function End Class
-
In the web application that holds you ServiceStack layer, add a new class file called AppHost.partial.cs(.vb). In it you will extend the ServiceStack service host, in order to register a route for the new service method. This requires you to implement the RegisterCustomRoutes method. For example:
using System; using EntitiesModelServicesDTOs.OrderServiceMessages; namespace ServiceStack.Demo { public partial class AppHost { partial void RegisterCustomRoutes(ref bool shouldRegisterDefaultRoutes) { shouldRegisterDefaultRoutes = true; this.Routes.Add<GetOrdersWithNavigation>(OrderService.OrdersBasePath + "/WithNavigation", ApplyTo.Get); } } }
Imports EntitiesModelServicesDTOs.OrderServiceMessages Partial Public Class AppHost Private Sub RegisterCustomRoutes(ByRef shouldRegisterDefaultRoutes As Boolean) shouldRegisterDefaultRoutes = True Me.Routes.Add(Of GetOrdersWithNavigation)(OrderService.OrdersBasePath & _ "/WithNavigation", ApplyTo.Get) End Sub End Class
Build your solution and start the web application for a test.
-
The new service method can be tested on a URL similar to the following:
http://localhost:<port_number>/Orders/WithNavigation