how to use services before app build in .net core 6.0 - .net-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()

Related

Does ServiceStack Cache Internal Requests?

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());
}

Can I use FlurlClient with Asp.Net Core TestServer?

We are using FlurlClient in a few projects and familiar with their fluent interface.
We now want to use it in asp.net core integration tests using TestServer.
The example from
http://asp.net-hacker.rocks/2017/09/27/testing-aspnetcore.html
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
_client = _server.CreateClient();
I was going to change code to
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
var httpClient = _server.CreateClient();
_client = new FlurlClient(httpClient);
and use all FlurlClient methods/extensions.
But then I noticed
Is it possible to use Furl.Http with the OWIN TestServer? which described that more work is required in owin implementation.
Is approach for Asp.Net Core TestServer similar? Or is it simplified?
It's much simplified, and your proposed change is exactly right. The question you linked to is old and my answer contains information that's no longer relevant in 2.x. (I have updated it.) In fact, the ability to provide an existing HttpClient directly in a FlurlClient constructor was added very recently, and with this specific use case in mind.
Here's an extension method I use as a replacement for CreateClient; you might find it handy if you do this a lot:
public static class TestServerExtensions
{
public static IFlurlClient CreateFlurlClient(this TestServer server) =>
new FlurlClient(server.CreateClient());
}

How to use X509 certificate with the Nest Elastic Client

I am currently upgrading from Elastic Search 1.7 to 5.2. I know there is no upgrade path, which is fine. One problem we had originally is with Nest and ElastiSearch.Net there was no way to attach an X509 certificate as it only had the ability to use Basic Authentication. To get around that we made copies of the existing github repos and modified the code directly to allow it. This ultimately is what kept us from upgrading for so long since we couldn't just use the nuget packages, because we now had custom code.
Now that we are upgrading I'm trying to find out if this was ever remedied. Or, at the very least are there any hooks that we can use to get the ElasticClient(in Nest) or the ElasticLowLevelClient (in ElasticSearch.Net) to take in a certificate and pass it on when making the call.
Another option, is to use a PUT request to create the Index on initial creation, which is where we are needing the certificate. The issue we have there is we require the use of the AutoMap method since we have some custom attributes added on our models, and need those to go in on Index creation. I'm not sure if there is a way to generate that result of AutoMap for a given model to JSON, and just use a webclient to attach the certificate.
Let me know if you need anymore details.
It's possible to customise the connections that both NEST and Elasticsearch.Net use all the way back to 1.x. This is done by providing your own implementation of IConnection to the ConnectionSettings instance that is passed to the ElasticClient constructor.
First create your custom IConnection; it's easiest to derive from HttpConnection
public class HttpConnectionWithClientCertificate : HttpConnection
{
protected override HttpWebRequest CreateHttpWebRequest(RequestData requestData)
{
var request = base.CreateHttpWebRequest(requestData);
// add the certificate to the request
request.ClientCertificates.Add(new X509Certificate("path_to_cert"));
return request;
}
}
then pass this to ConnectionSettings
var node = new Uri("http://localhost:9200");
var connectionPool = new SingleNodeConnectionPool(node);
var connection = new HttpConnectionWithClientCertificate();
var settings = new ConnectionSettings(connectionPool, connection);
var client = new ElasticClient(config);

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.

How to reference an initialized embedded RavenDB instance in a Class Library?

My scenario is this:
I have a custom RavenDB membership provider that is implemented in a class library (DLL). This provider needs to access a database to store and retrieve User and Role information. I'd like to use the same app database to store membership information to avoid having one more database.
I don't know how to get a reference to the already initialized database (app database) inside the class library code. I think I'm going the wrong way here... :)
Some code:
bool embeddedStore = Convert.ToBoolean(config["enableEmbeddableDocumentStore"]);
if (embeddedStore)
{
_documentStore = new EmbeddableDocumentStore()
{
// Here I'm using the same connection string used by the app.
// This gives me an error when I try to open a session in the DocumentStore.
ConnectionStringName =
config["connectionStringName"]
};
}
else
{
_documentStore = new DocumentStore()
{
ConnectionStringName =
config["connectionStringName"]
};
}
This is the connection string present in Web.config:
<add name="RavenDB" connectionString="DataDir = ~\App_Data\Database" />
How can I reuse the same database within the custom membership provider? Any ideas?
I thought about moving the class library code files to the Web project. This way I could get a reference to the DocumentStore easily, but the code wouldn't be as organized as I'd like.
I also tried to use 2 RavenDB databases: 1 for the app and 1 for the membership provider, but as I'm running RavenDB in its embeddable fashion I couldn't get it working.
These are the errors I got during my attempts so far:
RavenDB Could not open transactional storage.
Temp path already used by another database instance.
You need to pass the instance of the opened document store to your dll.
You can do that using a container or by providing an API call to do that.
You can't have two instance using the same db.

Resources