Multiple DbConfigurations with different execution strategies in EF6 - c#

We've been using EF6 for a while now to connect with an Azure database. For this database we use the ExecutionStrategy specific for Azure connection to have a more resilient connection:
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
}
}
See the microsoft article about connection resilience for more info
Recently however we've added a connection to a different database which resides on a MSSQL database server where we want to use the default execution strategy. Since this runs in the same app domain, we run into a problem:
The default DbConfiguration instance was used by the Entity Framework before the 'MyDbConfiguration' type was discovered. An instance of 'MyDbConfiguration' must be set at application start before using any Entity Framework features or must be registered in the application's config file. See http://go.microsoft.com/fwlink/?LinkId=260883 for more information."
Reviewing the article linked in the error I see the following statement:
Create only one DbConfiguration class for your application. This class specifies app-domain wide settings.
I've tried solutions from several related questions to this, but I keep running in the same problem. The things I've tried basically come down to setting the custom DbConfiguration in different ways, through code, attribute or config file.
I think the solution is to set the execution strategy without a custom DbConfiguration, but I'm not really sure it is, and how I should do this.

Related

How do handle EF database connections in Azure Functions V2?

I am converting a WebJob to Azure Functions. I am using Azure Functions version 2. My issue is connecting to my database. The classes inside the Azure Function project do not talk to the database but they do reference classes that do talk to the database. When I run this as a webjob I simply call the configuationmanager and get the setting for the connectionstring. I made adjustments to get the string from a different location for Azure Functions but I still get the following error:
Azure Function The ADO.NET provider with invariant name 'System.Data.SqlClient'
I put the connection string into the Application Settings:
local.settings.json: Values:{"myconnectionstring"..... )
I then access this value using this:
Environment.GetEnvironmentVariable("myconnectionstring");
I can step through the code and see it as expected in the watch window. That part is figured out. But what should the connection string actually look like to work.
The one that looks like the following:
"metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string=\"Server=tcp ……………………
Or
connection string="Server=tcp:somthing.windows.net,1433;Database=mydb;User ID=myid;
Or is there another style to the connection string I need to use for this to work.
Entity Framework 6.2 in the Core classes that do the work (.net 4.6.3)
The Azure Function is .netStandardv2

Have migrations and a service that needs the database?

How do I build a service that needs access to my database while also using migrations, given that the migrations tool "dotnet ef database update" runs my application BuildWebHost before creating the database? The attempt to configure the service needing the database throws an exception because the database doesn't exist and causes the migration command to fail. The database therefore never gets created.
I'm using asp.net core 2 and EF Core 2.
More specifically, running "dotnet ef database update" with a blank database fails with the following error:
An error occurred while calling method 'BuildWebHost' on class
'Program'.Continuing without the application service provider. Error:
Cannot open database "MyDb" requested by the login. The login failed.
Login failed for user 'MYCOMPUTER\MYNAME'.
This happens because I have built a custom configuration provider backed by my "MyDb" database (with the end goal of binding to it with a custom options class) as per this Microsoft Configuration Tutorial My program.cs looks like:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((builderContext, config) =>
{
var tmpconfig = config.Build();
config.AddMyOptionsConfig(options => options.UseSqlServer(tmpconfig.GetConnectionString("My_Database")));
})
.UseStartup<Startup>()
.Build();
and the AddMyOptionsConfig eventually runs:
public class EFConfigProvider : ConfigurationProvider
{
[...]
// Load config data from EF DB.
public override void Load()
{
var builder = new DbContextOptionsBuilder<MyDbContext>();
OptionsAction(builder);
using (var dbContext = new MyDbContext(builder.Options))
{
// dbContext.Database.EnsureCreated(); // will cause first migration to fail
Data = !dbContext.ConfigurationValue.Any() // throws exception
? CreateAndSaveDefaultValues(dbContext)
: dbContext.ConfigurationValue.ToDictionary(c => c.Id, c => c.Value);
which throws an SQLException when it attempts to access the database because the database hasn't been created yet.
However, if I try to use dbContext.Database.EnsureCreated() then the initial migration fails because the tables already exist. I thought of trying dbContext.Database.Migrate() instead but as a beginner I'm concerned there might be unintended consequences for a production environment. As such I'd prefer to have control over migrations via the command line tools.
Fundamentally, the problem seems to be that "dotnet ef database update" runs the application startup - BuildWebHost - before it creates the database but the custom configuration provider added in BuildWebHost needs the database to already exist.
How do I solve this dilemma?
In a sense, you've created a circular reference. When running migrations on a ASP.NET Core project, the application is initialized to instantiate the DbContext needed to run migrations against. This is due to the fact that DbContext in EF Core now requires a DbContextOptions instance to be injected, as opposed to the old way of doing things in EF, where the connection string name (or the actual full connection string) would be defined directly on the constructor.
Normally, this would work just fine, but, as you've noticed, because the application initialization, itself, requires an already existing database, there's no way to run this before the migrations. As a result, you have two options:
You can attempt to abstract the piece that requires an existing database. That could be as simple as wrapping it in a try-catch and swallowing the exception, or more complex. Since the migration piece doesn't need this particular functionality, it could safely be excluded from the application initialization in that scenario.
You can move your context and entities into a class library and implement an IDesignTimeDbContextFactory to satisfy the ability to construct a DbContext to migrate against. You would then run the migrations against this class library instead of your ASP.NET Core project. That then sidesteps the issue of having to initialize the application in order to do a migration.
BuildWebHost (and Startup.Configure) shouldn't be used for application startup logic. The guidance from the ASP.NET team is to use Program.Main instead.
My solution so far has been to move the entity backing the custom configuration provider into a separate dbContext.
Now the table can be created when needed via MyDbOptionsContext.Database.EnsureCreated() without affecting the migrations, as they are based on the main context (MyDbContext).
The options table no longer participates in migrations but as it's unlikely to change there shouldn't be a problem. The other downside is the need (due to having 2 contexts) to explicitly specify the context for the dotnet ef command line tool.

Using Azure Mobile Services Code First Generated Context class in another project

I have a Windows Azure Mobile Services app that has a Code First generated database. The connection string (for when run locally) looks like this:
<add name="MS_TableConnectionString" connectionString="Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\<database_name.mdf;Initial Catalog=<database_name>;Integrated Security=True;MultipleActiveResultSets=True"
providerName="System.Data.SqlClient" />
I created a new Console App project, referencing the Mobile Services project, and copied this connection string to the App.config file
In Program.Main() I created a new instance of the Context class from the Designer in the Mobile Services project. But when I run the Console App, and try to access one of the DbSets made public by the Context, I get the following exception:
"An exception occurred while initializing the database. See the InnerException for details."
With an inner exception of:
"The underlying provider failed on Open."
Which in turn has an inner exception of:
"Cannot attach the file 'C:\\...\\<database_name>.mdf' as database '<database_name>'."
If I remove the AttachDbFilename part of the connection string in the Console App, I get the following exception at the same point in the code:
"Cannot create more than one clustered index on table 'dbo.<Table_Name>'. Drop the existing clustered index 'PK_dbo.<Table_Name>' before creating another"
Does anyone have any idea why it would be trying to create this new clustered index when there already appears to be one?
Or any ideas what connection string I should use just to get a normal read/write connection to the database without it doing anything weird? Is this related to database initialization?
Edit: I've had a bit more of a play around with this, this morning. I can get it working without exceptions if I remove the inheritance of "Microsoft.WindowsAzure.Mobile.Service.EntityData" from my model classes, so this appears to be pretty significant.
Ok I've just battled through this and got it working. Gonna type this up in case it helps anyone one day.
So the problem is something to do with the fact that my Code First model classes were inheriting from EntityData. As I said in my edit above, removing this inheritance does appear to fix the problem. I think this is because the EntityData class has both a property with a [Key] attribute and a separate property with a [Index(IsClustered = true)] attribute. Because you can't have more than one Clustered Index in a table, the database initialization fails. In the default Azure Mobile Services project, there must be some magic that means this doesn't happen somewhere - but from a separate project you get the "Cannot create more than one clustered index on table" exception.
So what I did was disable Database Initialization in the separate Console App by adding the line:
Database.SetInitializer<MobileServiceContext>(null);
...before the DbContext is instantiated.
This allows me to use the database initialized by the Mobile Services App as an existing database, without attempting to make any changes to it.
I also needed the following AppSetting in the config file of the Console App, in order for it to use the correct Schema Name:
<add key="MS_MobileServiceName" value="<CorrectSchemaName>" />

How do I modify Entity Framework 6 using Npgsql 3 to use an SSL connected string?

So far what I've tried is to verify that my certificates for SSL are correct and through command line (Using psql) and through a console application (Npgsql) I'm able to reach the database with SSL enabled (No password needed). This is working.. My problem starts with the SSL.
The application I'm working on has working Data Access to Postgre using Entity Framework 6 on non-SSL. However when I try to update the string to use SSL, I need to find a place to pass in the client-certificates. I've looked at different places. Right now, I have changed the DbContext() constructor to use base(DbConnection,bool) so that I can pass in a connection that has been provided the callback method. However, I get this error:
The context cannot be used while the model is being created. This
exception may be thrown if the context is used inside the
OnModelCreating method or if the same context instance is accessed by
multiple threads concurrently. Note that instance members of DbContext
and related classes are not guaranteed to be thread safe.
I've tried to apply different fixes from SO to solve this error, but to no avail.
Anything helps!

Item added to DbContext twice

I am very new to SP.NET MVC, so excuse me for missing basic stuff.
I have created a website working with a database (Microsoft SQL Server) and I'm using Entity Framework package to manage it.
I have a DatabaseManager class as following:
Public class DatabaseManager : DbContext
{
Public DbSet<Connection> Connections { get; set; }
.
.
.
}
My application listens for syslog connections and then updates the Connections member accordingly:
Db.connections.Add(new Connection(SourceIp, DestinationIp))
db.SaveChanges();
Anyway, however weird it may seem, when I add a connection to the DbContext two connection are added (both identical) instead of one.
What might be my problem?
If you have registered event listener twice or included the registering file (may be JS file) twice, this issue would come. You can any time check at Firebug with list of events attached..

Categories