How to: Extend A Service With A New Method
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.
This topic describes how to extend the generated ServiceStack service with custom service methods. Suppose you have a solution in which the Category entity (based on the Northwind sample database) is exposed through ServiceStack with the help of Service Wizard. By default, the CRUD operations for Category will be supported by the five service methods defined in the CategoryService class. Suppose that your scenario requires you to have a service method, which will retrieve a category by its name. To implement it, you can use one of the following approach:
Do not modify the original source files generated by the Service Wizard. If you re-generate your ServiceStack layer later, all changes will be lost. Instead, you can use partial types and new class files for your custom code.
- In the DTO project, add a class file called CategoryServiceMessages.partial.cs(.vb). This file will hold the request and response messages used by ServiceStack for the new service method.
-
Open the newly added file, and make sure that the namespace in it is the same as the one in the CategoryServiceMessages.cs(.vb) file. Inside this namespace define the GetCategoryByName class and the GetCategoryByNameResponse class. For example:
using EntitiesModelServicesDTOs.CoreDTOs; using ServiceStack; namespace EntitiesModelServicesDTOs.CategoryServiceMessages { public partial class GetCategoryByName : IReturn<GetCategoryByNameResponse> { public String CategoryName { get; set; } } public partial class GetCategoryByNameResponse { public CategoryDTO Category { get; set; } } }
Imports EntitiesModelServicesDTOs.CoreDTOs Imports ServiceStack Namespace EntitiesModelServicesDTOs.CategoryServiceMessages Partial Public Class GetCategoryByName Implements IReturn(Of GetCategoryByNameResponse) Public Property CategoryName() As String End Class Partial Public Class GetCategoryByNameResponse Public Property Category() As CategoryDTO End Class End Namespace
In the project which holds the code base of the ServiceStack layer, add a new class called CategoryService.partial.cs(.vb). This file will hold the custom code required for the new service method.
-
Open the newly added file, and make sure that the namespace in it is the same as the one in the CategoryService.cs(.vb) file. Depending on whether your code is on C# or on VB, you can choose from the following options:
-
For C# - In the same namespace, extend the ICategoryServiceImplementation interface with the signature of the new service method:
public partial interface ICategoryServiceImplementation { GetCategoryByNameResponse Get(GetCategoryByName request); }
-
For VB - Open the CategoryService.vb file and in the body of the ICategoryServiceImplementation interface, add the signature of the new service method:
Public Interface ICategoryServiceImplementation Property ServiceReference() As Service Function [Get](request As QueryCategories) As QueryResponse(Of CategoryDTO) Function [Get](request As GetCategoryById) As GetCategoryByIdResponse Function Post(request As AddCategory) As HttpResult Sub Put(request As UpdateCategory) Sub Delete(request As DeleteCategory) ' 'The signature of the new method Function [Get](request as GetCategoryByName) As GetCategoryByNameResponse ' End Interface
On VB, if you re-generate the service layer, you will have to repeat this step.
-
-
In the same file and namespace, extend the CategoryServiceImplementation type with the actual logic of the new service method:
public partial class CategoryServiceImplementation { public virtual GetCategoryByNameResponse Get(GetCategoryByName request) { ServiceStack.Model.Category category = this.repository. GetBy(c => c.CategoryName == request.CategoryName); if (category == null) { throw new HttpError(HttpStatusCode.NotFound, "Resource not available.", "A Category object with the specified name is not available."); } GetCategoryByNameResponse response = new GetCategoryByNameResponse(); response.Category = category.ConvertTo<CategoryDTO>(); return response; } }
Partial Public Class CategoryServiceImplementation Public Overridable Function [Get](ByVal request As GetCategoryByName) As GetCategoryByNameResponse _ Implements ICategoryServiceImplementation.Get Dim category As Category = Me._repository.GetBy(Function(c) c.CategoryName = request.CategoryName) If category Is Nothing Then Throw New HttpError(HttpStatusCode.NotFound, "Resource not available.", "A Category object with the specified name is not available.") End If Dim response As New GetCategoryByNameResponse() response.Category = category.ConvertTo(Of CategoryDTO)() Return response End Function End Class
-
Again in the same file and namespace, add a partial class, which will extend the CategoryService class with the definition of the new method. For example:
using EntitiesModelServicesDTOs.CategoryServiceMessages; using EntitiesModelServicesDTOs.CoreDTOs; using System.Web; namespace ServiceStack.Demo { public partial class CategoryService { public GetCategoryByNameResponse Get(GetCategoryByName request) { return this.categorySvcImp.Get(request); } } }
Imports EntitiesModelServicesDTOs.CategoryServiceMessages Imports EntitiesModelServicesDTOs.CoreDTOs Imports ServiceStack.Demo.VB Imports ServiceStack.Model.VB Imports System.Net Partial Public Class CategoryService Public Function [Get](ByVal request As GetCategoryByName) As GetCategoryByNameResponse Return Me._categorySvcImp.Get(request) End Function End Class
-
The new service method has to be register it in the ServiceStack service host. This means, that you need to extend the AppHost class in a partial class, and to implement the RegisterCustomRoutes method. To do this, you need to add a new class in the application that hosts the service layer. Call it AppHost.partial.cs. Make sure that the namespace of the newly added class is the same as the one of the AppHost class. In the class body add the implementation of the RegisterCustomRoutes method. For example:
using EntitiesModelServicesDTOs.CategoryServiceMessages; namespace ServiceStack.Demo { public partial class AppHost { partial void RegisterCustomRoutes(ref bool shouldRegisterDefaultRoutes) { shouldRegisterDefaultRoutes = true; this.Routes.Add<GetCategoryByName>(CategoryService.CategoriesBasePath + "/ByName/{CategoryName}", ApplyTo.Get); } } }
Imports EntitiesModelServicesDTOs.CategoryServiceMessages Partial Public Class AppHost Private Sub RegisterCustomRoutes(ByRef shouldRegisterDefaultRoutes As Boolean) shouldRegisterDefaultRoutes = True Me.Routes.Add(Of GetCategoryByName)(CategoryService.CategoriesBasePath & _ "/ByName/{CategoryName}", ApplyTo.Get) End Sub End Class
- Build the solution and start the web application.
-
You can test the new web service on URL similar to the following:
http://localhost:<port_number>/Categories/ByName/{CategoryName}