I have hosted Azure Function V3 on Azure Linux environment. I am trying to read the connection string from the configuration section. But I am not getting it. I tried to put the connection string on both, Application Settings as well as Connection Strings sections as shown below.
I am using dependency injection and my Startup class looks like below.
using BHD.Data.Data;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
[assembly: FunctionsStartup(typeof(BHD.AzureFunctions.Startup))]
namespace BHD.AzureFunctions
{
class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var sqlConnection = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
builder.Services.AddDbContext<ApplicationDbContext>(
options => options.UseSqlServer(sqlConnection));
}
}
}
I get NullPointerException on ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString even though the connection string exists in local.settings.json file as shown below.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"AzureWebJobsDashboard": "UseDevelopmentStorage=true"
},
"ConnectionStrings": {
"DefaultConnection": "<My connection string>"
}
}
The way to get configuration in Azure Functions 2+ is IConfiguration, not ConfigurationManager.
You can inject IConfiguration into most places where you might inject anything else, but in Startup() you need to use something like this trick instead.
IConfiguration will automatically read from your function app settings when hosting in Azure, and your local.settings.json when run locally.
Edit (references):
The documentation for this is IMHO neither clear nor easy to find. There is also a lot of related discussion and confusion on GitHub.
The main source is DI in Azure Functions 2. Within it, most of what you need to know is almost easily missed:
The function host registers many services. The following services are safe to take as a dependency in your application:
[...] Microsoft.Extensions.Configuration.IConfiguration [...]
It also, in Working with options and settings says:
Values defined in app settings are available in an IConfiguration instance
The host does this for you; you do not need to do anything, and as above, it automatically gets settings from the correct source depending on your hosting context.
Related
I'm in the process of building a user management microservice using an API.NET Web API. I've added EntityFrameworkCore and using SQL Server as the database. I've made multiple services at this point, but this service does not seem to work the way I want it to.
FYI I'm using .NET 6.
The code you see below is how I add the AddDbContext to my Services in the Program.cs file.
builder.Services.AddDbContext<AppDbContext>(opt =>
{
// Getting the ConnectionString from the AppSettings-file.
string connectionString = builder.Configuration.GetConnectionString("Connection1");
Console.WriteLine("=====CONNECTION STRING=====");
Console.WriteLine("ConnectionString: " + connectionString + "\n");
Console.WriteLine("=====IT Asset Management=====");
Console.WriteLine("Service running: User Service");
if (builder.Environment.IsProduction())
Console.WriteLine("Environment is: PRODUCTION");
else
Console.WriteLine("Environment is: DEVELOPMENT");
// Configuring the DBContext to connect to the SQL server.
opt.UseSqlServer(connectionString);
});
When i run the code, my Console.WriteLines does not print, and it does not seem that the AddDbContext service gets initialized.
Below is my DbContext.
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using UserService.Models;
namespace UserService.Data
{
public class AppDbContext : IdentityDbContext<AppUser>
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options) { }
}
}
As of now I have tried everything I can think of and gotten help from colleagues, but we can't seem to find out what's wrong. I have another service running the exact same code, but with a different purpose (Only the Program.cs files are the same), so I've tried to copy paste the file. I have tried to reinstall all the related NuGet-packages, which did not work.
Then i tried to create a new ASP.NET Web API project and only installing the packages needed to make the DbContext and setup the service in the Program.cs file. Below is the Program.cs file from the new project.
The funny thing is that it does not work aswell in the new project and I have no idea why.
using UserManagement.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<AppDbContext>(opt =>
{
Console.WriteLine("=====IT Asset Management=====");
Console.WriteLine("Service running: User Service");
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
I cannot think of anything I have done differently between these three projects.
UPDATE 1:
I found this post: AddDbContext doesn't call DbContext constructor
and one of the answers was that I needed to use migrations. I have not migrated anything from this project, since there is no tables to be made. I am using the default Identity tables, and they are created by another service. But when I added the migrations the Console.WriteLines got printed. Then i ran the project with "dotnet run" but then the prints were gone. Why is this?
So I seemed that one of the answers inside the post in UPDATE 1, actually was the solution to the issue. I did indeed have to use the DbContext in some context. I added a dataseeding class which is called from the program.cs file, and it seems that the console.writelines are printed every time now.
I have a database context that I am trying to update using DB scaffolding.
The command I am using in package manager console is:
Scaffold-DbContext Name="pwa_db" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Data -Force
My appsettings.json looks like this:
"ConnectionStrings": {
"pwa_db": "Server=[My Server];Database=[My DB];Trusted_Connection=True;MultipleActiveResultSets=true"
}
yet when I run the command I get this error:
"A named connection string was used, but the name 'pwa_db' was not found in the application's configuration. Note that named connection strings are only supported when using 'IConfiguration' and a service provider, such as in a typical ASP.NET Core application. See https://go.microsoft.com/fwlink/?linkid=850912 for more information."
The strange thing is that this context was generated using this command so I'm not sure why it would stop working now.
I could easily get around this by just using the full connection string in the command instead of a named connection but then the DB context is generated with the connection string inside which I would prefer to avoid because you always get a warning like this:
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
The warning itself I'm not so concerned about because I'm using a trusted connection, so no credentials are being stored and I'm not expecting the source to ever leave our office.
Can anyone tell me what other configuration bits I might need to look at or what other issues may be at play? Because I can't see what is going wrong from the docs linked in any of the messages.
Have you tried adding the connection string to the appsettings.development.json file too?
I know this is old but maybe this can help someone else.
It works for me and I followed what you did in the scaffolding command and the json "ConnectionStrings". The error mentions IConfiguration. You are probably missing the connection set up in the Startup class.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<DTCCDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("pwa_db")));
...
}...
I want to develop an azure function to process D2C messages from IoT Hub. Previously I developed a function, coding directly on the portal, and I could see logs printed by the function for each message I send using a desktop app I developed. But I need to implement more complex actions, so I decided to do the same but using Visual Studio Code. I installed the neccessary extensions for that and I created a new project following the steps from bellow:
I selected a new folder
Language: C#
Template: IotHubTrigger
Function name: MyFunctionName
Namespace: MyNamespace
Create new local app setting file
Event hub namespace: Skip for now
Name of iot hub endpoint to which messages will be sent: messages/events
I selected the storage account used by my azure function.
In .vscode/settings.json file I changed "azureFunctions.projectRuntime" value from "~3" to "~2", to avoid this warning message in deployment process:
This the default file created:
using IoTHubTrigger = Microsoft.Azure.WebJobs.EventHubTriggerAttribute;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.EventHubs;
using System.Text;
using System.Net.Http;
using Microsoft.Extensions.Logging;
namespace MyNamespace
{
public static class MyFunctionName
{
private static HttpClient client = new HttpClient();
[FunctionName("MyFunctionName")]
public static void Run([IoTHubTrigger("messages/events", Connection = "")]EventData message, ILogger log)
{
log.LogInformation($"C# IoT Hub trigger function processed a message: {Encoding.UTF8.GetString(message.Body.Array)}");
}
}
}
I think that I'm missing the connection string. I can find it in local.settings.json file, the AzureWebJobsStorage value (some values removed):
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName={xxxxx};AccountKey={yyyyy};EndpointSuffix=core.windows.net",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
I think that I have to fill the Connection parameter with AzureWebJobsStorage value:
public static void Run([IoTHubTrigger("messages/events", Connection = "AzureWebJobsStorage")]EventData message, ILogger log)
But where do I need to define this AzureWebJobsStorage value to use it when running on Azure? If I deploy this function, I don't see any log when I send D2C message.
If I understand your question right, you want to know where to define configuration values when running your Functions in Azure? You can do that by going to the Azure Portal, navigating to your Function and clicking on Configuration in the menu (it's under settings).
From there, you can enter a new application setting with the name AzureWebJobsStorage and the connection string to your storage account. Without that, you're definitely not going to see any logging, because your Function just won't run.
Note:
Thanks for being so thorough in your question! However, a side note, I wonder why changing azureFunctions.projectRuntime is necessary?
I would like to ask if there is a way to tell ASP.NET Core 2 to choose different connection strings.
It is quite annoying to keep changing the connection string in the appsettings.json file every time I publish my website to the hosting server..
I am using this code to get connection string.
services.AddDbContext<AppIdentityDbContext>(options =>
options.UseSqlServer(Configuration["Data:WebDataBase:ConnectionString"]));
Maybe there is an easy way but I am thinking of using an if statement in my Startup.cs:
if (local) {
services.AddDbContext<AppIdentityDbContext>(options =>
options.UseSqlServer(Configuration["Data:WebDataBase1:ConnectionString"]));
}
else {
services.AddDbContext<AppIdentityDbContext>(options =>
options.UseSqlServer(Configuration["Data:WebDataBase2:ConnectionString"]));
}
But how can I set this local variable whether the server is my local computer or a live hosting server?
"Data": {
"WebDataBase1": {
"ConnectionString": "Data Source=DatasoruceName;Initial Catalog=DBname;Trusted_Connection=True;Integrated Security=True;"
},
"WebDataBase2": {
"ConnectionString": "Data Source=DatasoruceName;Initial Catalog=DatabaseName;Trusted_Connection=True;Integrated Security=True;"
}
}
Environment specific configuration is not supposed to be specified within code. ASP.NET Core has a mechanism for that which allows you to swap out the configuration dependending on what environment you run on.
While developing your application, you usually run in the Development environment. When you are deploying your application for production, the Production environment is used by default. But if you have other environments, you can totally make up new names for those too and have specific configurations for them. That all is explained in the Environments chapter of the documentation
What environments allow you to do is create multiple appsettings.json files. The ASP.NET Core by default comes with two files: appsettings.json and appsettings.Development.json.
The former is supposed to contain environment unspecific configuration; things that apply to all environments. The latter file contains development-specific configuration and for example adjusts the logging level so that you get more information during development. You can also use these files to specify a default connection string within appsettings.json and overwrite that for development within appsettings.Development.json.
Furthermore, the configuration file follows a very simple pattern: appsettings.<Environment>.json. So if you run in the Production environment, a file named appsettings.Production.json will be loaded if it exists. This mechanism allows you to configure all your environments differently without having to resort to detections within your code.
In addition, there is also the concept of user secrets during development. These are for development-specific configurations that do only apply to yourself but not for example to other members of your team. This is basically a per-machine configuration that allows you to overwrite both the appsettings.json and the appsettings.Development.json. This is described in the user secrets chapter.
Best practice is to avoid connection strings altogether within configuration files though, since you want to avoid that people that happen to have access to your configuration files (for example through your source code) can know the passwords to your database. In that case you can use other mechanisms, for example environment variables that are local to the process.
In your case, where you are just using integrated security, and as such rely on the credentials of the current user, this is not that much of a problem. The only thing you are leaking is the database name. – So you can definitely start out with putting the connection strings into the appsettings files.
So for example, this is how you would configure your database context within your Startup’s ConfigureServices:
services.AddDbContext<AppIdentityDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("WebDataBase"));
Your appsettings.Development.json would look like this:
{
"ConnectionStrings": {
"WebDataBase": "Data Source=DatasourceName;Initial Catalog=DBname;Trusted_Connection=True;Integrated Security=True;"
}
}
And your appsettings.Production.json would look like this:
{
"ConnectionStrings": {
"WebDataBase": "Data Source=DatasourceName;Initial Catalog=DatabaseName;Trusted_Connection=True;Integrated Security=True;"
}
}
I'm currently setting up a multiple publisher/single subscriber architecture, using Azure Storage Queues to buffer events and Azure Functions as the subscriber.
Publisher -> Queue -> Function
Creating the function is no problem, the part I am trying to work out is how to set up a development and a production deployment of the same function. I have created the function in Visual Studio, and the connection is a constant string literal:
public static class FooAdded
{
[FunctionName("FooAdded")]
public static void Run([QueueTrigger("tracker-readings", Connection = "https://foo.queue.core.windows.net/?sv=...")]string myQueueItem, TraceWriter log)
{
log.Info($"C# Queue trigger function processed: {myQueueItem}");
}
}
How can I provide a different value for the connection, depending on whether I deploy to the development environment or the live environment?
To set up local debug environment
You can use local.settings.json file to define the local settings. The prerequisite for using Azure storage locally is that you need to have Azure Storage Emulator running on your machine. In the local.settings.json file define the Azure Storage Account connection string as UseDevelopmentStorage=true. The file should look something like this:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"AzureWebJobsDashboard": "UseDevelopmentStorage=true"
}
}
By default, if no value is provided for the Connection property of the QueueTrigger attribute, it will use the AzureWebJobsStorage setting:
public static class FooAdded
{
[FunctionName("FooAdded")]
public static void Run([QueueTrigger("tracker-readings")]string myQueueItem, TraceWriter log)
{
log.Info($"C# Queue trigger function processed: {myQueueItem}");
}
}
Alternatively, if you want to explicitly specify a connection string, then you can set the Connection to the name of the connection string (not the connection string itself), and add an entry for it in the Values section of the configuration file:
QueueTrigger("tracker-readings", Connection = "CustomConnection")
In the local.settings.json file:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"AzureWebJobsDashboard": "UseDevelopmentStorage=true",
"CustomConnection": "Some custom connection string"
}
}
For more details refer to the official documentation:
Develop and run Azure functions locally
App settings reference
To set up an Azure environment:
The values in local.settings.json do not get published to Azure, and so a value for each setting needs to be defined in the Azure environment that you are deploying to. Note that the values for AzureWebJobsStorage and AzureWebJobsDashboard are set automatically based off the storage account you choose when creating the function.
The easiest way to define settings is through the Azure portal. Go to Application Settings in the Function App and define the actual value of the storage account connection string. You do not need to make any code changes to the azure function, it will automatically pick up the connection string from application settings.
Alternatively, you can use Azure Resource Manager templates, to deploy and update the environment settings programmatically.
Each function app you create in Azure has its own environment, so after providing values for the relevant settings, you can deploy your function to multiple environments in Azure (Dev/Test/Prod etc.) as well as debug your code locally without changing connection strings everytime.
Have the connection string in an environment variable(or an app setting in app services). You can then have the app setting set to to different values in different environments.
I am trying to work out is how to set up a development and a production deployment of the same function.
I am not clear why you want to create a function both as production and development.
Actually, Azure billing is based on your app service plan, so you create one more azure function may not cost more. You could create two function to distinguish them more clearly.
If you still want to use a single function, as Travis said, you could set two connection string in app settings for different environment.
This is a well known scenario and fully supported. All you need to do manage you deployment with the ARM Template and a Parameter File.
Deploy resources with Resource Manager templates and Azure PowerShell
The connection string, along with any other Sub/Resource Group dependent settings are just parameters.
You can set Connection to a config value (something like Connection=AppSettingsKey.DatabaseConnectionString) and then have different settings for different platforms (dev, staging, prod).
Depending on your tooling for build and deployment, you can inject config values for connection strings during one of these steps.
Another option would be to use a Keyvault secret that stores the connection string.