Does ServiceStack Cache Internal Requests? - caching

If I use the .ToOptimizedResult (documented here) from a "bare" request like so:
var svc = new MyService();
var svcResul = svc.Any(new requestDTO() {..});
Will the performance of svcResult benefit from caching, or must it be called from Http/Messaging/Client ?
Note: app is actually running a servicestack container.. but caller above is not invoked from inside a service.

ServiceStack doesn't do any Request Caching by default, you have to opt-in to Caching using one of the caching strategies.
You shouldn't use ToOptimizedResult() in Services that you wish to call directly, for cached Requests it returns a serialized compressed byte[] result which isn't accessible as a Typed Response DTO from C# API.
For caching Services that you want to call via C# you can use the CacheResponse Attribute instead.
Calling Other Services
Note: The recommended way to call other Services is to use the Service Gateway, e.g:
var result = Gateway.Send(new RequestDto());
If you want to call the C# method on the Service directly you should use ResolveService to resolve an autowired Service and call the method within a using statement, e.g:
using (var service = base.ResolveService<MyService>())
{
var result = service.Any(new RequestDto());
}

Related

how to use services before app build in .net core 6.0

I have earlier achieved this .net 3.1. But it couldn't be possible with .Net 6 because of startup.cs removed.
I have registered a few services,
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var appSettings = builder.Configuration.GetSection("AppSettings").Get<AppSettings>();
builder.Services.AddScoped<IEncryption, Encryption>();
//Here I need to get the IEncryption Service, and call the method in this service to encrypt/decrypt the connection string to pass to DBContext Service.
builder.Services.AddDbContext<CatalogDbContext>(options => options.UseNpgsql(
appSettings.ConnectionString));
var app = builder.Build();
Earlier in .NET 3.1, I used BuildServicProvider() to get the Encryption service, and call the methods in that service to do the required logic then got the proper connection string I wanted that would be passed to the DBContext service on the next line.
Now, .NET 6/7 is forced to use the services only after app = builder.Build(); so, I can't register the DBCOntext after the build() method.
How can I solve this case? Any recommended approach to do this in .NET 6/7?
You still can useStartup.cs in .net 6
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services); // calling ConfigureServices method
var app = builder.Build();
startup.Configure(app, builder.Environment); // calling Configure method
And then you can use ConfigureServices and Configure methods to register your services before building.
You didn't need to use BuildServiceProvider in .NET Core 3.1 either. AddDbContext has an overload that provides access to an IServiceProvider instance :
builder.Services.AddDbContext<CatalogDbContext>((services,options) =>{
var myOwnDecrypter=services.GetRequiredService<IMyOwnDecrypter>();
var cns=myOwnDecrypter.Decrypt(appSettings.ConnectionString,key);
options.UseNpgsql(cns);
});
or, if you use the ASP.NET Core Data Protection package :
builder.Services.AddDataProtection();
...
builder.Services.AddDbContext<CatalogDbContext>((services,options) =>{
var protector = services.GetDataProtector("Contoso.Example.v2");
var cns=protector.Unprotect(appSettings.ConnectionString);
options.UseNpgsql(cns);
});
or, if IConfiguration.GetConnectionString is used :
builder.Services.AddDataProtection();
...
builder.Services.AddDbContext<CatalogDbContext>((services,options) =>{
var conn_string=services.GetService<IConfiguration>()
.GetConnectionString("MyConnectionString");
var protector = services.GetDataProtector("Contoso.Example.v2");
var cns=protector.Unprotect(conn_string);
options.UseNpgsql(cns);
});
That said, it's the configuration provider's job to decrypt encrypted settings, not the service/context's. ASP.NET Core's configuration allows using multiple different configuration sources in the same host, not just a single settings file. There's nothing special about appsettings.json. That's just the default settings file name.
You can add another settings file with sensitive contents with AddJsonSettings. That file could use the file system's encryption, eg NTFS Encryption, to ensure it's only readable by the web app account
You can read settings from a key management service, like Hashicorp, Azure Key Vault, Amazon Key Management etc.
You can create your own provider that decrypts its input. The answers to this SO questino show how to do this and one of them inherits from JsonConfigurationProvider directly.
Important Caveat: In general, my suggestion below is a bad practice
Do not call BuildServiceProvider
Why is bad? Calling BuildServiceProvider from application code results in more than one copy of singleton services being created which might result in incorrect application behavior.
Justification: I think it is safe to call BuildServiceProvider as long as you haven't registered any singletons before calling it. Admittedly not ideal, but it should work.
You can still callBuildServiceProvider() in .Net6:
builder.Services.AddScoped<IEncryption, Encryption>();
// create service provider
var provider = builder.Services.BuildServiceProvider();
var encryption = scope.ServiceProvider.GetService<IEncryptionService>();
// use service here
or alternatively
builder.Services.AddScoped<IEncryption, Encryption>();
var provider = builder.Services.BuildServiceProvider();
using (var scope = provider.CreateScope()) {
var encryption = scope.ServiceProvider.GetService<IEncryptionService>();
// use service here
}
Alternative:
You can still use the classic startup structure in .Net6/7. We upgraded our .Net3.1 projects to .Net6 without having to rewrite/restructure the Startup()

Calling internal (Endpoint) function in WebAPI

I am using Hangfire to execute recurring jobs in my web API and I use System.Web.HttpContext.Current.Server.MapPath in the "RoutineMethod" function.
But the problem is it throws object null exception.
I searched the problem and found that recurring jobs don't use http threads, they use background threads.
Now to resolve this problem I need to call my internal (endpoint) using httpclient.
But to do that I need to give URL of the Web API (to generate URI). So is there any other way to call internal function using httpclient.
My current code:
public static async Task<IHttpActionResult> RoutineTask()
{
//It was like this before.
//await new DemoController().RoutineMethod();
//await new DemoController().RoutineMethod2();
//I am planning to do this.
using (var client = new HttpClient())
{
//But I need to give URI which I don't think is a good idea.
var uri = new Uri("http://localhost/DemoApp/api/DemoController/RoutineMethod");
await client.GetAsync(uri);
}
return new DemoController().Ok();
}
The short answer is no. The HttpClient, as its name implies, requires an http connection.
There are no smell issues with storing service connection information in a configuration file.
However, to expand on Nkosi's comment, it appears that since your code can create an instance of DemoController, that it must have a reference to that controller's project or even be in the same project. I would extract the interesting code into a library or service that all areas needing the information can reference.

Using HttpClient to make a call within a Web API

I've been working on a .NET 4.6.1 Web API project. As part of this project, I need to call another Web API and I want to use the HttpClient to do so.
From my research online, you can't rely on just doing a normal HttpClient within a using clause as it doesn't garbage collect correctly and can lead to memory leaks.
E.g., I'm currently using it as follows:
using (HttpClient client = new HttpClient { Timeout = TimeSpan.FromSeconds(CONTENTFUL_TIMEOUT_IN_SECONDS) } )
{
responseText = await client.GetStringAsync(uri).ConfigureAwait(continueOnCapturedContext:false);
}
But as suggested in other articles from Stack Overflow and others, this leads to memory leaks, and the way around this is to share a single instance of the HttpClient.
E.g., check HTTPCLIENT DESTABILIZING YOUR SOFTWARE and HttpClientHandler/HttpClient Memory Leak.
I'm not sure however how to setup a shared "single" instance of the HttpClient from within an WebAPI itself?
You should have a look on how to implement singleton pattern. Refer to this.
Then you can create a singleton of HttpClient and make it responsible for all HTTP calls from your API.

How to access session from a view in ASP .NET Core MVC 1.0

I am trying to access session data from inside a view.
Use Case: I'm storing status messages in the session that will be displayed at the top of the page. Currently I implemented this by using a DisplayMessages() function that sets some ViewData[....] properties and calling it at the beginning of every controller action.
Goal: I want to only set the status message once without needing additional code in the controller to display the messages on the next page load.
So I'm trying to access the messages that are stored in the session directly from the view.
So far I have tried the following:
Dependency Injection of an IHttpContextAccessor (doesn't seem to work anymore with ASP .NET Core MVC 1.0.0
Creating a static class to access the session, including the change from next() to next.invoke() suggested in the comment
This didn't work. I could access the HttpContext and Session.IsAvailable was true, but there was no data in the session.
The following should work in the view: Context.Session.TryGetValue
If you are using the SessionExtensions then Context.Session.GetString will work.
Injecting IHttpContextAccessor does work, but starting with ASP.NET Core 1.0.0 RC2, the IHttpContextAcessor is not registered by default, because it has significant performance overhead per request. See this GitHub announcement for more information.
Tratcher posted:
IHttpContextAccessor can be used to access the HttpContext for the current thread. However, maintaining this state has non-trivial performance costs so it has been removed from the default set of services.
Developers that depend on it can add it back as needed:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
But in your use case, I would suggest using a ViewComponent, which is a reusable piece of View with logic, that do not depend on a controller.
The documentation can be found here.
In your Views you would simply embed it with
#await Component.InvokeAsync("PriorityList", new { maxPriority = 2, isDone = false })
or
#Component.Invoke("PriorityList", new { maxPriority = 2, isDone = false })
for synchronous calls.

When self-hosting what exactly causes AddressAccessDeniedException : HTTP could not register URL

I am writing a bdd test for a component that will startup phantomjs and hit a specific route on my site and do processing on that. Because the component is fundamentally about automating a phantom instance there is no way to easily stub out the http requests.
So I want to stub out a self-hosted endpoint that will stub out the data I'm after. Because this is a unit test I think its really important for it to run in isolation so I do something like this:
async Task can_render_html_for_slide_async() {
var config = new HttpSelfHostConfiguration("http://localhost:54331");
config.Routes.MapHttpRoute("Controller", "{controller}", new {});
using (var server = new HttpSelfHostServer(config)) {
server.OpenAsync().Wait();
var client = new HttpClient();
var resp = await client.GetStringAsync(config.BaseAddress+"/Stub");
Console.WriteLine(resp);
}
}
public class StubController : ApiController
{
public string Get() {
return "Booyah";
}
}
Which gets me
AddressAccessDeniedException : HTTP could not register URL http://+:54331/
I understand that netsh or Admin mode is required for this but I don't understand why. Nodejs for example runs perfectly fine on windows but has no such requirement.
Also using OWIN directly needs no netsh-ing. So....what's going on?
I wrote an article about it on codeproject, it was done to make it possible for multiple application to share the same port.
You can have both, IIS and Apache (or OWIN in your case) listenening port 80. The routing to the right application is done thanks to the path of the url.
IIS and Apache both would use this driver (http.sys). But you need permission to "reserve" a path.
Administrators are always authorized. For other users, use netsh or my GUI tool HttpSysManager to set ACL.
Any method that requires giving permission via netsh uses a Windows kernel driver to provide http access.
If a library opens a socket itself and handles the http communication that way, no netsh use is needed.
So to answer your question, some methods are using the kernel driver and some are handling the protocol themselves.

Resources