EF6 Queries Using Wrong Database - c#

I am using Devart dotConnect for MySQL with Entity Framework 6 in a Windows Forms application (.Net Framework 4.8). I'm using the DB first approach with a dynamic connection string. My application can connect to any number of databases all with the same schema, so I am building the connection string based on input from the user. I have used this approach on several other projects and have never encountered this issue before. I have spent the last day searching for someone reporting a similar issue, with no luck.
I've created my EDMX model using an empty database (meaning no data, but full schema) called entity_model. I can create instances of the DBContext-derived entity class, and I can even use the attached database to perform general queries on the attached server (e.g. get a list of installed database). I can even perform SQL queries into the database identified in the connection string (using DbContext.Database.SQLQuery), and the results are what I expect. However, when I try to use the DbSet-derived members of the Entity class, my queries are always being directed at the database that I used to generate the model, regardless of the database identified in the connection string.
Stepping through the code, I can see that the settings in the entity object all look correct (entity.Database.Connection.ConnectionString looks exactly like I would expect, and entity.Database.Connection.Database has the correct database name). However, if I examine the internal query value (DbSet.SQL) for any of the DbSet objects, I get something similar to this:
SELECT
Extent1.ID,
Extent1.Field1,
Extent1.Field2,
Extent1.Field3,
FROM entity_model.table1 AS Extent1
where entity_model is the database that I used to create the model. I've done enough testing to know that the queries are actually being executed against this incorrect database, rather than the one passed in through the connection string.
As I've said, I've used this same technique on other projects and have not run into this issue. I've even stepped through some old code and I can see that the contents of the DbSet.SQL field contains no reference to any database (neither the one used to create the model, nor the one from the connection string). This is what I would expect to see in my new project as well.
Can someone explain to me how EF decides whether to include the schema name in the DbSet.SQL field? This is making me a little crazy.
I've tried this across a number of the 50 or so databases that I have installed on my machine, so I know the problem is not with any particular database.

I've found a solution (actually 2 solutions) to this problem. The basic problem is that EF is using the schema specified in the edmx EntitySet element by default:
<EntitySet Name="table1" EntityType="Self.table1" Schema="entity_model" store:Type="Tables" />
This behavior can be turned off in 2 ways. Either add the following lines to your applications startup code:
using Devart.Data.MySql.Entity.Configuration;
...
var config = MySqlEntityProviderConfig.Instance;
config.Workarounds.IgnoreSchemaName = true;
Or add the following sections to your config file:
inside the configSections element:
<section name="Devart.Data.MySql.Entity" type="Devart.Data.MySql.Entity.Configuration.MySqlEntityProviderConfigurationSection, Devart.Data.MySql.Entity.EF6, Version=8.21.2066.0, Culture=neutral, PublicKeyToken=09af7300eec23701"/>
And then after the configSections element:
<Devart.Data.MySql.Entity xmlns="http://devart.com/schemas/Devart.Data.MySql.Entity/1.0">
<Workarounds IgnoreSchemaName="true"/>
</Devart.Data.MySql.Entity>
I found a third option online, and that was to simply delete the schema name from each of the EntitySet elements in the edmx file, but I think that could cause some issues if you ever try to update the model from a database.
I'm hopeful that this answer will prove useful to someone else down the road.

Related

Do I need a migration for EF code first when the database does not exist in SQL Azure?

I have tried lots of variations of EF migration v6.0.1 (from no database, to empty databases to existing databases) and I have a particular problem with Azure DB instances not being able to correctly be created on first deploy using Octopus deploy.
There are a number of places where this could be going wrong, so I thought I would check some basics of EF Code First migration with you fine people if I may:
If I create a code-first model, and know that the database does not exist in the intended target database server on Azure. With the default of 'CreateDatabaseIfNotExists' approach and with AutomaticMigrations disabled;
If I then call 'migrate.exe' with the assembly containing my DbContext and migration Configuration will I get a new database created with the current state of the model? or will I get a new database with nothing in it? i.e. do I need to explicitly 'add-migration' for the initial state of the model?
I have read in the documentation that the database instance should be created automatically by the migration process, but no one states clearly (at least to me) that this newly created database will be generated with the current model without a formal 'initial state' migration created.
So the question is this: do I need an explicit migration model generated for migrate.exe to work from?
Through whatever means I try, I get a database but the application launches with the unfriendly message "Model compatibility cannot be checked because the database does not contain model metadata. Model compatibility can only be checked for databases created using Code First or Code First Migrations." Remembering that this is the same application library that just created the database in the first place (from scratch) I fail to understand how this has happened!
I did manually delete the target database a few times via SQL Server management studio, is this bad? Have I removed some vital user account that I need to recover?
Migrations and the Database Initializer CreateDatabaseIfNotExists are not the same.
Migrations uses the Database Initializer MigrateDatabaseToLatestVersion, which relies upon a special table in the database _MigrationsHistory.
By contrast, CreateDatabaseIfNotExists is one of the Database Initializers which relies upon the special database table EdmMetadata. It does exactly as it implies: Creates a database with tables matching the current state of the model, i.e. a table for each DbSet<T>, only when the database does not exist.
The specific error you have quoted here, Model compatibility cannot be checked because the database does not contain model metadata., occurs due to the existence of DbSet<T> objects which were added to the code base after the initial database creation, and do not exist in EdmMetadata.
There are 4 basic Database Initializers available, 3 of which are for use when migrations is not being used:
CreateDatabaseIfNotExists
DropCreateDatabaseWhenModelChanges
DropCreateDatabaseAlways
Also note, the 4th Initializer, MigrateDatabaseToLatestVersion, will allow you to use Migrations even if AutomaticMigrations is disabled; AutomaticMigrations serves a diffierent purpose, and does not interact with the Database Initializers directly.
If you intend to use Migrations, you should change the Database Initializer to MigrateDatabaseToLatestVersion and forget about the other 3. If, instead, you intend to not use Migrations, then the choice of Initializer is situational.
CreateDatabaseIfNotExists will be more appropriate when you are certain that your data model is not undergoing active change, and you only intend to be concerned with database creation on a new deployment. This Initializer will elp ensure that you do not have any issues with accidental deletion of a database or live data.
DropCreateDatabaseWhenModelChanges is most appropriate in development, when you are changing the model fairly often, and want to be able to verify these changes to the model. It would not be appropriate for a production server, as changes to the model could inadvertently cause the database to be recreated.
DropCreateDatabaseAlways is only appropriate in testing, where your database is created from scratch every time you run your tests.
Migrations differs from these 3 Database Initializers, in that it never drops the database, it instead uses Data Motion to execute a series of Create Table and Drop Table SQL calls.
You also can use Update-Database -Script -SourceMigration:0 in the Package Manager Console at any time, no matter which Database Initializer you are using, to generate a full SQL script that can be run against a server to recreate the database.
Firstly, many thanks to Claies who helped me get to the bottom of this problem. I have accepted his answer as correct as ultimately it was a combination of his response and a few additional bits of reading that got me to my solution.
In answer to the actual posts question 'Do I need a migration for EF code first when the database does not exist in SQL Azure?' the answer is yes you do if you have disabled automatic migrations. But there is a little more to be aware of:
The Azure aspects of this particular problem are actually irrelevant in my situation. My problem was two-fold:
The migration being generated was out of sync with respect to the target model. What do I mean? I mean, that I was generating the migration script from my local database which itself was not in sync with the local codebase which created a migration that was incorrect. This can be seen by comparing the first few lines of the Model text in the __MigrationHistory. This awareness was helped by referring to this helpful post which explains how it works.
And more embarrassingly (I'm sure we've all done it) is that my octopus deployment of the web site itself (using Octopack) somehow neglected to include the Web.Config file. From what I can tell, this may have occurred after I installed a transform extension to Visual Studio. Within my nuget package I can see that there is a web.config.transform file but not a web.config. Basically this meant that when the application started up, it had no configuration file to turn to, no connections string at all. But this resulted in the slightly misleading error
Model compatibility cannot be checked because the database does not
contain model metadata.
Whereas what it should have said was, there isn't a connection string you idiot.
Hopefully this helps people understand the process a little better after reading Claies answer and also that blog-post. First though, check you have a web.config file and that it has a connection string in it...

entity framework with two databases (completely different schemas )

I am very new to entity framework and I am trying to do something like following scenario.
I am creating ASP.net web from application. That website needs to connect to two databases which the schemas are completely different.
I have no idea how the basic structure should be.
I though of have EF on class library. please guide me with instructions since I have less knowledge.
Since you are using two different databases, the only viable option is to create two separate conceptual models. Even if you would be able to merge two different databases into a single conceptual model, it would be a pain to maintain is the databases are of mentionable sizes.
The two models could reside within the same project, in seprate folders to get different namespaces.
E.g.:
Company.MyApp.DataModel
Company.MyApp.DataModel.Model1
Company.MyApp.DataModel.Model2
Then you could put a new layer on top of these two models which do all the heavy lifting, and could even make them look like one database if you want that, or merge data from entities in both models into a DTO or something similar.
Check out the Repository pattern.
If you think about it, when you create a EDM model with Visual Studio it ask you to give an existing database, and when finished creating the model, it generates an EF connection string, that internally address to the given underlying database connection string.
E.g: metadata=res:///EFTestModel.csdl|res:///EFTestModel.ssdl|res:///EFTestModel.msl;provider=System.Data.SqlClient;provider connection string="Data Source=.\;Initial Catalog=EFTest;Integrated Security=True;MultipleActiveResultSets=True"*
So each model matches only a database, only a connection string.
EF4 still does not support creating one conceptual model which works with N storage models. At least this is not supported with any built-in provider. Perhaps in the future this could be done through a new provider that combines the support of many storages (from the same providers or different).
I havent done enough research on it, but perhaps Windows Server AppFabric (Codename Velocity) could be the bridge to go through this gap.
Note: I have tried even editing manually the xml for the EDM (edmx) to insert a second element inside the tag but it does not match the EDM XML Schema so VS warns about it: Error 10021: Duplicated Schema element encountered.
You are going to use model first approach as the database already exists.
You will need to create two edmx for the two database.
You can create one model project which you can have connection strings for both the edmx in the app.config file and then create your edmx.

Entity Framework Code First on external SQL server

I'm following the Entity Framework tutorial on:
Link
I've downloaded the source code, and ran. The project works fine (using the default connection string).
<add name="SchoolContext" connectionString="Data Source=|DataDirectory|School.sdf" providerName="System.Data.SqlServerCe.4.0" />
Next i've changed the connection string to connect to a remote server (which successfully connects). However the tables aren't created and when running the application and accessing the controller I get the following error.
Error:
Model compatibility cannot be checked because the database does not contain
model metadata. Model compatibility can only be checked for databases created
using Code First or Code First Migrations.
My database user is 'dbowner' so I wouldn't imagine it's database access issues.
I'm new to EF, and don't know much about Code First Migrations. Have you come across the above error, and would Code Migrations solve this issue? If so why?
From my reading (please correct me if I am wrong) the scenario here is that you have an existing (perhaps empty) database on a remote server that you wish to put your EF code-first work into.
The error is coming about because, I think, EF is looking for a _MigrationHistory table (metadata about what EF code-first migrations have been run against it etc) and can't find it. There is some reasonable coverage of this table here for some background/interest reading.
So to resolve the issue, I think the steps to take are:
Initialise your database so that it acknowledges the EF code-first stuff
Update the database, rolling forward all your migrations to date
I found some good coverage of how to do this here. This blog also has some good coverage of EF topics in general
PS. I am guessing that your .dsf/SQL Compact Edition database wouldn't have had this problem because EF code-first would have created it for you on first run so it was already acknowledged as being a code-first database.
Here is a link to Entity Framework Power Tools. It is made for creating models by 'Reverse Engineering' your Database on a remote server. You can then easily access this database using EF.
Reverse Engineer Code First - Generates POCO classes, derived DbContext and Code First mapping for an existing database
Both of the initializer methods which I had tried fail when the database already exists:
Database.SetInitializer<Context>(new Initializer());
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Context>());
However it is possible to force the database to be dropped using:
Database.SetInitializer(new DropCreateDatabaseAlways<Context>());
The following SO post provides the answer to my question:
Setting up a Entity Framework Code First Database on SQL Server 2008
I've tried a combination of the two, and have decided that the best solution is to manually go into SQL Management studio and DROP the database, and re-create it using the initializer as this allows me to Seed the contents of the database.
Database.SetInitializer<Context>(new Initializer());
See here for more information on Seeding the database as it is also quite an unstable processess!
How can I get my database to seed using Entity Framework CodeFirst?

NHibernate using 2 schema's within the same application?

Imagine you are writing a large scale application using NHibernate and you want to have 2 seperate schema's (using Sql Server by the way)
Application_System (all the tables relating to the system, config tables, user tables etc)
Application_Data (all the actual data that is stored/retrieved when the user interacts with the system)
Now I've been trying to find a simple clean way to do this in NHibernate and thought I'd found a solution by using the Catalog and Schema properties so for example:
Catalog("Application_System");
Schema("dbo");
Table("SystemSettings")
would generate sql for Application_System.dbo.SystemSettings. And this kinda works but if I have 2 Catalogs defined then the Create/Delete tables functionality of hbm2ddl.auto stops working. Now I've come to the conclusion that I am probably abusing the Catalog and Schema properties for something it wasn't intended for. However I can't seem to find a simple way of achieving the same thing that doesn't involve some convoluted scaffolding.
Any help would be appreciated. I can't believe NHibernate wouldn't support this out of the box I mean it's a fairly basic requirement.
SchemaExport does not support creating schema/catalog ootb but you can add the create schema/catalog ddl by yourself using auxiliary objects in xml, FluentNHibernate or MappingByCode. Note that the auxiliary object has to be added first.
Ok well I kind of found a half way house that I'm reasonably satisfied with. The ISession has a Connection property that exposes a ChangeDatabase(string databaseName) method that allowes you to change the database the session is pointing to.
My schema export is still knackered because ultimately it doesn't know which object is for which database so will attempt to save it all to the database defined in the configuration.
You win some you lose some.

Code First vs. Database First

I created an Entity Framework model based on an existing database, then generated the POCO entities from the model. The connection string in my web.config isn't Entity Framework, it's just the standard connection string (it's missing the CSDL, SSDL, MSL references).
I can compile my application, but when I run I get this error:
Code generated using the T4 templates for Database First and Model
First development may not work correctly if used in Code First mode.
To continue using Database First or Model First ensure that the Entity
Framework connection string is specified in the config file of
executing application. To use these classes, that were generated from
Database First or Model First, with Code First add any additional
configuration using attributes or the DbModelBuilder API and then
remove the code that throws this exception
My question is, where in my code does it realize the POCOs came from auto generation, and how can I get it to behave like Code First? I don't want to reference the CSDL etc in my connection string.
If the connection string has the metadata, EF thinks it is Model First or Database First. If it is a plain connection string, EF thinks it is Code First. However, if you want to start out doing model first but make EF think you are really doing code first (which is what you are doing), make sure you are using the DbContext code generator, not the default one. Code first POCOs are really that--"plain old c# objects"-- no special database aware or change tracking stuff in them at all. To use the DbContext code generator, right click on your model diagram and choose "Add new code generation item..." then select the ADO.NET DbContext Generator. Also, depending on how you named your primary and foreign keys and/or whether they are more complicated than just simple int IDs, you will probably need to fill in some code to map the relationships between your objects in the "OnModelCreating" method in your context. Delete the line throw new UnintendedCodeFirstException(); and replace it with your mapping code. Otherwise EF may not be able to figure out all the relationships (remember there's no metadata for it to rely on).
Hope this helps.
You need the following in your config file:
<connectionStrings>
<add name="<The name of your class>"
connectionString="metadata=res://*/<test>.csdl|res://*/<test>.ssdl|res://*/<test>.msl;provider=System.Data.SqlClient;provider connection string="data source=<your source>;initial catalog=<your db>;persist security info=True;user id=<your user id>;password=<your password>;multipleactiveresultsets=True;App=EntityFramework""
providerName="System.Data.EntityClient" />
</connectionStrings>
I'm using Database first and resolved this by copying the EDMX generated connection string to the app.config of my startup application. One already existed but apparently they were different

Categories

Resources