使用 Servicestack,在将 AutoQuery 与 ServiceSource 一起使用时如何缓存结果集?
Using Servicestack, how do you cache result sets when using AutoQuery with a ServiceSource?
我正在尝试将 ServiceStack 的 AutoQuery 与服务源一起使用,但我无法使缓存正常工作,或者误解了它应该如何工作。
我想要实现的目标是向 'edge' 微服务添加查询功能,该微服务调用提供完整数据列表的内部服务。
重现我的问题的最少代码:
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分钟),如果我在服务方法中打断点,列表'Names' 每次都会重新创建。即使我向服务发出相同的请求,也会发生这种情况。
我希望能够缓存结果集(在内存中很好)并且只在缓存过期时调用服务方法。我在这里做错了什么(或误解)?
编辑
我用来尝试 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>>();
}
}
如果您在服务实现中使用 AutoQuery,那只是一个 Custom AutoQuery implementation not a AutoQuery Service Data Source 查询普通服务的结果。
在这种情况下,听起来您确实想要一个可缓存的自动查询服务数据源,文档在其 GetGithubRepos Service 中显示了一个示例,该示例对 GitHub 进行了 API 调用的第 3 方 API:
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>>();
}
那你在注册Service DataSource的时候注册的是一个cached Service Data Source:
Plugins.Add(new AutoQueryDataFeature { MaxLimit = 100 }
.AddDataSource(ctx => ctx.ServiceSource<GithubRepo>(ctx.Dto.ConvertTo<GetGithubRepos>(),
HostContext.Cache, TimeSpan.FromMinutes(5)));
);
可以使用HostContext.LocalCache
缓存到本地Memory Cache中,而不是注册ICacheClient
caching provider.
我正在尝试将 ServiceStack 的 AutoQuery 与服务源一起使用,但我无法使缓存正常工作,或者误解了它应该如何工作。
我想要实现的目标是向 'edge' 微服务添加查询功能,该微服务调用提供完整数据列表的内部服务。
重现我的问题的最少代码:
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分钟),如果我在服务方法中打断点,列表'Names' 每次都会重新创建。即使我向服务发出相同的请求,也会发生这种情况。
我希望能够缓存结果集(在内存中很好)并且只在缓存过期时调用服务方法。我在这里做错了什么(或误解)?
编辑
我用来尝试 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>>();
}
}
如果您在服务实现中使用 AutoQuery,那只是一个 Custom AutoQuery implementation not a AutoQuery Service Data Source 查询普通服务的结果。
在这种情况下,听起来您确实想要一个可缓存的自动查询服务数据源,文档在其 GetGithubRepos Service 中显示了一个示例,该示例对 GitHub 进行了 API 调用的第 3 方 API:
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>>();
}
那你在注册Service DataSource的时候注册的是一个cached Service Data Source:
Plugins.Add(new AutoQueryDataFeature { MaxLimit = 100 }
.AddDataSource(ctx => ctx.ServiceSource<GithubRepo>(ctx.Dto.ConvertTo<GetGithubRepos>(),
HostContext.Cache, TimeSpan.FromMinutes(5)));
);
可以使用HostContext.LocalCache
缓存到本地Memory Cache中,而不是注册ICacheClient
caching provider.