Search code examples
razorinversion-of-controlservicestackdto

ServiceStack ResolveService


So my problem revolves around calling apphost.ResolveService described in the url below: Calling a ServiceStack service from Razor

I am in my _Layout.cshtml

And obviously the following code works perfectly, but as suggested in the answer in the above url it is sort of silly

SMSGateway.Services.EntityCollectionResponse response = 
    new ServiceStack.ServiceClient.Web.JsonServiceClient(
        "http://localhost:1337/")
        .Get<SMSGateway.Services.EntityCollectionResponse>(
            "/entities");

So that gives me a list of Entities :) But not optimal... so here is my attempt to do it in the correct way

var response = ConsoleAppHost.Instance
    .ResolveService<SMSGateway.Services.EntityService>(
        HttpContext.Current).Get(
            new SMSGateway.Services.EntitiesRequest());

// SMSGateway.Services.EntityCollectionResponse response =
//     base.Get<SMSGateway.Services.EntityService>().Get(
//         new SMSGateway.Services.EntitiesRequest());

foreach (var entity in response.Result)
{
    <li>
        <a href="@entity.MetaLink.Href">
            @Html.TitleCase(entity.Name) entities
        </a>
    </li>
}

Okay so the error I get is the following :

error CS0122: ConsoleAppHost is inaccessible due to its protection level....

Is this expected? I was pondering if this was not a case where I might not be allowed to call this in the _Layout.cshtml file?

Further reading brought me to the article InternalVisibleTo Testing Internal Methods in .NET 2.0

Which I found very interesting :P But no cigar :)


Solution

  • I would recommend you not calling services in a Razor template. A Razor template should be used only to render some markup from a model.

    The actual data access should be performed in the ServiceStack service that rendered this template. So in your case you could call another service from the operation:

    public object Get(SomeRequestDto message)
    {
        var response = this
            .ResolveService<SMSGateway.Services.EntityService>()
            .Get(new SMSGateway.Services.EntitiesRequest()
        );
    
        return response.Rersult;
    }
    

    or you might leave the container to inject the dependent service into the current service so that you don't even need to be using some service locator anti-patterns.

    public SomeService: Service
    {
        private readonly EntityService entityService;
        public SomeService(EntityService entityService)
        {
            this.entityService = entityService;
        }
    
        public object Get(SomeRequestDto message)
        {
            var response = this.entityService.Get(new SMSGateway.Services.EntitiesRequest()
    
            return response.Rersult;
        }
    }
    

    and then your Razor view will of course be strongly typed to the corresponding model:

    @model IEnumerable<WhateverTheTypeOfTheResultYouWannaBeLoopingThrough>
    foreach (var entity in Model)
    {
        <li>
            <a href="@entity.MetaLink.Href">
                @Html.TitleCase(entity.Name) entities
            </a>
        </li>
    }