DbContext class - ASP.Net.Core - entity-framework

How can I apply the :base("name=connectionstring_name") in ASP.NET Core?
Because my Visual Studio shows cannot convert from 'string' to 'Microsoft.EntityFrameworkCore.DbContextOptions' .
namespace SchoolDataLayer
{
public class Context: DbContext
{
public SchoolDBContext() : base("name=SchoolDBConnectionString")
{
}
}
}
public SchoolDBContext() : base("name=SchoolDBConnectionString")

As error says you should pass DbContextOptions class instead of connection string.
The DbContextOptions instance carries configuration information such as:
The database provider to use, typically selected by invoking a method such as UseSqlServer or UseSqlite. These extension methods require the corresponding provider package, such as Microsoft.EntityFrameworkCore.SqlServer or Microsoft.EntityFrameworkCore.Sqlite. The methods are defined in the Microsoft.EntityFrameworkCore namespace.
Any necessary connection string or identifier of the database instance, typically passed as an argument to the provider selection method mentioned above
Any provider-level optional behavior selectors, typically also chained inside the call to the provider selection method
Any general EF Core behavior selectors, typically chained after or before the provider selector method
here is an example:
public class Context: SchoolDbContext
{
public SchoolDbContext(DbContextOptions<SchoolDbContext> options)
: base(options)
{
}
}
for more information read https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext

Related

ASP.NET Core reference connection string in class library project

I've created an ASP.NET core 5 Solution and it consists of different projects. The Startup class and DataContext class are not in the same project so when I'm going to add a new migration on the Data project where DataContext is located I get the following error.
Unable to create an object of type 'DataContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
It is the structure of the Solution:
I managed to solve the problem by adding the Startup project's name while adding a new migration, but this way seems annoying to add the project's name every time creating a new migration.
dotnet ef migrations add initialcreation -s ..\API\API.csproj
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.
I also add a new constructor with no parameter in the DataContext class and the result was the following error:
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.
Finally, I added the OnConfiguring function on DataContext and now it is like this:
public class DataContext : DbContext
{
public DataContext() {}
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
this.ChangeTracker.LazyLoadingEnabled = false;
}
public DbSet<AppUser> Users { get; set; }
public DbSet<UserPhoto> Photos { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlite("name=DefaultConnection");
}
}
}
And again I got a new error while creating a new migration:
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.
UPDATE:
Here is appsettings.json file:
"ConnectionStrings": {
"DefaultConnection": "Data Source=database1.db"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"

EF Core Migrations in Azure Function startup

According to https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection the service provider should not be used until AFTER the startup has completed running. Indeed, if I try to get a registered service it will fail.
Example:
[assembly: FunctionsStartup(typeof(Startup))]
namespace Fx {
public sealed class Startup : FunctionsStartup {
public override void Configure(IFunctionsHostBuilder builder) {
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddEnvironmentVariables();
var configuration = configurationBuilder.Build();
builder.Services.AddInfrastructure(configuration);
builder.Services.AddApplication();
var serviceProvider = builder.Services.BuildServiceProvider();
DependencyInjection.AddDatabase(serviceProvider).GetAwaiter().GetResult();
}
}
}
public static class DependencyInjection {
public static async Task AddDatabase(IServiceProvider services) {
using var scope = services.CreateScope();
var serviceProvider = scope.ServiceProvider;
var context = serviceProvider.GetRequiredService<ApplicationDbContext>();
//Error generated here
if (context.Database.IsSqlServer()) {
await context.Database.MigrateAsync();
}
await ApplicationDbContextSeed.SeedSamplePersonnelDataAsync(context);
}
public static IServiceCollection AddInfrastructure(
this IServiceCollection services,
IConfiguration configuration) {
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
return services;
}
}
This produces the following error
Microsoft.EntityFrameworkCore: 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<TContext> object in its constructor and passes it to the base constructor for DbContext.
Is there a good option for migrating and seeding during startup?
The easiest way I found to run code after startup was by registering a custom IWebJobsStartup by using the WebJobsStartupAttribute (the FunctionsStartupAttribute actually also inherits from this attribute). In the WebJobsStartup class you'll need to register your extension using the AddExtension where you are able to use dependency injection and seed your database. My code:
[assembly: WebJobsStartup(typeof(DbInitializationService), "DbSeeder")]
namespace Our.Database.Seeder
{
public class DbInitializationService : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.AddExtension<DbSeedConfigProvider>();
}
}
[Extension("DbSeed")]
internal class DbSeedConfigProvider : IExtensionConfigProvider
{
private readonly IServiceScopeFactory _scopeFactory;
public DbSeedConfigProvider(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public void Initialize(ExtensionConfigContext context)
{
using var scope = _scopeFactory.CreateScope();
var dbContext = scope.ServiceProvider.GetService<YourDbContext>();
dbContext.Database.EnsureCreated();
// Further DB seeding, etc.
}
}
}
According to your code, I assume that you're building something aligned to the CleanArchitecture Repository on Github. https://github.com/jasontaylordev/CleanArchitecture
The main difference between this repo and your apporach, is that you're obviously not using ASP.NET, which is not a problem at all, but requires a little bit more configuration work.
The article already mentioned (https://markheath.net/post/ef-core-di-azure-functions) refers another blogpost (https://dev.to/azure/using-entity-framework-with-azure-functions-50aa), which briefly explains that EntityFramework Migrations are not capable of auto-discovering your migrations in an Azure Function. Therefore, you need to implement an instance of IDesignTimeDbContextFactory. I also stumbled upon it in the microsoft docs:
https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli#from-a-design-time-factory
You could for example place it inside your Infrastructure\Persistence\Configurations folder. (Once again, I'm only assuming that you're following the CleanArchitecture repo structure)
DI in AZURE Functions
Caveats
A series of registration steps run before and after the runtime processes the startup class. Therefore, keep in mind the following items:
The startup class is meant for only setup and registration. Avoid using services registered at startup during the startup process. For instance, don't try to log a message in a logger that is being registered during startup. This point of the registration process is too early for your services to be available for use. After the Configure method is run, the Functions runtime continues to register additional dependencies, which can affect how your services operate.
The dependency injection container only holds explicitly registered types. The only services available as injectable types are what are setup in the Configure method. As a result, Functions-specific types like BindingContext and ExecutionContext aren't available during setup or as injectable types

Issue with Entity Framework 6 while connecting to SQL Server from Azure function V2

I am trying to use an existing library which is a .net library which uses EF 6.0 to connect to a database. Since Azure Functions does not have an app.config file, I am trying to set the connection string using C# code. But I am getting the following exception while connecting to the DB using my DB context:
System.ArgumentException: The ADO.NET provider with invariant name 'System.Data.SqlClient' is either not registered in the machine or application config file, or could not be loaded. See the inner exception for details.
System.ArgumentException: The specified invariant name 'System.Data.SqlClient' wasn't found in the list of registered .NET Data Providers
MyDBContext.partial.cs:
[DbConfigurationType(typeof(MyDbConfiguration))]
public partial class MyDBContext : DbContext
{
public MyDBContext (string ConnectionString)
: base(ConnectionString)
{
}
}
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
I have a method as following to get the DBContext. This method will be used by the library methods to get the DB context instance.
public MyDBContext GetDB( string metadata, string connectionString )
{
EntityConnectionStringBuilder b = new EntityConnectionStringBuilder();
b.Metadata = metadata;
b.ProviderConnectionString = connectionString;
b.Provider = "System.Data.SqlClient";
return new MyDBContext (b.ConnectionString);
}
When I execute a library method to load data from db from an Azure function v2, which internally calls the above method to get DB Context and then connects to actual DB. Here MyDBContext object is getting created, but when it connects to db the following exception occurs.
System.ArgumentException: The ADO.NET provider with invariant name 'System.Data.SqlClient' is either not registered in the machine or application config file, or could not be loaded. See the inner exception for details.
System.ArgumentException: The specified invariant name 'System.Data.SqlClient' wasn't found in the list of registered .NET Data Providers
I just worked on this issue but for Azure function V1.
When using EF with Azure function, you can specify connection string in 'local.settings.json' file like this:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"AzureWebJobsDashboard": ""
},
"ConnectionStrings": {
"YourEntities": {
"ConnectionString": "metadata=res://*/EF.yourModel.csdl|res://*/EF.yourModel.ssdl|res://*/EF.yourModel.msl;provider=System.Data.SqlClient;provider connection string='data source=yourServer;initial catalog=yourDB;persist security info=True;user id=yourUserID;password=yourPwd;MultipleActiveResultSets=True;App=EntityFramework'",
"ProviderName": "System.Data.EntityClient"
}
}
}
Please pay attention to 'ProviderName' attribute. Case should be exact as shown above and provider should be 'EntityClient'
Plus 'Provider Connection String' attribute of actual connection string should be in single quote (I am not sure why Microsoft did this but this is how it is supposed to be).
This will help you run your function app locally with EF without any more changes
Now for deployment in Azure.
local.settings.json does not get deployed to cloud. As its name suggests it acts as configuration file for local run.
So you need to set connection string in 'Configuration' of Azure function app on portal.
There you can specify following parameters:
Name - 'YourEntities'
value - Just Connection string part from above json file
Type - 'Custom'
Slot Settings - according to your requirement
Now if you notice there is no way to specify ProviderName here. If you try to run function now you will get error for 'missing provider name'
Here your extended DBConfiguration class comes in handy.
Create your DB configuration class as below and specify provider as EntityType
public class YourDBContextConfig : DbConfiguration
{
public YourDBContextConfig()
{
SetProviderServices("System.Data.EntityClient",
SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
You can create this class in same file where you have created partial class for your DBContext
Add following attribute to your Context class:
[DbConfigurationType(typeof(YourDBContextConfig))]
Also make sure your partial context class has constructor that takes connection string as parameter and supply it while initializing context:
string connString =
ConfigurationManager.ConnectionStrings["YourEntities"].ConnectionString;
using (YourEntities db = new YourEntities(connString))
{
}
This will work for deployment.

How to define a BaseConfigration for all other configuration in EF7 code first?

I want to define a base configuration (for some reason) for all model configuration(which using fluent api on them), so i create BaseConfigration class:
public class BaseConfigration<T> : EntityTypeBuilder<T> where T : class
{
public BaseConfigration(InternalEntityTypeBuilder builder) :base(builder)
{
}
}
and all of other configuration inherit from it:
public class LoyaltyActivityConfig : BaseConfigration<LoyaltyActivity>
{
public LoyaltyActivityConfig(InternalEntityTypeBuilder builder) : base(builder)
{
this.Property(x => x.Title).HasMaxLength(100);
}
}
in this point all thing goes right, but when i want to introduce this configuration in OnModelCreating method:
new LoyaltyActivityConfig(modelBuilder.Entity<NotificationPlatform>());
it gives me error:
cannot convert from 'Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder' to 'Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder'
How can i do it?
As the exception message says, you are using incorrect type. During OnModelCreating you have EntityTypeBuilder<T> object. InternalEntityTypeBuilder should not be used in user code as its internal to EF core as said in documentation.
To define a BaseConfiguration which will be applied to each entity type in the model and derived by EntityTypeConfiguration, code should be organized in following manner:
public class BaseConfiguration<T> where T : class
{
public BaseConfiguration(EntityTypeBuilder<T> entityTypeBuilder)
{
// Write fluent API code here which will be applied to all entityTypes in the model
entityTypeBuilder.HasKey("Id");
}
}
public class LoyaltyActivityConfig : BaseConfiguration<LoyaltyActivity>
{
public LoyaltyActivityConfig(EntityTypeBuilder<LoyaltyActivity> entityTypeBuilder)
: base(entityTypeBuilder)
{
// Write fluent API code here which will be applied to EntityType LoyaltyActivity only
entityTypeBuilder.Property(x => x.Title).HasMaxLength(100);
}
}
Then call above code from OnModelCreating like this
new LoyaltyActivityConfig(modelBuilder.Entity<LoyaltyActivity>());
Above method will call BaseConfiguration constructor followed by EntityTypeConfiguration constructor, applying all fluent API code.
Few things to remember here,
In BaseConfiguration even if you have generic EntityTypeBuilder you will not be able to use generic methods since T is unknown type. You can define an interface which will be implemented by every T (all entity types in the model) and use generic methods afterwards. Using non-generic methods will also get you same result though. Its just matter of readability.
You can also use method calls instead of constructor in Config classes to achieve the same.
To organize code like above, IEntityTypeConfiguration<TEntity> feature from EF6 has been implemented in EF core and will be available in 2.0 release. More info https://github.com/aspnet/EntityFramework/issues/2805

Entity Framework 3.5: change constructor of entities class

The default constructor in a generated Entity Framework Entities file is like this:
public ProjectEntities() : base("name=ProjectEntities", "ProjectEntities")
{
this.OnContextCreated();
}
I want to change it to:
public ProjectEntities() : base(UtilClass.GetEnvDependantConnectionStringName(), "ProjectEntities")
{
this.OnContextCreated();
}
This is because I want to have a different connection string for all the dev environments and the production environment, and have no chance they are mixed up (which is what my custom method checks).
How do I do that? This code is thrown away every time the designer file is regenerated.
You need to create another file alongside the auto-created ProjectEntities.Designer.cs, say ProjectEntities.cs. In that you use partial to extend the functionality of your entities class like this:
public partial class ProjectEntities : ObjectContext
{
partial void OnContextCreated()
{
this.Connection.ConnectionString = UtilClass.GetEnvDependantConnectionString();
}
}
The file won't then get changed when you regenerate the .Designer.cs file. You'll have to fetch the connection string yourself...
We fixed it by calling our entities ProjectEntitiesPrivate, and what was partial class ProjectEntities before, is now a non partial class ProjectEntities : ProjectEntitiesPrivate, with the constructor I need:
public class ProjectEntities: ProjectEntitiesPrivate
{
public ProjectEntities():base(UtilClass.GetEnvDependantConnectionStringName())
{
}
....