Используя Servicetack, как вы кешируете результирующие наборы при использовании AutoQuery с ServiceSource? - PullRequest
1 голос
/ 10 июня 2019

Я пытаюсь использовать ServiceStack AutoQuery с источником службы, но либо не могу правильно настроить кэширование, либо неправильно понял, как это должно работать.

Чего я пытаюсь достичь, чтобы добавитьФункциональность запроса к «пограничному» микросервису, который вызывает внутреннюю службу, которая обслуживает полный список данных.

Минимальный код для воспроизведения моей проблемы:

class Program
{
    static async Task Main(string[] args)
    {
        IWebHost host = new WebHostBuilder()
            .UseKestrel((builderContext, options) => options.Configure(builderContext.Configuration.GetSection("Kestrel")))
            .UseStartup<Startup>()
            .Build();
        await host.RunAsync();
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services) {}
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseServiceStack(new AppHost());
        app.Run(context => Task.FromResult(0));
    }
}

public class AppHost : AppHostBase
{
    public AppHost() : base("Hello Web Services", typeof(HelloService).Assembly){ }

    public override void Configure(Funq.Container container)
    {
        container.AddSingleton<ICacheClient, MemoryCacheClient>(); // Otherwise HostContext.Cache is null
        Plugins.Add(new AutoQueryDataFeature { MaxLimit = 3, IncludeTotal = true }.AddDataSource(ctx => ctx.ServiceSource<string>(new Hello(), HostContext.Cache, TimeSpan.FromMinutes(5))));
    }
}

// Request DTO
[Route("/hello")]
[Route("/hello/{Name}")]
public class Hello : QueryData<NameDto>
{
    [QueryDataField(Condition = "StartsWith", Field = nameof(Name))]
    public string Name { get; set; }
}

public class NameDto
{
    public string Name { get; set; }
}

public class HelloService : Service
{
    public IAutoQueryData AutoQuery { get; set; }
    public async Task<object> Any(Hello query)
    {
        //Imagine I was making a service call to another microservice here...
        var data = new List<NameDto> { new NameDto { Name = "Bob" }, new NameDto { Name = "George" }, new NameDto { Name = "Baldrick" }, new NameDto { Name = "Nursey" }, new NameDto { Name = "Melchett" }, new NameDto { Name = "Kate" } };

        DataQuery<NameDto> dataQuery = AutoQuery.CreateQuery(query, Request, new MemoryDataSource<NameDto>(data, query, Request));

        return AutoQuery.Execute(query, dataQuery);
    }
}   

Пакеты Nuget: Mircosoft.AspNetCore.All (2.2.1) и ServiceStack (5.4.0)

Итак,в консоли (.NET Core 2.2) приведенный выше код будет раскручиваться и прослушивать порт 5000.

Если я сделаю запрос, я получу свой список, который ограничен количеством ожидаемых результатов, и яможно также пропустить / принять, как и ожидалось.

Однако каждый раз, когда я вызываю метод службы, результаты не кэшируются (что указывается, когда я регистрирую кэш подключаемого модуля в течение 5 минут), и если я добавлюточка останова в методе службы, список «Имен» создается заново каждый раз.Это происходит, даже если я делаю тот же запрос к службе.

Я хотел бы иметь возможность кэшировать набор результатов (в памяти все в порядке) и использовать метод службы только тогда, когда срок действия кэша истекает.Что я здесь делаю неправильно (или неправильно)?

Редактировать

Код, который я использовал, чтобы опробовать предложение Mythz ... теперь у меня нет автозапросафункции работают на всех.

class Program
{
    static async Task Main(string[] args)
    {
        IWebHost host = new WebHostBuilder()
            .UseKestrel((builderContext, options) => options.Configure(builderContext.Configuration.GetSection("Kestrel")))
            .UseStartup<Startup>()
            .Build();
        await host.RunAsync();
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseServiceStack(new AppHost());
        app.Run(context => Task.FromResult(0));
    }
}

public class AppHost : AppHostBase
{
    public AppHost() : base("Hello Web Services", typeof(HelloService).Assembly){ }

    public override void Configure(Funq.Container container)
    {
        container.AddSingleton<ICacheClient, MemoryCacheClient>();

        Plugins.Add(new AutoQueryDataFeature { MaxLimit = 5 }
            .AddDataSource(ctx => ctx.ServiceSource<GithubRepo>(ctx.Dto.ConvertTo<QueryGithubRepo>(),
                HostContext.Cache, TimeSpan.FromMinutes(5))));
    }


}


public class QueryGithubRepo : QueryData<GithubRepo>
{
    public string User { get; set; }
    public string Organization { get; set; }
}

public class GithubRepo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Homepage { get; set; }
    public int Watchers_Count { get; set; }
    public int Stargazers_Count { get; set; }
    public int Size { get; set; }
    public string Full_Name { get; set; }
    public DateTime Created_at { get; set; }
    public DateTime? Updated_At { get; set; }

    public bool Has_Downloads { get; set; }
    public bool Fork { get; set; }

    public string Url { get; set; } // https://api.github.com/repos/NetCoreWebApps/bare
    public string Html_Url { get; set; }
    public bool Private { get; set; }

    public GithubRepo Parent { get; set; } // only on single result, e.g: /repos/NetCoreWebApps/bare
}

public class NameDto
{
    public string Name { get; set; }
}

public class HelloService : Service
{    
    public object Get(QueryGithubRepo request)
    {
        if (request.User == null && request.Organization == null)
            throw new ArgumentNullException("User");

        var url = request.User != null
            ? $"https://api.github.com/users/{request.User}/repos"
            : $"https://api.github.com/orgs/{request.Organization}/repos";

        return url.GetJsonFromUrl(requestFilter: req => req.UserAgent = GetType().Name)
            .FromJson<List<GithubRepo>>();
    }
}

1 Ответ

0 голосов
/ 10 июня 2019

Если вы используете AutoQuery в своей реализации Службы, это просто Пользовательская реализация AutoQuery , а не Источник данных службы AutoQuery , который запрашивает результаты обычной Службы.

В этом случае звучит так, как будто вам нужен кэшируемый источник данных службы Auto Query Service, пример которого приведен в документации в его Сервисе GetGithubRepos , который выполняет вызов API для стороннего API GitHub:

public class QueryGithubRepo : QueryData<GithubRepo> 
{
    public string User { get; set; }
    public string Organization { get; set; }
}

public object Get(GetGithubRepos request)
{
    if (request.User == null && request.Organization == null)
        throw new ArgumentNullException("User");

    var url = request.User != null
        ? $"https://api.github.com/users/{request.User}/repos"
        : $"https://api.github.com/orgs/{request.Organization}/repos";

    return url.GetJsonFromUrl(requestFilter:req => req.UserAgent = GetType().Name)
        .FromJson<List<GithubRepo>>();
}

Затем вы регистрируете это кэшированный источник данных службы при регистрации источника данных службы:

Plugins.Add(new AutoQueryDataFeature { MaxLimit = 100 }
    .AddDataSource(ctx => ctx.ServiceSource<GithubRepo>(ctx.Dto.ConvertTo<GetGithubRepos>(), 
        HostContext.Cache, TimeSpan.FromMinutes(5)));
);

Вы можете использовать HostContext.LocalCache для кэширования в локальном кэше памяти вместо зарегистрированного ICacheClient поставщика кэширования .

...