Publish .Net Core app with Entity Framework to IIS - sql

I am trying to published my .Net Core app to IIS that uses Entity Framework. I can publish it fine but no database is included in the publish wizard.
I have followed different tutorials including this:
https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/?view=aspnetcore-3.0
However none tell you how to deploy your database or what needs to be done if using entity framework. I'm relatively new to published and hosting web apps so I'm not sure what to do.
At the moment my frontend of the web app loads on IIS but when I go to login it brings up a 500 error. I have included my connection string and added a user and gve correct permissions under SSMS.
It came to my attention when publishing it shows "no databases found int he project".
Would this effect me not being able to access the database and bringing up the 500 error when logging in and how do i fix this.
No databases found in the project

How did you created your database locally in the first place? Did you manually ran the database update command? I would suggest you add to your startup.cs file a code to ensure your database is created and any missing migrations were applied, you can achieve this within your Configure method:
using (var scope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
scope.ServiceProvider.GetRequiredService<DbContext>().Database.Migrate();
}
Or just EnsureCreated() instead of Migrate() if you don't to apply missing migrations on future loads.

It seems that you need to change your ConnectionString configuration in appsettings.json from the format
"Data": {
"DefaultConnection": {
"ConnectionString": "xxxxxx"
}
}
to:
"ConnectionStrings": {
"DefaultConnection": "xxxxxx"
}
And your startup with
services.AddDbContext<ApplicationDbContext>(opt => opt.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));

Related

Can I use Serilog Enrichers to get Machine / Hostname Name?

I have been searching on how I can get client hostname/computer name using ASP.Net Core MVC using .net 3.1 .
There are threads here on SO but most of them are not working on intranet apps (clients used VPN to connect to network).
I've seen some suggestion on some thread to use Serilog Enrichment.
My question is how can I use this one? Can I really get the Machine Name (value passed on the application to db) using this plugin?
You should be able to. Try this configuration on:
"Using": [ "Serilog.Enrichers.ClientInfo" ],
"Enrich": [ "WithMachineName", "WithClientIp" ]
You'd need the nuget package Serilog.Enrichers.ClientInfo
UPDATE:
You can also write the logs directly to the database using one of the database sinks. For eg, to write to a MSSQL database, you could use the sink here: Serilog.Sinks.MSSqlServer
This gives you control over what properties and you want to log including Client Info

ConnectionString in .NET Core 6

I'm playing around in .NET Core 6 Web API's. But I can't seem to figure out how the connection string works now.
The first part here that is commented out works fine. But I need to be able to throw the program on different systems and change the connection string with appsettings.json.
The second part is what I attempted but that doesn't work.
Config connection string in .net core 6 is where I got it from.
//builder.Services.AddDbContext<TodoContext>(opt =>
// opt.UseSqlServer(#"Data Source=JOHANDRE\\SQL2017; Database=ToDoItems; User=xxx; Password=xxx;"));
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseSqlServer(builder.Configuration.GetConnectionString("ToDoItemsDatabase")));
My appsettings.json:
"ConnectionStrings": {
"ToDoItemsDatabase": "Server=JOHANDRE\\SQL2017; Database=ToDoItems; User=xxx; Password=xxx;"
},
I want to add that it does not throw errors. it just does not seem to find the connection.
Problem is how you start your Web API from the service. You are using Process without setting ProcessStartInfo.WorkingDirectory to the folder containing exe and configuration and the started process shares the working directory with parent one, so either move appsettings.json to the parent project folder or set the WorkingDirectory to match the directory containing the exe:
toDoTest.StartInfo.UseShellExecute = false;
toDoTest.StartInfo.WorkingDirectory = "C:\\Develop\\ToDoMVCtutorial\\bin\\Release\\net6.0\\publish\\";
Also you can try redirecting your Web API output to capture the logs.

How to get .Net Core 3.1 Azure WebJob to read the AzureWebJobsStorage connection string from the Connected Services setup?

I'm building a WebJob for Azure to run in an App Service using .Net Core 3.1.
The WebJob will be triggered via Timers (it's basically a cronjob).
Timer triggers require the AzureWebJobsStorage connection string as storage is required for Timer events.
When deployed to Azure App Service, I want the WebJob to read the AzureWebJobsStorage value from the properties on the App Service.
I have a Resource Manager template that deploys my infrastructure and sets the connection string on my App Service resource:
"connectionStrings": [
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('_StoreAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('_StoreAccountName')), '2019-04-01').keys[0].value,';EndpointSuffix=core.windows.net')]"
}
],
When testing my WebJob locally, I need to set that AzureWebJobsStorage value so that my local builds can connect to storage.
Since I re-deploy the infrastructure all the time as I make tweaks and changes to it, I do not want to manually maintain the long connection string in my appsettings.json or a local.settings.json file.
In Visual Studio, In theory, I can add a Service Dependency to the project for Azure Storage and that will store the connection string in my local Secrets.json file. Then, when I redeploy the infrastructure I can use the Visual Studio UI to edit the connection and re-connect it to the newly deployed storage account (i.e. it will create and update the connection string without me having to do it manually).
When I add Azure Storage as a connected service, Visual Studio adds a line like this in my Secrets.json file:
"ConnectionStrings:<LABEL>": "DefaultEndpointsProtocol=https;AccountName=<LABEL>;AccountKey=_____________;BlobEndpoint=https://<LABEL>.blob.core.windows.net/;TableEndpoint=https://<LABEL>.table.core.windows.net/;QueueEndpoint=https://<LABEL>.queue.core.windows.net/;FileEndpoint=https://<LABEL>.file.core.windows.net/",
and this in my ServiceDependencies/serviceDependencies.local.json:
"storage1": {
"resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/Microsoft.Storage/storageAccounts/<LABEL>",
"type": "storage.azure",
"connectionId": "<LABEL>",
"secretStore": "LocalSecretsFile"
}
and this in my ServiceDependencies/serviceDependencies.json:
"storage1": {
"type": "storage",
"connectionId": "<LABEL>"
}
Where <LABEL> is the name of the Storage Account (in both JSON snippits).
When I run the WebJob locally, it loads the appsettings.json, appsettings.Development.json, secrets.json, and Environment Variables into the IConfiguration.
However, when I run the WebJob locally it dies with:
Microsoft.Azure.WebJobs.Host.Listeners.FunctionListenerException: The listener for function 'Functions.Run' was unable to start.
---> System.ArgumentNullException: Value cannot be null. (Parameter 'connectionString')
at Microsoft.Azure.Storage.CloudStorageAccount.Parse(String connectionString)
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.get_TimerStatusDirectory() in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Scheduling\StorageScheduleMonitor.cs:line 77
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.GetStatusBlobReference(String timerName) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Scheduling\StorageScheduleMonitor.cs:line 144
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.GetStatusAsync(String timerName) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Scheduling\StorageScheduleMonitor.cs:line 93
at Microsoft.Azure.WebJobs.Extensions.Timers.Listeners.TimerListener.StartAsync(CancellationToken cancellationToken) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions\Extensions\Timers\Listener\TimerListener.cs:line 99
at Microsoft.Azure.WebJobs.Host.Listeners.SingletonListener.StartAsync(CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Singleton\SingletonListener.cs:line 72
at Microsoft.Azure.WebJobs.Host.Listeners.FunctionListener.StartAsync(CancellationToken cancellationToken, Boolean allowRetry) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Listeners\FunctionListener.cs:line 69
I have confirmed that if I add the ConnectionStrings:AzureWebJobsStorage value to my appsettings.json then the program runs fine.
So I know it's an issue with the loading of the AzureWebJobsStorage value.
Has anyone figured out how to get an Azure WebJob, running locally, to properly read the connection string that Visual Studio configures when adding the Azure Storage as a Connected Service?
What's the point of adding the Connected Service to the WebJob if it won't read the connection string?
(note: I realize that the WebJobs docs https://learn.microsoft.com/en-us/azure/app-service/webjobs-sdk-how-to#webjobs-sdk-versions state that Because version 3.x uses the default .NET Core configuration APIs, there is no API to change connection string names. but it's unclear to me if that means the underlying WebJobs code also refuses to look in the Connected Services setup or if I'm just missing something)
I found a work-around, but I don't like it... basically check if there's a ConnectionStrings:AzureWebJobsStorage value at the end of my ConfigureAppConfiguration code and if not, try and read the one from the secrets.json file and set the ConnectionStrings:AzureWebJobsStorage to that value.
private const string baseAppSettingsFilename = "appsettings.json";
private const string defaultStorageAccountName = "<LABEL>";
...
IHostBuilder builder = new HostBuilder();
...
builder.ConfigureAppConfiguration(c =>
{
c.AddJsonFile(
path: baseAppSettingsFilename.Replace(".json", $".{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json"),
optional: true,
reloadOnChange: true);
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development")
{
c.AddUserSecrets<Program>();
}
// Add Environment Variables even though they are already added because we want them to take priority over anything set in JSON files
c.AddEnvironmentVariables();
IConfiguration config = c.Build();
if (string.IsNullOrWhiteSpace(config["ConnectionStrings:AzureWebJobsStorage"]))
{
string storageConnectionString = config[$"ConnectionStrings:{defaultStorageAccountName}"];
if (string.IsNullOrWhiteSpace(storageConnectionString))
{
throw new ConfigurationErrorsException($"Could not find a ConnectionString for Azure Storage account in ConnectionStrings:AzureWebJobsStorage or ConnectionStrings:{defaultStorageAccountName}");
}
c.AddInMemoryCollection(new Dictionary<string, string>() {
{ "ConnectionStrings:AzureWebJobsStorage", storageConnectionString }
});
}
});
This seems exceedingly dumb but even looking at the Azure SDK source code I'm thinking it's just hard coded to a single key name and the Service Configuration in Visual Studio is simply not supported: https://github.com/Azure/azure-webjobs-sdk-extensions/blob/afb81d66749eb7bc93ef71c7304abfee8dbed875/src/WebJobs.Extensions/Extensions/Timers/Scheduling/StorageScheduleMonitor.cs#L77
I just ran into a similar problem where VS2019 automatically configured Function and Function1 with Connection = "ConnectionStrings:AzureWebJobsStorage" and it couldn't find that. Simply changing it to Connection = "AzureWebJobsStorage" worked like a charm.
FYI - I also had to change BlobTrigger("Path/{name}"... to BlobTrigger("path/{name}"...
re: Microsoft.Azure.StorageException: The specified resource name contains invalid characters

Set connection string in Azure DevOps pipeline

I have some integration tests that I'd like to run against LocalDB.
My config.json file has the following section...
{
"ConnectionStrings": {
"DefaultConnection": ""
}
}
Is it possible to set this value in the build config?
You can achieve this in several ways. Like Powershell scripts to replace the values and ofcourse this replace token extension
You should define your variable like below
{
"ConnectionStrings": {
"DefaultConnection": "#{connectstring}#"
}
}
During the deployment it will get replaced with your actual values.
Refer this SO for more details
I solved it using the Configuration of Connection Strings in the target App Service for my Azure Web App.
Within the App startup I use this code to access it, and if running local for debugging it uses the config.json or secrets.json.
Configuration.GetConnectionString("My_ConnectionString_Name");
So you can have your local db mentioned in the secrets.json and set the productive database in the Azure App connection string (if applicable).

Running RavenDB as an EmbeddableDocumentStore and accessing RavenDB Management Studio

I'm playing with an embedded RavenDB => RavenDB-Embedded.1.0.499 package installed via NuGet in Visual Studio 2010. It's being used in a current project that I started after reading this excellent MSDN article:
Embedding RavenDB into an ASP.NET MVC 3 Application
Now I'd like to access the RavenDB Management Studio (Web UI).
I followed the steps described here: Is it possible to connect to an embedded DB with Raven Management Studio and here Running RavenDB in embedded mode with HTTP enabled but I didn't get the point.
This is the code I'm using to initialize the DocumentStore:
_documentStore = new EmbeddableDocumentStore
{
ConnectionStringName = "RavenDB",
UseEmbeddedHttpServer = true
};
and this is the ConnectionString present in Web.config:
<add name="RavenDB" connectionString="DataDir = ~\App_Data\Database" />
I also read the steps described in RavenDB: Embedded Mode. I tried to start the server manually:
// Start the HTTP server manually
var server = new RavenDbHttpServer(documentStore.Configuration,
documentStore.DocumentDatabase);
server.Start();
but the above code seems outdated since I have no RavenDbHttpServer, documentStore.Configuration and documentStore.DocumentDatabase. I managed to find Raven.Database.Server.HttpServer but the other objects are missing in the _documentStore.
So, the question is:
How can I hit the Web UI to visualize my embedded database docs? What's the URL I should put in my browser address bar?
Any advice is appreciated.
EDIT: I've found a way of getting it to work. As I described in my blog post it may not be the best approach but it does work:
RavenDB Embedded with Management Studio UI
Note: one downside of the above approach is that I'm not able to access the database in my app because once it has been opened by the server it gets locked. This way I have to stop the server and then reload my app in the browser.
I hope RavenDB's gurus out there have a better/correct approach... just let us know.
I've never had to run the server manually in order to access the management studio. The only few steps that haven't been mentioned in your question that I usually do:
// Add the following line prior to calling documentStore.Initialize()
Raven.Database.Server.NonAdminHttp.EnsureCanListenToWhenInNonAdminContext(8080);
Copy Raven.Studio.xap into the root folder of my web project.
When my web application is running, the RavenDB Management Studio is then accessible at http://localhost:8080.