CommandTimeout for DbContext - c#

I register DbContext using Dependency Injection, where I use connection string and CommandTimeout(600) option.
But when I use the sqlServerOption having command timeout set, it is not working. In resolved context I always receive timeout used in connection string (or default if not specified in connection string).
I don't want to set timeout via connection string for some reasons. What's wrong there please? I found question How to set CommandTimeout for DbContext?, but solved differently.
services.AddDbContext<MyDbContext>(
options => options.UseSqlServer(
"someConnectionString",
sqlServerOptions => sqlServerOptions.CommandTimeout(600))); //600s

did you pass options parameters to DbContext base?
public partial class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDBContext> options) : base(options)
{
}
....
}

Related

C# EF Core 6 How do I pass connection string into Context constructor as a string and not as an option

I have a strange requirement here. We have a ton of databases that are basically idential but with different clients. So each db has the same table structures. With that being said, I can't use the standard way of setting up the connectionstring in the startup. I would like to see if there is a way I can pass the connectionstring in the contructor without it being the DbContextOptions<> object. We use to be able to do that in earlier versions of EF but now it's expecting that option in the base().
The other option is just doing raw ADO with a command object, which I REALLY don't want to do, I would prefer if I could use EF.
So this is what it looks like, but would prefer to have a second constructor that allows the connectionString to be passed in. NO Start Up or NO appsettings.json, just want to pass in a string with the connectionString.
`
public class Context : DbContext
{
public Context(DbContextOptions<Context> options) : base(options) // <== base expects only options
{
Database.SetCommandTimeout(9000);
}
public Context(string connectionString) : base()
{
this.Database.Connection ????? What to do here
}
public DbSet<Transcription> Transcriptions { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.HasDefaultSchema("dbo");
builder.Entity<Transcription>().ToTable("Transcriptions", "dbo");
}
}`
Any idea why this keeps throwing errors or why it won't allow me to set the connectionString instead of using options?
I have looked an keep getting old solutions from 12+ years ago, nothing for EF 6
Possibly more convenient would be to use multitenancy approaches described in the docs but if you still want to use just the ctor you can override the OnConfiguring method and use connection string from the ctor there:
public class Context : DbContext
{
private readonly string _connectionString;
public Context(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
// setup options - provider, connection string, etc.
optionsBuilder.UseNpgsql(_connectionString);
}
}
}
Or manually building options from the connection string:
public class Context : DbContext
{
public Context(string connectionString):base(BuildOpts(connectionString))
{
}
private static DbContextOptions<Context> BuildOpts(string connectionString) =>
new DbContextOptionsBuilder<Context>()
.UseNpgsql(connectionString) // setup options - provider, connection string, etc.
.Options;
}

How do I set the OnConfiguring method in my DbContext without any hardcoded connection string?

I'm using multiple examples on how to set up my MySql service but in almost every one (including the Microsoft), they are using a hard coded connection string.
This is my startup:
services.AddDbContext<DbContext>(options =>
{
options.UseMySql(configuration.GetConnectionString("DefaultConnection"),
mysqlOptions =>
{
mysqlOptions.ServerVersion(new ServerVersion(new Version(8, 0, 18)));
});
});
And whenever I try to Update-Database I see an error telling me that there's no password setted
MySql.Data.MySqlClient.MySqlException (0x80004005): Access denied for user 'root'#'localhost' (using password: YES).
After looking around a bit I found an overrideable method called OnConfiguring, but I want to set the connection string in a dynamic way, cause If I change my environment, I want that string to change too.
This is where I found almost every example with hardcoded strings (ex. https://dev.mysql.com/doc/connector-net/en/connector-net-entityframework-core.html)
I am addressing this portion of your question:
"After looking around a bit I found an overrideable method called OnConfiguring, but I want to set the connection string in a dynamic way, cause If I change my environment, I want that string to change too."
In order to solve getting the connection string into the OnConfiguring method inside your DbContext class I simply passed IConfiguration into the DbContext constructor like this.
private readonly IConfiguration _config;
public CoreDbContext(IConfiguration config)
{
_config = config;
}
public CoreDbContext(DbContextOptions<CoreDbContext> options,
IConfiguration config) : base(options)
{
_config = config;
}
Then in the OnConfiguring method I pulled the connection string from that config object injected at construction. Like this:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(_config.GetConnectionString("YourKeyValueForConnStringInAppSettings.json"));
}
}
Finally here are details of my environment / Project:
Windows 10 machine
Visual Studio 2022 (Version 17.0.4)
ASP.NET Core Web API
.NET 6.0
Configure for HTTPS
Use controllers checked
Enable OpenApi support
Authentication type at time of writing is None
You need to init configuration if not configured.
This can be done by overriding the OnConfiguring method in DbContext.
This way you can have global application configuration that will be pulled when application is ran. And use local configuration when doing Db migration.
Try this
public class YourContext: DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
if (options.IsConfigured == false)
options.UseMysql({YOUR_CONNECTION_STRING});
}
}
Based on the comment, I think you want to separate the EF Core related classes to a different assembly.
In the UseMySql there is an option to configure MigrationsAssembly. You can get more details here
Here is pseudo-code for SQL Server.
services.AddDbContext<EFCoreDemoContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
assembly => assembly.MigrationsAssembly(typeof(EFCoreDemoContext).Assembly.FullName));
});
in configuration file, you should have a ConnectionStrings that points to your database with other options.
"ConnectionStrings": {
"DefaultConnection": "server=127.0.0.1;uid=root;pwd=12345;database=test"
},
When you use configuration.GetConnectionString, you are looking up a value from the configuration section.
Try this at startup.cs
public class Startup
{
private readonly IConfiguration _config;
public Startup(IConfiguration config)
{
_config = config;
}
// This method gets called by the runtime. Use this method to add services to the
container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MainContext>(options =>
options.UseOracle(_config["CONNECTION_STRING"]));
}
}
And do that at your Context ctor
public MainContext(DbContextOptions<MainContext> options) : base(options) { }

Getting DbContext via Configuration with DbContextOptions

I am trying to get the DbContext I registered with options via services.AddDbContext(...) on the service provider of the project, but when calling configuration.Get<ModelContext> it can not be constructed as the options apparently weren't provided and therefore also no database provider is given.
I am using ASP.NET Core 2.2 with Entity Framework Core 2.2.3 and my DbContext is defined in a separate project.
My DbContext:
public class ModelContext : DbContext
{
public ModelContext(DbContextOptions<ModelContext> options) : base(options) { }
public ModelContext() { }
}
I did not override OnConfiguring(DbContextOptionsBuilder) in ModelContext.
public class StartUp
{
public void ConfigureServices(IServiceCollection services)
{
public services.AddEntityFrameworkSqlServer();
services.AddDbContext<ModelContext>(options => options.UseSqlServer(modelConnectionString));
}
}
In the controller (or anywhere really) I call public HomeController(IConfiguration configuration) => _modelContext = configuration.Get<ModelContext>(); which throws the unexpected exception.
What I specifically get is an InvalidOperationException with the message:
No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
According to the documentation I read and examples I looked at, the ModelContext should be created with the options I defined when calling AddDbContext<ModelContext>. Is the Get method the wrong one to use?
After configuring the db context service in "ConfigureServices" method of the Startup.cs file with something like this :
var connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<BottinContext>(options => options.UseSqlServer(connectionString)) ;
Simply add a :
ModelContext db
parameter to the constructor of your controller and let DI magic happen.
If you've got many controllers and wish to simplify things, you can use a base contructor that holds the db context
public BaseController(ModelContext context /* as well as other injections */)
{
_db = context;
}
internal ModelContext _db;
you are trying to get dbContxt instance in a wrong way. Get method is not used to get instance of dbContext object that you registered with dependency injection container.
if you want to get instance of your dbContext class that you registered you can inject it through construction injection for example
public class RepositoryWrapper : IRepositoryWrapper
{
private readonly ModelContext _modelContext;
public RepositoryWrapper(ModelContext modelContext)
{
_modelContext= modelContext;
}
}
is something i am doing in my project.

Pass parameter to class which inherits from DbContext

My application is an ASP.NET Core 1.0 Web API. I am using Entity framework.
I wan't to change the CommandTimeout like this:
public class MyDbContext: DbContext
{
private const int Timeout = 180;
public MyDbContext(DbContextOptions options): base(options)
{
this.Database.SetCommandTimeout(Timeout);
}
}
this works if Timeout is defined in my class.
However, I would like to have the value of my Timeout inside my appsettings.json file like this:
"DbContextSettings": {
"Timeout": "180"
}
so there are two ways to set the CommandTimeout now:
Pass the CommandTimeOutInSeconds as a parameter to the constructor.
Get the value out of appsettings.json inside the constructor of my class
I don't know how to achieve one of the ways.
Is this possible? or is there any known workaround?
Edit
Iam never really Initailizing MyDbContext. I do this in ConfigureServices in the Startup class like this:
services.AddDbContext<MyDbContext>(db => db.UseSqlServer("secret"));
Edit 2
#dennisanberlin so let's say I have a controller calling the following method:
public class SomeRepository
{
private MyDbContext context;
public SomeRepository(MyDbContext myContext)
{
this.context = myContext;
}
public Person SomeMethodCalledByController(){
return myContext.SomeTableWhichContainsPersons.FirstOrDefault(person => person.name == "Bob");
}
}
The class SomeRepository is never getting initialized by me. Iam doing this via dependency injection in the startup like this:
services.AddTransient<SomeRepository>();
The class knows about MyDbContext from the above shown line:
services.AddDbContext<MyDbContext>(db => db.UseSqlServer("secret"));
Therefore I never pass an object of MyDbContext directly to any of my classes working with the database.
You can pass the timeout to your constructor like this:
public MyDbContext(DbContextOptions options, int timeout): base(options)
{
Timeout = timeout;
this.Database.SetCommandTimeout(Timeout);
}
And here is an example on how to read settings from your App settings.json csharpcorner
EDIT
public MyDbContext(DbContextOptions options): base(options)
{
int timeout = //return timeout setting from appsettings.json
this.Database.SetCommandTimeout(Timeout);
}
You can try to do it that way, that you read your appsettings.json inside your constructor.

Register generic EntityFrameworkCore DbContext in ASP.Net Core 1.0

I am trying to use generic DbContext in ASP.Net Core 1.0 however I am getting following error:
ArgumentException: Type 'idmin.data.IdminDataContext`1[System.Int32]' does not have a default constructor Parameter name: type
Here is how IdminDataContext is defined:
public class IdminDataContext<TKey> : DbContext where TKey : IEquatable<TKey>
{
public IdminDataContext(DbContextOptions<IdminDataContext<TKey>> options) : base(options)
{
}
public DbSet<Idmin.Models.Client<TKey>> Clients { get; set; }
}
and here is how I am registering it in startup class.
public void ConfigureServices(IServiceCollection services)
{
var connection = "my connection string";
services.AddDbContext<IdminDataContext<int>>(options => options.UseSqlServer(connection));
services.AddMvc();
}
I tried adding default constructor in IdminDataContext class but it didn't help.
Any idea what am I missing here?
I found issue. It was silly mistake on my part. Instead of using constructor injection in Controller I was using action method injection which was causing exception in Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder's CreateModel method. Fixed this issue by moving dependency injection to constructor instead of action method injection.

Categories

Resources