ASP.NET Core Web App .NET 6, reading configuration from within a Razor pages' cs file - razor-pages

I'm trying to find a simple way to access a value from the configuration of a .NET 6, ASP.NET Core Web App, in a Razor page's .cs file, using Visual Studio 2022. There's a lot of examples of how to do this online the old way. I'm trying to get up to speed with ASP.NET Core, and .NET 6, and I don't want to use the old techniques.
Right now I can connect to a SQLite database with a very simple configuration. Here's the code in my Index.cshtml.cs file,
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
using System.Data.SQLite;
namespace Something.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
var cnnStrBuilder = new SQLiteConnectonStringBuilder();
cnnStrBuild.DataSource = "DBFile.SQLite";
using (SQLiteConnection cnn = new(cnnStrBuilder.ConnectionString))
{
cnn.Open();
...
}
}
}
}
This works. I can read and display data without the need for a DBContext, or EntityFramework, or anything else. All I want to change is to get the connection string from the appsettings.json file. My appsettings.json looks like this,
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"MyConStr" : "DBFile.SQLite"
},
"AllowedHosts": "*"
}
in my Program.cs file is the builder object, which I could use to get the configuration.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
but how do I get access to the builder object in the Razor page's .cs file so I can get to the configuration? .NET 6 is supposed to make things like this easier, I thought.
BTW, I purposefully left 'connection string' out of the title of this post because I feel like this should be a generic thing. It shouldn't be so hard to retrieve a string value from the configuration settings from anywhere in the application. What am I missing?

You can inject an instance of IConfiguration in your PageModel which you can use to access appsettings.json values.
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IConfiguration _configuration;
public IndexModel(ILogger<IndexModel> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
public void OnGet()
{
var cnnStrBuilder = new SQLiteConnectonStringBuilder();
cnnStrBuild.DataSource = _configuration.GetConnectionString("MyConStr");
using (SQLiteConnection cnn = new(cnnStrBuilder.ConnectionString))
{
cnn.Open();
...
}
}
}
You can also check this article for more info:
https://www.learnrazorpages.com/configuration/

Related

Building my first Azure functions , am i doing things correctly?

I created my first Azure Function which integrate with SharePoint Online list, using those main points:-
1-I created an Azure App with self-sign certificate to authorize my Azure function.
2-I created a new Azure Function project using Visual Studio 2019. here are the main components Function.cs:-
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using PnP.Core.Services;
using PnP.Core.Model.SharePoint;
using System.Collections.Generic;
namespace FunctionApp1
{
public class Function1
{
private readonly IPnPContextFactory pnpContextFactory;
public Function1(IPnPContextFactory pnpContextFactory)
{
this.pnpContextFactory = pnpContextFactory;
}
[FunctionName("Function1")]
public void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
using (var context = pnpContextFactory.Create("Default"))
{
var myList = context.Web.Lists.GetByTitle("SubFolders");
Dictionary<string, object> values = new Dictionary<string, object>
{
{ "Title", System.DateTime.Now }
};
// Use the AddBatch method to add the request to the current batch
myList.Items.AddBatch(values);
context.Execute();
}
}
}
}
Startup.cs:-
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PnP.Core.Auth;
using System.Security.Cryptography.X509Certificates;
[assembly: FunctionsStartup(typeof(FunctionApp1.Startup))]
namespace FunctionApp1
{
class Startup :FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var config = builder.GetContext().Configuration;
var azureFunctionSettings = new AzureFunctionSettings();
config.Bind(azureFunctionSettings);
builder.Services.AddPnPCore(options =>
{
options.DisableTelemetry = true;
var authProvider = new X509CertificateAuthenticationProvider(azureFunctionSettings.ClientId,
azureFunctionSettings.TenantId,
StoreName.My,
StoreLocation.CurrentUser,
azureFunctionSettings.CertificateThumbprint);
options.DefaultAuthenticationProvider = authProvider;
options.Sites.Add("Default", new PnP.Core.Services.Builder.Configuration.PnPCoreSiteOptions
{
SiteUrl = azureFunctionSettings.SiteUrl,
AuthenticationProvider = authProvider
});
});
}
}
}
local.setting.json:-
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"SiteUrl": "https://***.sharepoint.com/",
"TenantId": "0b***",
"ClientId": "92***",
"CertificateThumbPrint": "EB***",
"WEBSITE_LOAD_CERTIFICATES": "EB***"
}
}
then i deploy it to Azure and it is working well, where each 5 minutes it adds a new list item. but i have those questions:-
Am i am doing things correctly, especially from a security perspective? as at the end my Azure function will have a public URL as follow https://functionapp1*****.azurewebsites.net and this can be called by any anonymous user.. so is this a security hole? if so, then how i can fix it?
I am currently using self-Signed certificate, so is it fine for Production? if not, then what i need to do , to get a proper SSL ?
Thanks
EDIT. Here is the url for the fucntion

Azure app insights TelemetryConfiguration not working in Startup.cs file of azure function

my startup file looks like this
[assembly: FunctionsStartup(typeof(Startup))]
{
public override void Configure(IFunctionsHostBuilder builder)
{
var context = builder.GetContext();
var _config = context.Configuration;
var telemetryConfig = new TelemetryConfiguration();
telemetryConfig.ConnectionString = _config.GetConnectionString("ApplicationInsights");
builder.Services.AddSingleton<TelemetryClient>(x => new TelemetryClient(telemetryConfig));
}
}
and the settings file looks like this
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"APPINSIGHTS_INSTRUMENTATIONKEY": "xxxxxx-fbc9-441b-9869-70bcb4afc93a",
"TimerInterval": "0 */5 * * * *"
},
"ConnectionStrings": {
"ApplicationInsights": "InstrumentationKey=xxxxxx-fbc9-441b-9869-
70bcb4afc93a;IngestionEndpoint=https://xxx-
in.applicationinsights.azure.com/;LiveEndpoint=https://xxxxxx.livediagnostics.monitor.azure.c
om/" }
}
problem here is that telemetryConfig is set properly i can see the InstrumentationKey and ConnString
but new TelemetryClient(telemetryConfig) has InstrumentationKey empty and most of the properties set to null.
even if i set InstrumentationKey directly (which is obsolete) the dependency injected value in the code has empty InstrumentationKey
in code i am using it like this
private readonly TelemetryClient _telemetry;
public FHIRExtract(ILogger<FHIRExtract> logger, TelemetryClient telemetry, IConfiguration configuration)
{
_logger = logger;
_config = configuration;
_telemetry = telemetry;
}
[FunctionName("FHIRExtract")]
public async Task Run([TimerTrigger("%TimerInterval%"
_telemetry has InstrumentationKey empty
but when i set this value in the code
//_telemetry.InstrumentationKey = "xxxxxx-fbc9-441b-9869-70bcb4afc93a";
it works and i am able to send telemetry data
I am trying to use non obsolete functions to configure Telemetry client and want to use it by DI.
what am i doing wrong ?
You shouldn't setup Application Insights this way.
Install the Microsoft.Azure.WebJobs.Logging.ApplicationInsights NuGet package in the azure functions project
In the Configure override add the logging:
builder.Services.AddLogging();
Configure the instrumentatio key by setting APPINSIGHTS_INSTRUMENTATIONKEY in the settings.json file
Inject a TelemetryConfiguration in your function and create an instance of TelemetryClient:
public FHIRExtract(ILogger<FHIRExtract> logger, TelemetryConfiguration
telemetryConfig, IConfiguration configuration)
{
_logger = logger;
_config = configuration;
_telemetry = new TelemetryClient(telemetryConfig);
}
For some reason the custom telemetry logging in azure functions breaks when only setting APPLICATIONINSIGHTS_CONNECTION_STRING in the configuration but I am sure that will be fixed in the near future. In my experience the application insights integration always lags behind a bit.
References:
Offical docs
Sample repository demoing various Application Insights techniques

How to inject IHttpClientFactory in Container servicestack.net?

I'm working on a solution that interacts with Redis, using the servicestack.net library.
I have a class that inherits from ServiceStack.AppHostBase and asks me for an override of the Configure method. This method has as a parameter a Funq.Container that I see is an implementation of IServiceProvider, IResolver and IContainer, and none of these interfaces have the AddHttpClient method that is provided by the IServiceCollection. Method I need to be able to inject the IHttpClientFactory. Any idea how to solve my problem?
To do it in ASP.NET (not .NET Core), the quick way would be to:
install Microsoft.Extensions.DependencyInjection package and call .AppHttpClient() extension
Build the Service Provider you would normally see in .NET Core
Get the instance of IHttpClientFactory from the Service Provider
Register the instance of IHttpClientFactory with Funq.Container again
using Microsoft.Extensions.DependencyInjection;
public class AppHost : AppHostBase
{
public override void Configure(Container container)
{
...
RegisterHttpClientFactory(container);
}
private container RegisterHttpClientFactory(Container container)
{
var services = new ServiceCollection()
.AddHttpClient();
// You can kind of inspect services returned.
// You can see this extension registers lot of other things too beside
// IHttpClientFactory.
// Also you can see the lifetime of IHttpClientFactory is Singleton.
var serviceProvider = services.BuildServiceProvider();
container.AddSingleton(serviceProvider.GetService<IHttpClientFactory>());
return container;
}
}
If you happen to use Unity Adaptor
Unity has a package to give you an extension as well to build the Service Provider directly into the Unity Container:
using Microsoft.Extensions.DependencyInjection;
using Unity;
using Unity.Microsoft.DependencyInjection;
public static class UnityConfig
{
public static void RegisterTypes(IUnityContainer container)
{
...
container.RegisterServices();
container.RegisterHttpClientFactory();
}
private static IUnityContainer RegisterHttpClientFactory(
this IUnityContainer unityContainer)
{
new ServiceCollection()
.AddHttpClient()
.BuildServiceProvider(unityContainer);
return unityContainer;
}
}
This is the interface definition of IServiceCollection from IServiceCollection.cs:
public interface IServiceCollection : IList<ServiceDescriptor>
{
}
AddHttpClient is just an extension method from Microsoft.Extensions.DependencyInjection that wraps adding a number of additional dependencies to ASP.NET Core IOC.
So you should continue to register it on ASP.NET Core IOC, i.e:
public class Startup : ModularStartup
{
public new void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseServiceStack(new AppHost
{
AppSettings = new NetCoreAppSettings(Configuration)
});
}
}
As any dependencies registered .NET Core Startup are also available to ServiceStack.

how distinguish traces from different instances .net core application in Application Insights

I work on .NET Core 2.2 console application that uses Microsoft.Extensions.Logging and is configured to send logs to Azure Application Insights using Microsoft.ApplicationInsights.Extensibility by:
services.AddSingleton(x =>
new TelemetryClient(
new TelemetryConfiguration
{
InstrumentationKey = "xxxx"
}));
...
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
loggerFactory.AddApplicationInsights(serviceProvider, logLevel);
It works ok: I can read logs in Application Insights. But the application can be started simultanously in few instances (in different Docker containers). How can I distinguish traces from different instances? I can use source FileName, but I don't know how I should inject it.
I tried to use Scope:
var logger = loggerFactory.CreateLogger<Worker>();
logger.BeginScope(dto.FileName);
logger.LogInformation($"Start logging.");
It's interesting that my configuration is almost identical as in example: https://github.com/MicrosoftDocs/azure-docs/issues/12673
But in my case I can't see the property "FileName" in Application Insights.
For console project, if you want to use the custom ITelemetryInitializer, you should use this format: .TelemetryInitializers.Add(new CustomInitializer());
Official doc is here.
I test it at my side, and it works. The role name can be set.
Sample code is below:
static void Main(string[] args)
{
TelemetryConfiguration configuration = TelemetryConfiguration.CreateDefault();
configuration.InstrumentationKey = "xxxxx";
configuration.TelemetryInitializers.Add(new CustomInitializer());
var client = new TelemetryClient(configuration);
ServiceCollection services = new ServiceCollection();
services.AddSingleton(x => client);
var provider = services.BuildServiceProvider();
var loggerFactory = new LoggerFactory();
loggerFactory.AddApplicationInsights(provider, LogLevel.Information);
var logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("a test message 111...");
Console.WriteLine("Hello World!");
Console.ReadLine();
}
Check the role name in azure portal:
If you really have no way to distinguish them you can use a custom telemetry initializer like this:
public class CustomInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = Environment.MachineName;
}
}
and/or you can add a custom property:
public class CustomInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
if(telemetry is ISupportProperties)
{
((ISupportProperties)telemetry).Properties["MyIdentifier"] = Environment.MachineName;
}
}
}
In this example I used Environment.MachineName but you can of course use something else if needed. Like this work Id parameter of yours.
the wire it up using:
services.AddSingleton<ITelemetryInitializer, CustomInitializer>();

ASP.NET Web Api 2, Ninject, OWIN, and IIS

I'm using Ninject for dependency injection in my ASP.NET Web Api 2 project. Everything is working perfectly locally through Visual Studio and IIS Express, but when I deploy to IIS, the dependency's are not resolved. Below is my Startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var webApiConfiguration = new HttpConfiguration();
webApiConfiguration.EnableCors();
webApiConfiguration.SuppressDefaultHostAuthentication();
webApiConfiguration.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
webApiConfiguration.MapHttpAttributeRoutes();
app.UseNinjectMiddleware(CreateKernel).UseNinjectWebApi(webApiConfiguration);
ConfigureAuth(app);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
app.Run(async context =>
{
await context.Response.WriteAsync("Welcome to Web API");
});
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
kernel.Load(new CourseModule(), new DataPullModule(), new DegreeModule(), new ResponseModule(), new RestSharpModule());
return kernel;
}
}
The error I get is when trying to access one of my controllers is below:
An error occurred when trying to create a controller of type 'DegreeController'. Make sure that the controller has a parameterless public constructor.
Here is my constructor for the DegreeController:
public DegreeController(IDegreeMapper degreeMapper, IDegreeRepository degreeRepository)
{
_degreeMapper = degreeMapper;
_degreeRepository = degreeRepository;
}
And here is the DegreeModule where I bind interfaces to classes.
public class DegreeModule : NinjectModule
{
public override void Load()
{
Bind<IDegreeController>().To<DegreeController>().InRequestScope();
Bind<IDegreeMapper>().To<DegreeMapper>().InRequestScope();
Bind<IDegreeRepository>().To<DegreeRepository>().InRequestScope();
Bind<IDegreeRatingCalculator>().To<DegreeRatingCalculator>().InRequestScope();
}
}
var kernel = CreateKernel();
app.UseNinjectMiddleware(() => kernel).UseNinjectWebApi(configuration);

Resources