I am new on EF Core.
I have created my context which refers from appsettings.json
public APITestDBContext(DbContextOptions<APITestDBContext> options):base(options)
{
}
Working well. I able to update DB in Code First approach. But when I tried to make an instance from dbcontext, it's expecting option. I don't know what options that expecting.
private APITestDBContext db = new APITestDBContext();
There is no argument given that corresponds to the required formal
parameter 'options' of
'APITestDBContext.APITestDBContext(DbContextOptions)'
What I need to write there?
What you did is fine that you supported dependency injection for your class, and In the ConfigureService Method in Startup.cs you mentioned how the injection to be resolved.
So now when you need to create new like that. Your class requires a constructor parameter which you need to provide which will be the same parameter value in your startup class.
or create another overload for the constructor as below which accepts no parameters :
public APITestDBContext():base()
{
}
But to do that it will call DBContext.OnConfiguring() method to setup your db which you need to implement.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Your Connection String");
}
Related
I have an ASP.NET Core application, which is structured in three layers i.e.:
Data access layer (Entity Framework)
Business logic layer
ASP.NET MVC web application
As it is right now, the configuration works, and I can access my database in my web application library. However, when I constructed my data access layer with EF Db first, I got a generic class, which looks something like this:
public partial class ClassContext: DbContext
{
public ClassContext(DbContextOptions<ClassContext> options)
: base(options)
{
}
public virtual DbSet<Entity> Entity{ get; set; }
....
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("ConnString");
}
}
}
As you can see, the connection string is hardcoded into my OnConfiguring method, which is not recommended.
Therefore, I followed the following "guide" here, which suggest me to use the built in DI, to pass the connection string from my Web library.
This I did:
public void ConfigureServices(IServiceCollection services)
{
//Add connectionstring to EF
services.AddDbContext<ClassContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ConnectionStringFromAppSettings")));
}
According to the "guide" the connectionstring should be provided to the constructor of my ClassContext class - and it sort of does, however not with any connection string..
This means, that optionsBuilder.IsConfigured evaluates false, and wants to use the hard coded connectionstring.
Therefore, I would very much like to know, if I use the DI incorrect, since i cannot access the connection string in my ClassContext class
Update
I removed the OnConfiguring() method, and do now inject the context into my service class constructor in the business logic layer the following way:
public MasterService(ClassContext context)
{
MinorService = new MinorService(context);
}
public Stuff AddStuffIntoDatabase(Stuff test)
{
//business logic going before here
MinorService.addstuffMethod(test)
}
However, now I get the following error, when I want to do an operation in my database:
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.
This did not happen before, when I configured the connectionstring wrongly in my OnConfiguring() method.
What about the appsettings.json? Is it like this?
"ConnectionStrings": {
"ConnectionStringFromAppSettings": "your_connection_string"
}
Configuration.GetConnectionString always search for the section "ConnectionStrings"
https://learn.microsoft.com/it-it/dotnet/api/microsoft.extensions.configuration.configurationextensions.getconnectionstring?view=dotnet-plat-ext-3.1
I am using .Net Core, using the built-in dependency injection. In my login screen, I need the user to also choose a departmental database - we have different databases with the same structure to use the same application with different data. However, I can't figure out how to add/modify the dbContext that late. Startup.cs has the DI, but I don't know which connection string to read from the config until the user has chosen the department. It is a small database, and the company is not concerned about the management of the duplicate databases.
How can I add the service late
services.AddDbContext<my_accountingContext>(options =>
options.UseMySQL(Configuration.GetConnectionString("CorrectDepartmentConfig")));
when I actually know what CorrectDepartmentConfig is?
Or, if that can't be done, how can I do a smelly change of the my_accountingContext after Startup.cs?
You can use an implementation factory overload of IServiceCollection in ConfigureServices method form Startup class:
//First register a custom made db context provider
services.AddTransient<ApplicationDbContextFactory>();
//Then use implementation factory to get the one you need
services.AddTransient(provider => provider.GetService<ApplicationDbContextFactory>().CreateApplicationDbContext());
The implementation of CreateApplicationDbContext depends on your specific needs, but a base implementation should look like the following:
public ApplicationDbContext CreateApplicationDbContext(){
//TODO Something clever to create correct ApplicationDbContext with ConnectionString you need.
}
After this implementation, you can inject the correct ApplicationDbContext in your controller, action...
public MyController(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public IActionResult([FromServices] ApplicationDbContext dbContext){
}
You can always set the connection string from inside the protected OnConfiguring method. You can get access to the IConfiguration instance from there (the DbContext class has a service locator, Instance property), retrieve the connection string, and then call UseMySql extension method with the appropriate connection.
Something like this:
protected virtual void OnConfiguring(DbContextOptionsBuilder builder)
{
var configuration = (this as IInfrastructure<IServiceProvider>).GetService<IConfiguration>();
var connectionString = configuration.GetConnectionString("<name>");
builder.UseMySql(connectionString);
base.OnConfiguring(builder);
}
For the strongly-typed version of GetService do not forget to reference namespace Microsoft.Extensions.DependencyInjection.
NOTE: This example has been simplified
I have got a Client's Contact table and wanted to retrieve specific client contact information from DB. The code I typed belove brings me all contact details. I wanted to use a parameter to only bring me specific client contacts.
I used IClientContactRepository interface like this
public interface IClientContactRepository
{
IQueryable<ClientContactModel> ClientContacts { get; }
}
And i used this class to retrive data from database with dapper
public class ClientContactRepository : IClientContactRepository
{
private readonly IConfiguration configuration;
private List<ClientContactModel> ClientContactList {get;set;}
public ClientContactRepository(IConfiguration config)
{
configuration = config;
SqlConnection conn = new SqlConnection(configuration["ConnectionString"]);
using (var connection = conn)
{
ClientContactList = connection.Query<ClientContactModel>("Select * FROM ContactTable ").ToList();
}
}
public IQueryable<ClientContactModel> ClientContacts => ClientContactList;
}
In my Startup class
services.AddTransient<IClientContactRepository, ClientContactRepository>();
My QUESTION is: can I pass the client's id parameter to the constructor.
I tried this: add a parameter to the constructor
public ClientContactRepository(IConfiguration config, int clientId)
and tried to start up class.
services.AddTransient<IClientContactRepository, ClientContactRepository(int,i)>()
Didn't work....
Can someone help me how to pass parameter please?
Yes, but where are you getting the client ID from - is it a configured value that will be static for the lifetime of the application? If so, you can use the AddTansient method overload that accepts a factory delegate to create the objects.
The better way (will cover all use cases) is registering the type that can provide that information (create one if no such type exists) with the DI container and use that as a parameter in the constructor of your repo.
As an example, let’s say you get your client ID from a claim, so the type you need to inject is IPrincipal:
services.AddScoped<IPrincipal>(
provider => provider.GetService<IHttpContextAccessor>()
.HttpContext
.User);
You would then inject the IPrincipal into your repo constructor and retrieve the client ID. An even better way would be to create your own type “ClientIdAccessor” which is responsible for providing the client ID. You would then not have a dependency on IPrincipal when testing your repo and the implementation of this new type would only depend on external libraries for your asp.net core implementation.
Side note: are you certain you want to use AddTransient for your repo? Usually you’d want to use the same repo object for the lifetime of the request (I.e. AddScoped).
I have a project that works for a single database. Now I need to get it to work with a second (within the same project) that has the same data structure. So I am using the same model and am trying to pass in the Data Connection name upon calling my Data Context Class. Unfortunately I am receiving the following error:
The type String cannot be constructed. You must configure the
container to supply this value.
Here is the code that I tried:
public UniversityContext(string context)
: base(context){
}
When I looked for answers I found this answer for that error and it recommends, having a parameter-less constructor as well. I tried that and still the same issue.
Here it is with the parameter-less constructor:
public UniversityContext()
: base("UniversityConnection")
{
}
public UniversityContext(string context)
: base(context)
{
}
In case it matters I am using Unity.
Unity automatically tries to use the most greedy constructor. In your case this is:
public UniversityContext(string context)
You can configure this with an injection constructor, without seeing your code something like this:
container.RegisterType<UniversityContext>(
new InjectionConstructor("UniversityContext"));
I hope somebody is able to help me, because it seems I'm totally stuck.
For upcoming projects in our company we'd like to use Entity Framework 5 with an code first approach. I played around a little while and everytime I try to use EF with our existing libraries, I fail because it seems EF heavily relies on an existing app.config.
In our company, we have an inhouse database library that allows us to connect to various data sources and database technologies taking the advantages of MEF (managed extensibility framework) for database providers. I just have to pass some database settings, such as host (or file), catalog, user credentials and a database provider name, the library looks for the appropriate plugin and returns me a custom connection string or IDbConnection.
We'd like to use this library together with EF because it allows us to be flexible about which database we use also change the database at runtime.
So. I saw that a typical DbContext object takes no parameters in the constructor. It automatically looks for the appropriate connection string in app.config. We don't like such things so I changed the default constructor to take a DbConnection object that get's passed to the DbContext base class. No deal.
Problems occur when the code first model changes. EF automatically notices this and looks for migration classes / configuration. But: A typical migration class requires a default parameterless constructor for the context! What a pity!
So we build our own migration class using the IDbContextFactory interface. But again, it seems that also this IDbContextFactory needs a parameterless constructor, otherwise I'm not able to add migrations or update the database.
Further, I made my own data migration configurator where I pass the context, also the target database. Problem is here: It doesn't find any migration classes, no matter what I try.
I'm completely stuck because it seems the only way to use EF is when connection strings are saved in app.config. And this is stupid because we need to change database connections at runtime, and app.config is read-only for default users!
How to solve this?
The answer is provided here
https://stackoverflow.com/a/15919627/941240
The trick is to slightly modify the default MigrateDatabaseToLatestVersion initializer so that:
the database is always initialized ...
... using the connection string from current context
The DbMigrator will still create a new data context but will copy the connection string from yours context according to the initializer. I was even able to shorten the code.
And here it goes:
public class MasterDetailContext : DbContext
{
public DbSet<Detail> Detail { get; set; }
public DbSet<Master> Master { get; set; }
// this one is used by DbMigrator - I am NOT going to use it in my code
public MasterDetailContext()
{
Database.Initialize( true );
}
// rather - I am going to use this, I want dynamic connection strings
public MasterDetailContext( string ConnectionString ) : base( ConnectionString )
{
Database.SetInitializer( new CustomInitializer() );
Database.Initialize( true );
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
public class CustomInitializer : IDatabaseInitializer<MasterDetailContext>
{
#region IDatabaseInitializer<MasterDetailContext> Members
// fix the problem with MigrateDatabaseToLatestVersion
// by copying the connection string FROM the context
public void InitializeDatabase( MasterDetailContext context )
{
Configuration cfg = new Configuration(); // migration configuration class
cfg.TargetDatabase = new DbConnectionInfo( context.Database.Connection.ConnectionString, "System.Data.SqlClient" );
DbMigrator dbMigrator = new DbMigrator( cfg );
// this will call the parameterless constructor of the datacontext
// but the connection string from above will be then set on in
dbMigrator.Update();
}
#endregion
}
Client code:
static void Main( string[] args )
{
using ( MasterDetailContext ctx = new MasterDetailContext( #"Database=ConsoleApplication801;Server=.\SQL2012;Integrated Security=true" ) )
{
}
using ( MasterDetailContext ctx = new MasterDetailContext( #"Database=ConsoleApplication802;Server=.\SQL2012;Integrated Security=true" ) )
{
}
}
Running this will cause the two databases to be created and migrated according to the migration configuration.
It needs a parameterless constructor in order to invoke it. What you could do is provide your default DbConntectionFactory in the empty constructor, something like:
public DbContext()
{
IDbContextFactory defaultFactory; //initialize your default here
DbContext(defaultFactory);
}
public DbContext(IDbContextFactory factory)
{
}