Turn off logging EFCore logging for nunit tests - c#

I have unit tests for a project with EF core 7.0.
I use the InMemory provider to test queries to the database.
Each test generates a lot of messages like this:
Context 'MyContext' started tracking 'Player' entity. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see key values.
If I turn this option on, I see the following message:
Context 'MyContext' started tracking 'Player' entity with key '{Id: 1}'.
Is there a way to turn such messages off completely?
UPDATE
I tried to use NullLoggerProvider, so my method for database creation looks as follows:
var loggerFactory = new LoggerFactory();
loggerFactory.AddProvider(NullLoggerProvider.Instance);
return new(new DbContextOptionsBuilder<MyContext>()
.UseLoggerFactory(loggerFactory)
.UseInMemoryDatabase(databaseName)
.Options);
I've also added appsettings.json to the Test project, which looks:
{
"Logging": {
"LogLevel": {
"Default": "None"
}
}
}
but it didn't help.

Related

Problem with HealthChecks.UI.SqlServer.Storage migrations

I'm trying to use Healthchecks UI in my asp.net core application with SqlServer.Storage for history purposes. It works with InMemoryStorage (without history part, of course).
So, Startup.cs code is like this:
services.AddHealthChecks()
.AddHangfire(...)
.AddDbContextCheck<...>("Database")
.AddAzureBlobStorage(...)
.AddProcessAllocatedMemoryHealthCheck(...)
.AddCheck(...);
services
.AddHealthChecksUI(settings =>
{
settings.SetEvaluationTimeInSeconds(...);
settings.SetMinimumSecondsBetweenFailureNotifications(...);
settings.MaximumHistoryEntriesPerEndpoint(...);
})
.AddSqlServerStorage(Configuration.GetConnectionString("..."));
later, additional configuration is in Configure method
Everything works when AddInMemoryStorage is used instead of AddSqlServerStorage. When AddSqlServerStorage is used, app crashes on startup, with
SqlException: Invalid object name 'Configurations'.
Sure, SQL tables are missing, but I cannot force [migration from nuget package to be applied to database.
Of course, I could copy/paste migration or create tables in database but I would like to skip that because of future changes and keeping code clean.
Can someone point me in right direction to solve this?
Thanks
I've managed to solve it.
Since migrations weren't applied to database, I ran them in Main like this.
var app = CreateHostBuilder(args).Build();
//run heaalthchecksui migrations
using (var scope = app.Services.CreateScope())
{
var healthChecksDb = scope.ServiceProvider.GetRequiredService<HealthChecksDb>();
healthChecksDb.Database.Migrate();
}
app.Run();
So, maybe that workouround could help someone with similar problem.
In Configure:
using IServiceScope scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope();
using HealthChecksDb healthChecksDb = scope.ServiceProvider.GetRequiredService<HealthChecksDb>();
healthChecksDb.Database.Migrate();
In ConfigureServices:
// build an intermediate service provider
ServiceProvider serviceProvider = services.BuildServiceProvider();
try
{
using HealthChecksDb healthChecksDb = serviceProvider.GetRequiredService<HealthChecksDb>();
healthChecksDb.Database.Migrate();
}
finally
{
serviceProvider.Dispose();
}

Entity Framework Core automatically inserts double quotes in Oracle database

I'm developing an ASP.NET Core 6 MVC web app, and I want to use the integrated Identity platform to handle user roles and authentication.
Since this web app must be cross-database, I need to make this work on a Oracle autonomous database. So I've installed the Oracle.EntityFrameworkCore package from NuGet, and switched to the UseOracle method in the ConfigureServices method of my Startup.cs file.
services.AddDbContext<ApplicationDBContext>(options =>
{
// options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
options.UseOracle(Configuration.GetConnectionString("OracleConnection"))
});
The connection can be established, but here's the issue: when prepping up the UserManager and creating User Roles in Startup.cs, any standard method that EF Core invokes actually executes a query with double quotes around object names, e.g. doing this
private async Task CreateRoles(IServiceProvider serviceProvider)
{
//Custom roles
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
string[] roleNames = { "Admin" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
var roleExist = await RoleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
// create the roles and seed them to the database: Question 1
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
await CreateUser(serviceProvider, "Admin");
}
This code executes this query:
SELECT "a"."Id", "a"."ConcurrencyStamp", "a"."Name", "a"."NormalizedName"
FROM "AspNetRoles" "a"
WHERE "a"."NormalizedName" = :normalizedName_0
FETCH FIRST 1 ROWS ONLY
which fails.
It searches for an "AspNetRoles" table, double quotes means it searches EXACTLY that, and thus it doesn't exist because tables are all uppercase in Oracle autonomous database, and cannot be CamelCase. I get an error ORA-00942.
I can't figure out how to make EF Core NOT use double quotes without reverting to the DevArt Oracle package.
How can I solve this?
In the end I understood the problem wasn't only for Identity Provider tables.
So I've implemented linq2db and linqToDb.Identity and made it work by putting
OracleTools.DontEscapeLowercaseIdentifiers = false;
in the ConfigureServices method of Startup.cs.
Quick note: LinqToDb.Identity NuGet package is stuck on a May 2021 release which is incompatible with recent Linq2DB packages due to various missing references to this (e.g. this.GetTable) in the IdentityDataConnection class, so you might as well download the source code and include it in your project (or make a class library out of it) and fix the class.
Repo link: https://github.com/linq2db/LinqToDB.Identity

A named connection string was used, but the name 'DefaultConnection' was not found in the application's configuration

I have a DbContext named FileManagerContext in my DOTNET 6 API:
public class FileManagerContext : DbContext {
public FileManagerContext(DbContextOptions<FileManagerContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
}
}
It's a pretty simple DbContext with a simple Entity in it. Anyway, I have this appsettings.json too:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=FM;User=SA;Password=1234;"
},
"AllowedHosts": "*"
}
And here is the startup snippet in Program.cs's top level statement:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<FileManagerContext>(
opt => opt.UseSqlServer("name=DefaultConnection"));
I could use migrations in the case. All thing goes good. I can add migrations and I can update database successfully. But when I run the application and try to use DbContext I get this error:
System.InvalidOperationException: A named connection string was used,
but the name 'DefaultConnection' 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.
I've also tried to get the connection string like this:
var cs = builder.Configuration.GetConnectionString("DefaultConnection");
But it returns null. Can anybody help me through please?
From the sounds of the error, your configuration might not be getting picked up.
How is the configuration being created?
Do you see AddConfiguration extension method being called on the Webhost? the contents of the method should look something like the following:
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, false)
.AddJsonFile($"appsettings.{envLower}.json", true, true)
.AddEnvironmentVariables()
.Build();
The the call to build may or may not exist. We typically manually create the configuration object because we need to construct the Loggers and then use AddConfiguration extension method to add that object to the host.
If you don't see that, please take a look at the documentation from Microsoft for guidance on how to set it up. Configuration Documentation
Alternatively, you can get the connection string via the Configuration Object and pass it to the UseSqlServer method.
services.AddDbContext<FileManagerContext>((provider, options) => {
IConfiguration config = provider.GetRequiredService<IConfiguration>();
string connectionString = config.GetConnectionString("DefaultConnection");
options.UseSqlServer(connectionString);
});
in the alternate method, a service provider is passed to the action. You can use the provider to fetch the IConfiguration object from the DI container.
use builder.Configuration
builder.Services.AddDbContext<FileManagerContext>(options =>
{
options.UseSqlServer(builder.Configuration["ConnectionStrings:DefaultConnection"]);
});
or builder.Configuration.GetConnectionString as #Serge mentions
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
Don't use "name=DefaultConnection" since it is treated as connection string name including "name="
I am using this syntax in program.cs and everything is working properly
var builder = WebApplication.CreateBuilder(args);
.......
builder.Services.AddDbContext<FileManagerContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
and since you are using Ms Sql Server , the connection string for sql server is usually like this
"DefaultConnection": "Data Source=xxxx;Initial catalog=xxx;Integrated Security=False;User ID=xxxx;Password=xxxx;"
and my OnConfiguring code looks like this
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
...your code
}
}
Thanks To #Neil I figured it out. The configurations were not loaded into app. But, since I'm in dotnet 6's top level statements, adding configurations seems a little bit different from #Neil's advice. So here is the working solution:
builder.Services.AddControllers();
var currentDirectory = AppDomain.CurrentDomain.BaseDirectory;
var environmentName = builder.Environment.EnvironmentName;
builder.Configuration
.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{environmentName}.json", true, true)
.AddEnvironmentVariables();
// blah blah
builder.Services.AddDbContext<FileManagerContext>(
opt => opt.UseSqlServer("name=DefaultConnection"));
I was trying to scaffold the DB changes by this command :
Scaffold-DbContext -Connection name=YourDBContextObjectName -Provider
Microsoft.EntityFrameworkCore.SqlServer -project Destination_NameSpace
-context YourDBContextObjectName -OutputDir Entities -force
But the issue is the same in the code-first approach as well, In my scenario, I had different appsettings for different ENVs.
The appSettings.json was like so (as you can see, there is connectionStrings information in this file:
Also, my appsettings.dev.json was like so :
The problem is being occurred because when I tried to scaffold the DB changes by command, Firstly it tries to build your project, and because the Redis and RabbitMQ connection information is missed in the appsettings.json file, It cannot connect to the dataSources. so It returns the error.
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: Object reference not set to an instance of an object.
System.InvalidOperationException: A named connection string was used, but the name 'DBContextName' was not found in the application's configuration. Note that named connection strings are only supported when using 'IConfiguration' and a service provider...
Solution :
Set The correct Project as the Startup project
Add missing information about other connections to the appsettings.json
As you see, regardless of the error, It's not always related to the Database connection string.
I had the same problem but the error occurs when the application was published on the server. My string connection is in the user's secret, what I did was the following:
In program.cs
// Add services to the container.
builder.Services.AddDbContext<DataContext>(options =>
{
options.UseSqlServer("Name=dbconnection");
});
builder.Services.AddDbContext<DataContext>();
And before publish, select the string connection in data base options and make sure it is the correct:
Publish Options
It works for me in dot 6 API
Hello, the highlighted code worked for

Entity Framework Core Using multiple DbContexts

I'm having a problem that when I try to access a field in my PartsDbContext I get the following error:
System.Data.SqlClient.SqlException: 'Invalid object name 'fieldName''
It seems that this is because I'm trying to make my PartsDbContext use the same database as my ApplicationDbContext which is used with Identity. I need to know how to setup a 2nd dbcontext to work with EF core that uses/creates a different database.
I've tried creating a 2nd connection string but that gets me this error:
System.Data.SqlClient.SqlException: 'Cannot open database "PartsDb" requested by the login. The login failed.
Login failed for user 'DESKTOP-4VPU567\higle'.'
Here's my code:
appsettings.json
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-PrecisionCustomPC-b14db89e-86ad-4855-a17f-ac64a04339aa;Trusted_Connection=True;MultipleActiveResultSets=true",
"PartsConnection": "Server=(localdb)\\mssqllocaldb;Database=PartsDb"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
PartsDbContext.cs
public class PartsDbContext : DbContext
{
public DbSet<PartsViewModels.Tower> Towers { get; set; }
public DbSet<PartsViewModels.Motherboard> Motherboards { get; set; }
public PartsDbContext(DbContextOptions<PartsDbContext> options)
: base(options)
{
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddEntityFramework()
.AddDbContext<PartsDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("PartsConnection")));
services.AddMvc();
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdminRole", policy => policy.RequireRole("Admin"));
});
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
AdminController.cs
[Authorize(Policy = "RequireAdminRole")]
public class AdminController : Controller
{
private readonly PartsDbContext _context;
public AdminController(PartsDbContext context)
{
_context = context;
}
public IActionResult Index()
{
return View();
}
public IActionResult Towers()
{
var model = _context.Towers.ToList();
return View(model);
}
}
The line var model = _context.Towers.ToList(); is where the error is showing up.
Once again. I want to setup my PartsDbContext to work with Entity Framework Core in a way that EF-Core will automatically create the database.
I figured it out. This mostly came about because I accidentally deleted the database that Identity was using and I needed to figure out how to get it back.
Apparently there's nothing wrong with my connection string the way it is. I just needed to go into the package manager and type these commands in this order:
Add-Migration init -Context PartsDbContext
Update-Database -Context PartsDbContext
I found this out because that is what I had to do to get my ApplicationDbContext working again and it turns out that this step is done for you when you create a new MVC Core Web Application in Visual Studio using Individual User Authentication.
So basically the steps for adding more DbContexts is to:
Create a DbContext Class
Create a Connection string for that DbContext in appsettings.json
Add the DbContext to your configured services in Startup.cs
Setup the DbContext in the controllers that will use it.
Open the package manager and run the 2 lines above. (if "-Context" doesn't work try "--context"
Run your program and let EntityFrameworkCore take care of the rest.
First of all, thanks #Joe Higley answer this questions,I want to add more situation to help more people.
My situation is i'm trying to use EF-Identity and Area to create a admin panel, in my admin area own their own controller/models/views..., also contain a new DBcontext.
There is problems, if you try to context.Database.EnsureCreated(); to initialize DB there will show
System.Data.SqlClient.SqlException: 'Invalid object name 'fieldName''
Ref this link Migrations with Multiple Providers
We can use migrations and use --context to set which DbContext you want to run
In VScode you can run
dotnet ef migrations add InitialCreate --context BlogContext
dotnet ef database update
In Package Management Console you can run
Add-Migration InitialCreate -Context BlogContext
Update-Database
In addition to the prior comments, you can use this way:
dotnet ef migrations add InitialCreate --context MyContext
dotnet ef database update --context MyContext
or
Add-Migration InitialCreate -Context MyContext
Update-Database -Context MyContext
I cannot comment yet, but I would like to add to the answer.
Currently I am working my way through this tutorial: https://learn.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/model?view=aspnetcore-5.0&tabs=visual-studio
But I too started out with ApplicationDbContext for Identity. So, I ran into a similar problem. Your answer helped me out, thanks!
The tutorial however suggests a cleaner way to do this.
Add a data model
Scaffold the data model!
This step is huge. It creates the Context class, a connection string in appsettings.json, adds the Context in Startup.cs and more.
For usage of scaffolding take a look at linked tutorial.
Run given commands in the PMC and you are set.
Add-Migration init -Context ModelContext
Update-Database -Context ModelContext
So, I would suggest to use scaffolding as it does the most work for you.

How to make Entity Framework create database before using it outside DbContext?

In certain test environments, I have configured my application to create a new instance of it's database using Entity Framework migrations when the database does not already exist.
I am also using Hangfire, and have configured it use SQL Server in my OWIN startup class. At present I am instantiating a new instance of my DbContext to force database creation prior to configuring Hangfire to use the database:
public class Startup
{
public void Configuration(IAppBuilder app)
{
using (var dbContext = new MyDbContext())
{
// Force database creation before configuring Hangfire to use it.
}
GlobalConfiguration.Configuration.UseSqlServerStorage("myConnection");
// ...
}
}
This feels a bit hacky. What is the recommended way to ensure Entity Framework has created the database before it is used outside of the DbContext?
Turns out that the snippet in the question isn't necessarily enough by itself (depending upon platform and other EF configuration settings), so we actually make it explicit in the code now (as per Diana's comment), which also makes it feel less hacky:
// Trigger database creation before Hangfire tries to use it.
var initializer = new MigrateDatabaseToLatestVersion<MyDbContext, MyConfiguration>(true);
Database.SetInitializer(initializer);
var connectionString = this.Configuration.GetConnectionString("MyConnectionString");
using (var dbContext = new MyDbContext(connectionString))
{
dbContext.Database.Initialize(false);
}

Categories