How and when does Entity Framework Code First generate the DB? - c#

I came across this related question: How can I generate DDL scripts from Entity Framework 4.3 Code-First Model?
But this doesn't appear to answer the question of when a Code First application actually checks the existence/correctness of the DB and modifies it if necessary. Is it at run-time or build time? Assuming it's at run-time is it at start-up or when you create the DbContext or at the last possible moment e.g. when you try to write/read the DB table(s) it checks they exist on a case-by-case basis?

It is ceated at rutime the first time you access an entity, ie,
using (var db = new MyDBContext())
{
var items = db.MyObj.Count() // <- Here it is created!
}
There are some flavors on how, like if you set the creating strategy to CreateDatabaseIfNotExists, DropCreateDatabaseAlways, Etc. Please give this a look:
http://www.entityframeworktutorial.net/code-first/database-initialization-strategy-in-code-first.aspx

The column Model in the table __MigrationHistory is serialized and gzipped(base64) version of your EDMX. In code first the column Model is generated by Add-Migration and stored in the second part of the migration partial class and in the database when the database is created as binary stream varbinary(max).
When the database initializer (Database.SetInitializer) is called, then EF generate from the classes on the fly(Runtime) the current Entity Data Model(EDMX). The generated model will be serialized, zipped(base64) and finally compare it with the stored Model of the migration history table.
The comparison happens before the DbContext is created and, if the two Models(binary streams) are not identical then you will get a compatibility exception.

Related

EF Core code-first: cross-schema table relations and GenerateCreateScript()

We are using .NET Core 3.1, Microsoft.EntityFrameworkCore v3.1.9 and Npgsql.EntityFrameworkCore.PostgreSQL v3.1.4. We are developing unit tests and we would like to have a clean database each time tests are run. We have 2 database schemas and corresponding DbContext files:
schema
DbContext
test1
Test1DbContext.cs
test2
Test2DbContext.cs
Some tables from schema test1 reference tables from schema test2 with foreign keys. When I call test1DbContext.GetService<IRelationalDatabaseCreator>().GenerateCreateScript(), it also outputs script for creating referenced tables from schema test2. When test2DbContext.GetService<IRelationalDatabaseCreator>().GenerateCreateScript() is called and its output is executed, it throws the following exception for the tables that were already created by previous call:
Npgsql.PostgresException: '42P07: relation "<xxx>" already exists'
This is the code that we have currently. Pay attention to the comments.
private void PrepareEmptyDatabase(Test1DbContext test1DbContext, Test2DbContext test2DbContext)
{
// this drops the *whole* database, not just the `test1` schema
test1DbContext.Database.EnsureDeleted();
// creates empty database without any schemas
test1DbContext.GetService<IRelationalDatabaseCreator>().Create();
// create `test1` schema and related tables from schema `test2`
var script1 = test1DbContext.GetService<IRelationalDatabaseCreator>().GenerateCreateScript();
test1DbContext.Database.ExecuteSqlRaw(script1);
// try to create tables from `test2` schema - exception: some of them already exist,
// they were created with test1DbContext.Database.ExecuteSqlRaw(script1)
var script2 = test2DbContext.GetService<IRelationalDatabaseCreator>().GenerateCreateScript();
test2DbContext.Database.ExecuteSqlRaw(script2);
}
Is there a way to re-create the database from C# database models when there are cross-schema foreign keys?
In general, the answer is no... If you have relationships (foreign keys) between your tables, then these tables should be modeled together in the same EF Core model, using the same DbContext type. A single DbContext can have a model which has tables in multiple schemas - I'd highly advise having a single DbContext here. What is your reason for wanting to do a DbContext-per-schema setup?
As a workaround, you can tell EF Core to not create certain tables in migrations (see the docs). So a table can be created via one context, and ignored (for migrations) in the other.

Including a new table in the DBContext made with CodeFirst

We have a Data library they have built it with CodeFirst.
Now I ran a SQL command and added a new table to that database.
But Now I want to also see its generated object class, its DBContext definitions ,etc.. in the code first code so I can use them in my LINQ queries.
So what I did was following number 3 method from this MSDN page:
https://msdn.microsoft.com/en-us/library/jj200620.aspx
But it didn't do it right. For example it did not add any definition for this new table to DBContext.cs file, for example all my other tables that used to be there are defined like this:
DbSet<Zipcode> Zipcodes { get; set; }
But it has not added anything for me.
What is the correct way to do this?
I'm unaware of a way to simply add a new table and have it 'plug-n-play' with your existing model without manual work. The two options I know of are:
Rebuild the model using Code First from DB and include your added table
Manually create the table as a class and add the DbSet and entity in the OnModelCreating method in your model
Code First from Database only works when you already have a database. If you want to add a new table, you will have to start using Code-First(alone), that means: add the entity Zipcode to the model, DbSet to the DbContext and after that when you compile it will generate de table in the database.

how to insert/update entity framework entities that are partially mapped to a view?

I have a database with an 'Equipment' table and a view called 'EquipmentStatuses' that does something complicated to associate each entry in the 'Equipment' table to some aggregated value from another table and returns a simple 2 column view with the EquipmentId and a calculated value.
I have mapped the 'Equipment' table to an Equipment entity and created one extra scalar field that maps to the calculated field from the View. It all works fine when I just retrieve records from the database, but when I try to insert or update the Equipment table I get an error from Entity Framework:
Unable to update the EntitySet 'EquipmentStatuses' because it has a DefiningQuery and no <InsertFunction> element exists in the <ModificationFunctionMapping> element to support the current operation.
It seems that entity framework is trying to insert something into the view, but fails because I haven't actually told EF how to do that. However, the single field from the view doesn't need to be updated because it's calculated automatically from another table.
Is there a way to tell entity framework to ignore the view and just update the Equipment table without writing all sorts of boilerplate insert/update/delete stored procedures?
I'm using EF6 with a SQL Server database.
You can detach that object from the context:
dbContext.Entry(EquipmentStatuses).State = EntityState.Detached;
If this is MVC you could also compose viewmodels, send them to the view and then map them back to the proper entities on the POST. A tool like Automapper is great for this.

Problems in handling EntityFramework objects

(Using: Visual Studio 2010, SQL Server 2012, Entity Framework 4.0, MVC3 web application)
I have tables with one-to-many and many-to-many relationships, I used (database first) to automatically generate model classes, meaning I have objects inherited from EntityObject and the base class inherited from ObjectContext.
I am having trouble with (inserting, updating, deleting) objects.
For example when creating an object: I have a many-to-many relationship between 2 tables (Area and Cell) and the middle table (CellArea), and I want to add a new Cell object which is connected to many areas, so I did the following code:
Cell _cell = new Cell()
foreach (Area ar in current_areas)
{
var ca = new CellArea();
//ca attributes
_cell.CellAreas.Add(ca);
}
db.SaveChanges();
db.Cells.AddObject(_cell);
db.SaveChanges();
I tried other code snippets and none of them worked, I always get IEntityChangeTracker and other similar exceptions.
What is the proper way to manage such cases?
When there are intertable relationships EF should create what is known as navigational properties. When you perform the initial query you can either choose to have those properties loaded or not. In particular EF has a keyword named Include that allows you to populate those other properties at will. If all the other date is included and any changes are made, you only have to call the SaveChanges method and the original table data and all navigational property data is saved in one shot.
Google "EF Include"

Reading several tables from one single Entity Framework ExecuteStoreQuery request.

I have a library which uses EF4 for accessing a SQL Server data store. For different reasons, I have to use SQL Server specific syntax to read data from the store (for free text search), so I have to create the SQL code by hand and send it through the ExecuteStoreQuery method.
This works fine, except that the query uses joins to request several tables aside the main one (the main one being the one I specify as the target entity set when calling ExecuteStoreQuery), and EF never fills up the main entity's relationship properties with the other table's data.
Is there anything special to do to fill up these relationships? Using other EF methods or using special table names in the query or something?
Thanks for your help.
Executing direct SQL follows very simple rule: It uses column from the result set to fill the property with the same name in materialized entity. I think I read somewhere that this works only with the the main entity you materialize (entity type defined in ExecuteStoreQuery = no relations) but I can't find it now. I did several tests and it really doesn't populate any relation.
Ok so I'll write here what I ended up doing, which does not looks like a perfect solution, but it does not seem that there is any perfect solution in this case.
As Ladislav pointed out, the ExecuteStoreQuery (as well as the other "custom query" method, Translate) only maps the column of the entity you specify, leaving all the other columns aside. Therefore I had to load the dependencies separately, like this :
// Execute
IEnumerable<MainEntity> result = context.ExecuteStoreQuery<MainEntity>(strQuery, "MainEntities", MergeOption.AppendOnly, someParams).ToArray();
// Load relations, first method
foreach (MainEntity e in result)
{
if (!e.Relation1Reference.IsLoaded)
e.Relation1Reference.Load();
if (!e.Relation2Reference.IsLoaded)
e.Relation2Reference.Load();
// ...
}
// Load relations, second method
// The main entity contains a navigation property pointing
// to a record in the OtherEntity entity
foreach(OtherEntity e in context.OtherEntities)
context.OtherEntities.Attach(e);
There. I think these two techniques have to be chosen depending on the number and size of generated requests. The first technique will generate a one-record request for every required side record, but no unnessecary record will be loaded. The second technique uses less requests (one per table) but retrieves all the records so it uses more memory.

Categories

Resources