Data Access has been discontinued. Please refer to this page for more information.

How to: Expose Domain Methods

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 article will show you how to expose domain methods mapped to database stored procedures (or table-valued functions) through a ServiceStack layer generated by Service Wizard.

Suppose that you created a stored procedure called ReadProductDetails in the Northwind sample database. This procedure takes a single parameter - the ID of a given product, and returns a subset of the columns from the Products table and the Categories table. Suppose the procedure is mapped to a domain method in your model. The method's signature is public IEnumerable<ProductDetails> ReadProductDetails(int? productID).

To expose this method in your ServiceStack layer, you will extend the generated code with entirely new service. You can follow the next steps:

  1. In the DTOs project, add a class file called ProductDetailsDTO.cs(.vb). This file will hold the ProductDetailsDTO class and will contain the properties of ProductDetails (the complex type mapped to the stored procedure's result shape). The class has to be in the same namespace as the rest of the DTOs in your ServiceStack layer. For example:

    using System;
    using System.Collections.Generic;
    namespace EntitiesModelServicesDTOs.CoreDTOs
    {
        public partial class ProductDetailsDTO
        {
            public string ProductName { get; set; }
            public string CompanyName { get; set; }
            public string CategoryName { get; set; }
            public string QuantityPerUnit { get; set; }
            public decimal? UnitPrice { get; set; }
            public short? UnitsInStock { get; set; }
            public short? UnitsOnOrder { get; set; }
            public short? ReorderLevel { get; set; }
            public bool Discontinued { get; set; }
        }
    }
    
    Namespace CoreDTOs
        Partial Public Class ProductDetailsDTO
            Public Property ProductName() As String
            Public Property CompanyName() As String
            Public Property CategoryName() As String
            Public Property QuantityPerUnit() As String
            Public Property UnitPrice() As Decimal?
            Public Property UnitsInStock() As Short?
            Public Property UnitsOnOrder() As Short?
            Public Property ReorderLevel() As Short?
            Public Property Discontinued() As Boolean
        End Class
    End Namespace
    
  2. In the DTOs project, add a class file called ProductDetailsServiceMessages.cs(.vb). It will hold the request and response messages used by ServiceStack. For example:

    using System;
    using ServiceStack;
    using EntitiesModelServicesDTOs.CoreDTOs;
    namespace EntitiesModelServicesDTOs.ProductDetailsServiceMessages
    {
        public partial class GetProductDetailsById : IReturn<GetProductDetailsByIdResponse>
        {
            public int ProductId { get; set; }
        }
        public partial class GetProductDetailsByIdResponse
        {
            public  ProductDetailsDTO Product { get; set; }
        }
    }
    
    Imports ServiceStack
    Imports EntitiesModelServicesDTOs.CoreDTOs
    Namespace ProductDetailsServiceMessages
        Partial Public Class GetProductDetailsById
            Implements IReturn(Of GetProductDetailsByIdResponse)
            Public Property ProductId() As Integer
        End Class
        Partial Public Class GetProductDetailsByIdResponse
            Public Property Product() As ProductDetailsDTO
        End Class
    End Namespace
    
  3. In the project which holds the code base of the ServiceStack layer, add a class file called EntitySpecificRepositories.partial.cs(.vb). This file will hold the repository dedicated to the stored procedure. For example:

    using ServiceStack.Model;
    using System.Linq;
    namespace ServiceStack.Demo.Repositories
    {
        public partial interface IProductDetailsRepository 
            : IBaseRepository<ServiceStack.Model.ProductDetails, ServiceStack.Model.EntitiesModel>
        {
            ProductDetails ReadProductDetails(int productId);
        }
        public partial class ProductDetailsRepository 
            : BaseRepository<ServiceStack.Model.ProductDetails, ServiceStack.Model.EntitiesModel>, 
            IProductDetailsRepository
        {
            public ProductDetails ReadProductDetails(int productId)
            {
                ProductDetails product = this.context.ReadProductDetails(productId).SingleOrDefault();
                return product;
            }
        }
    }
    
    Namespace Repositories
        Public Interface IProductDetailsRepository
            Inherits IBaseRepository(Of Model.ProductDetails, Model.EntitiesModel)
            Function ReadProductDetails(ByVal productId As Integer) As Model.ProductDetails
        End Interface
        Partial Public Class ProductDetailsRepository
            Inherits BaseRepository(Of Model.ProductDetails, Model.EntitiesModel)
            Implements IProductDetailsRepository
            Public Function ReadProductDetails(ByVal productId As Integer) As Model.ProductDetails _
                Implements IProductDetailsRepository.ReadProductDetails
                Dim product As Model.ProductDetails = Me.context.ReadProductDetails(productId).SingleOrDefault()
                Return product
            End Function
        End Class
    End Namespace
    
  4. In the project which holds the code base of the ServiceStack layer, add a class file called ProductDetailsService.cs(.vb). This file will hold the ServiceStack service for the domain method and its implementation.

  5. In the ProductDetailsService.cs(.vb) file, add the IProductDetailsServiceImplementation interface. For example:

    using System;
    using System.Linq;
    using System.Net;
    using System.Collections.Generic;
    using ServiceStack;
    using ServiceStack.Demo.DynamicQueryUtils;
    using ServiceStack.Demo.Repositories;
    using ServiceStack.Model;
    using EntitiesModelServicesDTOs.CoreDTOs;
    using EntitiesModelServicesDTOs.ProductDetailsServiceMessages;
    namespace ServiceStack.Demo
    {
        public partial interface IProductDetailsServiceImplementation
        {
            Service ServiceReference {get; set; }
            GetProductDetailsByIdResponse Get(GetProductDetailsById request);
        }
    }
    
    Imports EntitiesModelServicesDTOs.ProductDetailsServiceMessages
    Imports ServiceStack.Demo.VB.Repositories
    Imports System.Net
    Imports EntitiesModelServicesDTOs.CoreDTOs
    Imports ServiceStack.Model
    Public Interface IProductDetailsServiceImplementation
        Property ServiceReference() As Service
        Function [Get](ByVal request As GetProductDetailsById) As GetProductDetailsByIdResponse
    End Interface
    
  6. In the same file, add the ProductDetailsServiceImplementation class, which will hold the actual implementation of the service method. For example:

    public partial class ProductDetailsServiceImplementation : IProductDetailsServiceImplementation
    {
        private IProductDetailsRepository repository;
        public Service ServiceReference { get; set; }
        public ProductDetailsServiceImplementation(IProductDetailsRepository repository)
        {
            this.repository = repository;
        }
        public virtual GetProductDetailsByIdResponse Get(GetProductDetailsById request)
        {
            ServiceStack.Model.ProductDetails product = this.repository.ReadProductDetails(request.ProductId);
            if (product == null)
            {
                throw new HttpError(HttpStatusCode.NotFound, 
                                    "Resource not available.", 
                                    "A Product object with the specified identificator is not available.");
            }
            GetProductDetailsByIdResponse response = new GetProductDetailsByIdResponse();
            response.Product = product.ConvertTo<ProductDetailsDTO>();
            return response;
        }
    }
    
    Partial Public Class ProductDetailsServiceImplementation
        Implements IProductDetailsServiceImplementation
        Private repository As IProductDetailsRepository
        Public Property ServiceReference() As Service _
            Implements IProductDetailsServiceImplementation.ServiceReference
        Public Sub New(ByVal repository As IProductDetailsRepository)
            Me.repository = repository
        End Sub
        Public Overridable Function [Get](ByVal request As GetProductDetailsById) As GetProductDetailsByIdResponse _
            Implements IProductDetailsServiceImplementation.Get
            Dim product As ProductDetails = Me.repository.ReadProductDetails(request.ProductId)
            If product Is Nothing Then
                Throw New HttpError(HttpStatusCode.NotFound, 
                                    "Resource not available.", 
                                    "A Product object with the specified identificator is not available.")
            End If
            Dim response As New GetProductDetailsByIdResponse()
            response.Product = product.ConvertTo(Of ProductDetailsDTO)()
            Return response
        End Function
    End Class
    
  7. In the same file as well, add the ProductDetailsService class, which will hold the ServiceStack web service method. For example:

    public partial class ProductDetailsService : Service
    {
        private static string productDetailsBasePath = @"/ProductDetails";
        public static string ProductDetailsBasePath
        {
            get
            {
                return productDetailsBasePath;
            }
            protected set
            {
                productDetailsBasePath = value;
            }
        }
        private IProductDetailsServiceImplementation productDetailsSvcImp;
        public ProductDetailsService(IProductDetailsServiceImplementation productDetailsSvcImp)
        {
            this.productDetailsSvcImp = productDetailsSvcImp;
            this.productDetailsSvcImp.ServiceReference = this;
        }
        public GetProductDetailsByIdResponse Get(GetProductDetailsById request)
        {
            return this.productDetailsSvcImp.Get(request);
        }
    }
    
    Partial Public Class ProductDetailsService
        Inherits Service
        Private Shared _productDetailsBasePath As String = "/ProductDetails"
        Public Shared Property ProductDetailsBasePath() As String
            Get
                Return _productDetailsBasePath
            End Get
            Protected Set(ByVal value As String)
                _productDetailsBasePath = value
            End Set
        End Property
        Private productDetailsSvcImp As IProductDetailsServiceImplementation
        Public Sub New(ByVal productDetailsSvcImp As IProductDetailsServiceImplementation)
            Me.productDetailsSvcImp = productDetailsSvcImp
            Me.productDetailsSvcImp.ServiceReference = Me
        End Sub
        Public Function [Get](ByVal request As GetProductDetailsById) As GetProductDetailsByIdResponse
            Return Me.productDetailsSvcImp.Get(request)
        End Function
    End Class
    
  8. Finally, in order to register the new service, you need to extend the ServiceStack service host class in a partial class and to implement the ApplyCustomIocContainerEntityRepositoriesConfiguration, ApplyCustomIocContainerServiceImplementationsConfiguration, and RegisterCustomRoutes methods. In the generated code, this would be the web application that host the service layer. For example:

    using System;
    using System.Reflection;
    using Funq;
    using ServiceStack;
    using ServiceStack.Demo;
    using ServiceStack.Demo.Repositories;
    using EntitiesModelServicesDTOs.ProductServiceMessages;
    using EntitiesModelServicesDTOs.ProductDetailsServiceMessages;
    namespace ServiceStack.Demo
    {
        public partial class AppHost
        {
            partial void ApplyCustomIocContainerEntityRepositoriesConfiguration(Funq.Container container, 
                ref bool shouldRegisterDefaultEntityRepositories)
            {
                shouldRegisterDefaultEntityRepositories = true;
                container.RegisterAutoWiredAs<ProductDetailsRepository, IProductDetailsRepository>().
                    ReusedWithin(ReuseScope.None);
            }
            partial void ApplyCustomIocContainerServiceImplementationsConfiguration(Funq.Container container, 
                ref bool shouldRegisterDefaultServiceImplementations)
            {
                shouldRegisterDefaultServiceImplementations = true;
                container.RegisterAutoWiredAs<ProductDetailsServiceImplementation, IProductDetailsServiceImplementation>().
                    ReusedWithin(ReuseScope.None);
            }
            partial void RegisterCustomRoutes(ref bool shouldRegisterDefaultRoutes)
            {
                shouldRegisterDefaultRoutes = true;
                this.Routes.Add<GetProductDetailsById>(ProductDetailsService.ProductDetailsBasePath + 
                    "/{ProductId}", ApplyTo.Get);
            }
        }
    }
    
    Imports ServiceStack.Demo.Repositories
    Imports Funq
    Imports EntitiesModelServicesDTOs.ProductDetailsServiceMessages
    Partial Public Class AppHost
        Private Sub ApplyCustomIocContainerEntityRepositoriesConfiguration(ByVal container As Funq.Container,
                                                 ByRef shouldRegisterDefaultEntityRepositories As Boolean)
            shouldRegisterDefaultEntityRepositories = True
            container.RegisterAutoWiredAs(Of ProductDetailsRepository, IProductDetailsRepository)() _
                .ReusedWithin(ReuseScope.None)
        End Sub
        Private Sub ApplyCustomIocContainerServiceImplementationsConfiguration(ByVal container As Funq.Container,
                                                  ByRef shouldRegisterDefaultServiceImplementations As Boolean)
            shouldRegisterDefaultServiceImplementations = True
            container.RegisterAutoWiredAs(Of ProductDetailsServiceImplementation, IProductDetailsServiceImplementation)() _
                .ReusedWithin(ReuseScope.None)
        End Sub
        Private Sub RegisterCustomRoutes(ByRef shouldRegisterDefaultRoutes As Boolean)
            shouldRegisterDefaultRoutes = True
            Me.Routes.Add(Of GetProductDetailsById)(ProductDetailsService.ProductDetailsBasePath & _
                                                    "/{ProductId}", ApplyTo.Get)
        End Sub
    End Class
    
  9. Build your solution and start your application.

  10. You can test the new service method on a URL similar to the following:

    http://localhost:<port_number>/ProductDetails/{ProductId}