I'm trying to deploy a .NET Core 2.1 application to an Azure Service App. I'm able to run my solution locally successfully, but when I deploy the application out to Azure Service Apps, I begin to get the following error when I call dotnet /home/site/wwwroot/MyApplication.dll on the application.
Unhandled Exception: System.ArgumentNullException: String reference not set to an instance of a String.
Parameter name: s
at System.Text.Encoding.GetBytes(String s)
at MyApplication.Startup.ConfigureServices(IServiceCollection services) in /home/vsts/work/1/s/MyApplication/Startup.cs:line 51
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
at Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureApplicationServices()
at Microsoft.AspNetCore.Hosting.Internal.WebHost.Initialize()
at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
at MyApplication.Program.Main(String[] args) in /home/vsts/work/1/s/MyApplication/Program.cs:line 23
at MyApplication.Program.<Main>(String[] args)
bash: line 1: 1096 Aborted (core dumped) dotnet /home/site/wwwroot/MyApplication/MyApplication.dll
This appears that I have a null reference on line 51 of my Startup.cs method, inside the ConfigureServices method. This correlates to the line where I build my configuration, using _configuration = builder.Build()
public IConfiguration _configuration { get; set; }
public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
{
_configuration = configuration;
_hostingEnvironment = hostingEnvironment;
}
public void ConfigureServices(IServiceCollection services)
{
var builder = new ConfigurationBuilder()
.SetBasePath(_hostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
_configuration = builder.Build(); // this is line 51
var appSettingsSection = _configuration.GetSection("AppSettings");
...
}
Which the only thing that I can think of would be my appsettings.json transformation. In dev, I point to my dev server, in prod, I point to production. So after the build, I have a file transformation set up to make this change.
However, I've pulled the generated appsettings.json from the App Service, inserted it into my local appsettings.json, and it still compiles and runs just fine.
Here's the appsettings.json that is getting deployed. It's the exact same, except for the name of the server and username/password strings.
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AppSettings": {
"SecretKey": "MySecretKey",
"SuperAdminEmail": "myemail#domain.com"
},
"ConnectionStrings": {
"AuthDatabase": "Server=prod.mydomain.com;Database=Authentication;User Id=myUser;Password=Password1!;TrustServerCertificate=true"
},
"AllowedHosts": "*"
}
I can't see what would be causing this. I tried deploying this to an AWS Lightsail Ubuntu 18.04 instance to see if that works. I got the same error sometimes, however, after enough tries of killing the service, rebooting the server, and other items, I got it to run for 10 minutes, before it crashed again.
I'm not great with logging, especially on Azure App Services... how can I go about tracking down WHAT string is null, and what I can do to verify that it has any necessary values?
As you mentioned in the comments, the value of _hostingEnvironment.ContentrootPath on the AppService is '/home' and this is the issue as your application is not able to read something due to the wrong path referred by the AppService.
You can change your ContentRootPath by following this SO post and accordingly choose the approach as per your requirements.
Related
In a .net core application, why I am always in "production", even if the ASPNETCORE_ENVIRONMENT is 'development'?
class Program {
static void Main(string[] args) {
var builder = new ConfigurationBuilder();
BuildConfig(builder);
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Build())
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
// ...
var host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) => {
switch (serviceToUse) { // ... } })
.UseSerilog()
.Build();
IService svc;
// ... got the corresp service...
svc.Run();
}
static void BuildConfig(IConfigurationBuilder builder) {
string env = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")
?? "Production";
builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env}.json", optional: true)
.AddUserSecrets<Program>()
.AddEnvironmentVariables();
}
}
However, when I test
I get rather unexpected values in the test setting:
[13:36:55 ERR] Environment.GetEnvironmentVariable('ASPNETCORE_ENVIRONMENT')=Development
[13:36:55 ERR] Environment.GetEnvironmentVariable('DOTNET_ENVIRONMENT')=Development
[13:36:55 ERR] The test setting is 'test value from secrets.json';
having this in projet:
My question is why does it take the Production settings if I am in Development mode?
It’s unclear to me where you are retrieving the configuration using this._config.GetValue<string>("test"). My guess is that you are retrieving it from the IConfiguration injected via DI.
In that case, you should note that the Host.CreateDefaultBuilder will also already come with its own configuration setup which does not really care about what you are doing before with your BuildConfig.
The default host builder will already set up configuration using the files appsettings.json and appsettings.<env>.json (and others) and will register that configuration as the IConfiguration singleton that is being provided to your application. In order to register the environment-specific file, it will use the environment from the IHostEnvironment which is what decides what actual environment your application is running on.
With ASP.NET Core and the web host, it is possible to configure this environment using the ASPNETCORE_ENVIRONMENT environment variable. However, since you are not using ASP.NET Core but just the host builder on its own, this environment variable is not being used. Instead, the default host builder will look for the environment variable DOTNET_ENVIRONMENT.
Alternatively, you can also call hostBuilder.UseEnvironment to set the environment explicitly if you want to take it from a different environment variable.
Finally, when you are setting up your own configuration, I would suggest you to reconfigure your host so that it will use this configuration object itself instead of building its own (which may or may not match your desired setup). You can do that by calling hostBuilder.ConfigureAppConfiguration.
I am new to .net core development and I am trying to deploy the web app .net core 2.1 to IIS in windows 10. I have followed all the steps including creating applicationpool 'No Managed Code' and everything worked fine. Later after 2 days it stopped working then I redoployed my project using the release type to Debug and here I am getting this exception displayed in the brower which is the same in the log file.
However, the same app works fine in visual studio. My machine has the following .net packages installed.
.Net Core Runtme 2.1.7(x64)
.Net Core 2.1.7 - Windows Server Hosting
.net Core Runtime 2.1.7(x86)
.Net Core SDK 2.1.503 (x86)
.Net Core SDK 2.1.503(x64)
Microsoft Web Deploy 4.0
After going through all the articles available and tweaking and changing the app was finally worked but later it stopped working and giving the above error.
My Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.Configure<DataProtectionTokenProviderOptions>(o =>
{
o.Name = "Default";
o.TokenLifespan = TimeSpan.FromHours(1);
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("DefaultConnection"),
mysqloptions => {
mysqloptions.ServerVersion(new Version(8, 0, 13), ServerType.MySql);
}));
services.AddTransient<IProductRepository, EFProductRepository>();
services.AddScoped<Cart>(sp => SessionCart.GetCart(sp));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddIdentity<ApplicationUser, IdentityRole>(
options =>
{
options.Stores.MaxLengthForKeys = 128;
options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultAuthenticatorProvider;
options.SignIn.RequireConfirmedEmail = false;
options.Password.RequireDigit = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
}
)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddRoles<IdentityRole>()
//.AddDefaultUI();
.AddDefaultTokenProviders();
//Authentication
services.AddDbContext<MainContext>(options =>
options.UseMySql(Configuration.GetConnectionString("ModelConnectionString"),
mysqloptions => {
mysqloptions.ServerVersion(new Version(8, 0, 13), ServerType.MySql);
mysqloptions.MigrationsAssembly("GasStationApp");
}));
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, MyUserClaimsPrincipalFactory>();
services.AddMvc().AddNToastNotifyToastr(new ToastrOptions()
{
ProgressBar = false,
PositionClass = ToastPositions.TopFullWidth
}
);
services.Configure<IISOptions>(options => {
options.AutomaticAuthentication = false;
options.ForwardClientCertificate = false;
});
My Program.cs
public class Program
{
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.RollingFile("logs/log-{Date}.txt")
.CreateLogger();
try
{
Log.Information("Starting web host");
BuildWebHost(args).Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.ConfigureAppConfiguration((builderContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: false);
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseSerilog() // <-- Add this line
.Build();
}
The app works fine in VS2017 but does not work when deployed to IIS in Windows 10 Please help me resolve this issue. Any advice would be helpful. Thanks in advance.
The files are appsettings.json and appsettings.{environment}.json. ASP.NET Core relies on an environment variable (ASPNETCORE_ENVIRONMENT) to determine which config(s) to load. By default, this is set to Development in Visual Studio, which then of course causes appsettings.Development.json to be utilized. When you publish your app, you should then set the ASPNETCORE_ENVIRONMENT environment variable on the destination to Production, which would then cause appsettings.Production.json to be utilized. (I do not remember if casing matters, though it might, especially with case-sensitive file systems like those utilized by Linux and Mac OS. It's best to just ensure that the file is named appsettings.Production.json, just in case.)
Additionally, the environment-specific JSON file overrides the inspecific one. In other words, appsettings.json is read in first, and then appsettings.{environment}.json is read in. Anything in appsettings.json set also in the environment-specific version will be overridden by that value in the environment-specific version.
In short, the pattern should be this. Any config not specific to a particular environment should go into appsettings.json. Any environment-specific config should go into the respective environment-specific config files. I've found it to be good practice to put placeholders for environment-specific and secret config values in appsettings.json as well. For example:
"ConnectionStrings": {
"DefaultConnection": "[CONNECTION STRING]"
}
Due to the way that config layers upon itself, and since appsettings.json is the first thing to be loaded in, you can provide the actual value in any other form of config (environment-specific JSON, environment variables, user secrets, Azure Key Vault, etc.). This then documents all your app's config in one place, with clear indication of what needs to actually be provided.
This kinda weird, the deployed files has three appsettings.json, appsettings.development.json, appsettings.production.json. I failed to look into this because, I thought the default appsettings.json file should have the original configuration but it turned out that the appsettings.json and appsettings.development.json in the deployed folder has only the default settings which were available when you create a web app project in VS 2017. appsettings.production.json file has the original configuration.
Solution.
Copied the appsettings.production.json file and renamed it to appsettings.json and the web app works fine now.
Due to my limited reputation point (as I am still new to StackOverflow), I am unable to comment on other posts, so I have to create a new question. However, this does not appear to be the same problem indicated in the post .net-core-2.0 azure app service 502.5 error. I am not getting the 502.5 error but rather this message:
I did perform the steps in the other post anyway to no success. I am still getting the same problem shown above. I even completely deleted my Asp.Net Core 1.1 app service and related SQL database and then re-created a new one to host my Asp.Net Core 2.0 app service. Still, no success.
Does anybody have any fresh ideas about how to fix this problem?
Thank you for your feedback.
UPDATE:
I got the following error:
SqlException: Login failed for user 'null'. System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, object providerInfo, bool redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, bool applyTransientFaultHandling)
I just did a migration for our Azure web app from ASP.Net Core 1 to 2.0. It also uses Azure SQL. Interestingly it didn't throw any SQL related errors as you experienced. Here are some note hopefully helpful to you:
use 'Tools|Nuget package manager' to update all components to the latest 2.0 version, including ASP.Net Core and EF Core.
fix errors shown after the updates. Some are easy to fix simply following the visual studio suggestions. Some might be tricky, and needs a lot of googling and researching. the key places needs to be careful are shown below:
in Program.cs:
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})
.UseSetting("detailErrors", "true")
.UseStartup<Startup>()
.UseSetting("DesignTime", "true")
.CaptureStartupErrors(true)
.Build();
}
startup constructor becomes:
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
in ConfigureServices methods,
// authentication cookie
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/"; //new Microsoft.AspNetCore.Http.PathString("/");
options.AccessDeniedPath = "/"; //new Microsoft.AspNetCore.Http.PathString("/");
});
// Add EntityFramework's Identity support.
services.AddEntityFrameworkSqlServer();
// Add ApplicationDbContext.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"])
);
In Configure method, I added this for server side debugging:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
on Azure web portal, add a new setting key/value pair, Hosting:Environment Development:
Note: this is for debugging purpose (server-side mainly), and can be removed from production version.
during publishing, check the box to remove additional files at destination:
Here are my lessons learned while upgrading to ASP.Net Core 2.0. If you start from your original project again, the SQL bug might not be there by following one or two points mentioned above.
In .NET Core 2 Web API app, I could override configuration urls using appsettings.json, but in the official docs they introduced extra file "hosting.json", Why? What's the point of adding complexity?
Below code is fully working using appsettings.json:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) //see Side note below
.AddJsonFile("appsettings.json", optional: true)
.AddCommandLine(args)
.Build();
return WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
}
}
appsettings.json content:
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
},
"urls": "http://*:5005/"
}
Side note:
Commenting .SetBasePath(Directory.GetCurrentDirectory()) will keep VS 2017 debug mode operational (means apply launchSettings.json, and auto launch url) otherwise it won't. I guess its related to the CreateDefaultBuilder implementation.
I think, hosting.json is a configuration file used specifically for asp.net core application hosting. (if you know more about hosting)
WebHostBuilder directly maps its keys with the hosting.json file and it doesn't have the capability to load the config section as we do in normal configuration settings.
According to link attached in your post
Use Configuration to configure the host. In the following example,
host configuration is optionally specified in a hosting.json file. Any
configuration loaded from the hosting.json file may be overridden by
command-line arguments.
If only we explicitly us hosting.json then the WebHostBuilder configurations can be modified using dotnet command.
for example
dotnet run --urls "http://*:8080"
this will override the urls from hostings.json file.
Hopefully, this may give some idea.
PC: hosting.json can be renamed like myappsettings.json it can
have configuration and Web Host Builder configuration.
I have created a new ASP.NET Core web application in Visual Studio 2015. I've also set up an Azure web app to pull in the app from GitHub and run it. This works fine, but I'm having trouble connecting to the database on Azure.
Locally, this works, and it uses config.json and in code Data:DefaultConnection:ConnectionString for the connection string.
How can I leave the code as it is, and have it work in Azure too? I've tried setting the Application Settings in the portal, both the Connection Strings and the App Settings. And using both "SQLCONNSTR_DefaultConnection" and "Data:DefaultConnection:ConnectionString" as the key.
(Setting the App Settings doesn't seem to work by the way. I think the value I provide is too long).
So how can I provide the connection string for my Azure database to my Azure web app (ASP.NET 5), without checking it in in source control?
Update
My Startup.cs looks like this (see the full file on GitHub):
public Startup(IHostingEnvironment env)
{
var configuration = new Configuration()
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
if (env.IsEnvironment("Development"))
{
configuration.AddUserSecrets();
}
configuration.AddEnvironmentVariables();
Configuration = configuration;
}
In the ConfigureServices method, there is also:
services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings"));
and, also in the ConfigureServices method:
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]))
.AddDbContext<InvoicesDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
// So this is where I want my app in Azure to use the connection string I
// provide in the portal
Short answer
I've tried setting the Application Settings in the portal, both the Connection Strings and the App Settings. And using both "SQLCONNSTR_DefaultConnection" and "Data:DefaultConnection:ConnectionString" as the key.
You're close.
Go to Azure web app > configure > connection strings.
Add a connection string with the name DefaultConnection.
Use Configuration.Get("Data:DefaultConnection:ConnectionString") to access it.
Example using timesheet_db instead of DefaultConnection
This is an example from my own timesheet application. My connection string was named timesheet_db. Just replace all instances of that string with DefaultConnection to adapt the example to your use case.
Azure web app configuration
Azure web app service control manager
The online service control manager at https://myWebAppName.scm.azurewebsites.net/Env will show the connection strings.
Startup.cs
Setup configuration settings in Startup so that the environmental variables overwrite the config.json.
public IConfiguration Configuration { get; set; }
public Startup()
{
Configuration = new Configuration()
.AddJsonFile("config.json")
.AddEnvironmentVariables(); <----- will cascade over config.json
}
Configure the database in Startup.
public void ConfigureServices(IServiceCollection services)
{
services
.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ProjectContext>(options =>
{
var connString =
Configuration.Get("Data:timesheet_db:ConnectionString");
options.UseSqlServer(connString);
});
}
Of course, the example uses a connection string named timesheet_db. For you, replace all instances of it with your own connection string named DefaultConnection and everything will work.
In RC2 I had to change how my connection strings were read to get them to work in Azure. In my case I had to ensure the Azure connection string was named "DefaultConnection", and accessed by:
RC1:
{
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\MSSQLLocalDB;Database=db;Trusted_Connection=True;"
}
}
}
Accessed by:
var conn = Configuration["Data:DefaultConnection:ConnectionString"];
RC2:
{
"Data": {
},
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=db;Trusted_Connection=True;"
}
}
Accessed by:
var conn = Configuration.GetConnectionString("DefaultConnection");
You have a number of options to set your connection string.
The default setup class get the enviroment settings from different sources.
You could set your connection string in config.production.json. or config.staging.json. See the startup class
public Startup(IHostingEnvironment env)
{
// Setup configuration sources.
var configuration = new Configuration()
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
if (env.IsEnvironment("Development"))
{
// This reads the configuration keys from the secret store.
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
configuration.AddUserSecrets();
}
configuration.AddEnvironmentVariables();
Configuration = configuration;
}
I think you are looking for SlotSticky Settings
Use this command in Azure PowerShell to set 2 app settings as sticky to the slot
Set-AzureWebsite -Name mysite -SlotStickyAppSettingNames #("myslot", "myslot2")
And this command to set 2 connection strings as sticky to the slot
Set-AzureWebsite -Name mysite -SlotStickyConnectionStringNames #("myconn", "myconn2")