Entity Framework core navigation property between two contexts - c#

There are similar questions with this issue, but not for EF Core. Not a duplicate of Entity Framework Core Using multiple DbContexts . That one is related to not being able to access the database at all from the second context, and the two contexts use different databases. This question is about a single database and the issue is related to migrations
I have two EF core db contexts using the same SQL Server database.
In the first context I have many entities, one of them is User.
In the second one there is a single entity called UserExt which has a navigational property to User
public class UserExt
{
[Key]
public long UserID { get; set; }
public virtual User User { get; set; }
[Required]
public string Address { get; set; }
}
The issue is that when creating the migration for UserExt using 'add-migration', all entities from the first context are also included.
Tried providing the context, but same result
add-migration --context SecondContext
With EF 6 it was possible to solve this using ContextKey (https://msdn.microsoft.com/en-us/library/system.data.entity.migrations.dbmigrationsconfiguration.contextkey(v=vs.113).aspx) but has not been ported to EF Core
Is there a way to make this work so that the migrations in the second context would contain only its entities ?

Solved using database context inheritance. This way I can have separate migrations.
public class SecondDbContext : FirstDbContext
{
public virtual DbSet<UserExt> ExtendedUsers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("connectionString", options =>
options
.MigrationsAssembly("SecondDbContextAssemblyName")
.MigrationsHistoryTable("__SecondEFMigrationsHistory") // separate table to store migration history to avoid conflicts
);
}
}
Because SecondDbContext is inherited from FirstDbContext, every entity change made in the base context will be inherited. Which means that when a new migration is added to the second context, it will try to apply the changes again from the first context again. Workaround is to:
Add a new migration (for example Add-Migration Inherit_FirstDbContext)
Delete everything from the migration's Up and Down methods
Apply the empty migration in the database. (Update-Database)
This ensures that the Entity Framework snapshot will contain the changes, without actually having to re-apply them in the database.

Related

EF Core - Reuse Table entity

I have a database, which exists on 3 different stage servers.
The database is identical on all three servers.
I write an application to sync the database tables based on some logic.
For that approach i wrote an generic database context which contains the entites, because they are identical on all servers as well:
public abstract class GenericContext : DbContext
{
public GenericContext(DbContextOptions<ContextA> options)
: base(options)
{
}
public GenericContext(DbContextOptions<ContextB> options)
: base(options)
{
}
public GenericContext(DbContextOptions<ContextC> options)
: base(options)
{
}
public DbSet<Application> Applications { get; set; }
[...]
}
The thought behind this was to handle the entites like Application centralized.
The entity Application looks like:
[Table("Applications", Schema = "dbo")]
public class Application
{
public string Alias { get; set; }
[Key]
public int Id { get; set; }
[...]
}
In my startup class i register all 3 contexts with their matching DbContextOptions.
The reason for the approach is that my repositories expect a genric context to minimize the overhead to handle 3 different database types.
An example of this is:
public int AddApplication(GenericContext context, Application entity)
{
context.Applications.Add(entity);
return entity.Id;
}
When i start my application everything works fine, until i try to access one of the contexts and they get actually build up.
Then the following exception is thrown:
Cannot use table 'dbo.Applications' for entity type 'Application'
since it is being used for entity type 'Application' and potentially other
entity types, but there is no linking relationship.
Add a foreign key to 'Application' on the primary key properties and
pointing to the primary key on another entity type mapped to 'dbo.Applications'.
As the exception states it seems to be not possible to reuse the table entity for multiple contexts.
Is there any way to manage the entities in the desired centralized way but avoid the exception?
Finally i found the source of my error.
The entity Application was used in more than one of my contexts.
My thought was, that they were identically because the point to the same database table, but they differ in some of the navigation properties and so ef core correctyl states that the table is used more then once.
My solution was to unify the entity Application for both contexts.

Make IdentityUser.PhoneNumber nullable

Im trying to make PhoneNumber nullable for my IdentityUser model, but its not working. I have this User class
public class User : IdentityUser<Guid>
{
...props
}
I tried adding it in my Context like:
public class DbContext: IdentityContext<User, Role, Guid>
{
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
ConfigureUserTable(builder);
}
private void ConfigureUserTable(ModelBuilder builder)
{
builder.Entity<User>
.Property(x => x.PhoneNumber)
.IsRequired(false);
}
}
but when I created a new migration, nothing changed in the new migration.
Also tried to override like:
public class User : IdentityUser<Guid>
{
public override string? PhoneNumber { get; set; }
...props
}
But still nothing in the new migration.
How can I make PhoneNumber optional on DB without creating a new migration and updating the field by writing explicit SQL?
It seems like the problem was that someone went and made the column on DB not nullable manually. Because of that, nothing new was generated when I was running migrations.
Based on your answer, it looks like you had a situation related to your environment. In case you are new to migrations, it's SUPER important that you don't make direct database modifications to any tables handled by EF Migrations. You can get naming conventions out of sync, or in your case, migrations that just don't appear to do what you need to.
Entity Framework, although appears like it "checks to see if the model matches", what it's really doing is checking to see if the last applied migration matches the ones it thinks it needs to run. To Date, I don't think there is a way to have EF validate every single field during a migration add.
Just something to be aware of. If you have DBAs who don't want modifications made directly, use the new migration bundles for deployment or script them.
Deployments of EF Migrations Reference

Asp.Net identity and CodeFirst migrations

I installed nuget packages for asp.net identity and followed this MS tutorial which, by creating a new user, creates all of the tables e.g. AspNetRoles, AspNetUsers in my database.
The problem is, I'm trying to use CodeFirst migrations for DB source control and my Visual Studio side has none of the models for these tables. I don't want the "Create user" method to automatically create the tables, I need to get my models in Visual Studio and then push it using a migration.
Currently I have all the [AspNet] tables in my database and no reflection of this in my migrations. I can delete the tables but need to know how to populate the code first!
I'll also probably need to customise the models later, but that's another issue.
You can extent your asp.net identity as bellow
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Attendance> Attendances { get; set; }
public DbSet<PunchRecord> PunchRecords { get; set; }
}
Took me two hours to find I simply needed to add a model that inherits IdentityUser and then add a migration. I've called the model "ApplicationUser" - based on a tutorial I found on how to extend AspNetRoles and an answer somewhere in stackoverflow, it seems this name is possibly automatic or scaffolded somewhere... but I just chucked it manually in my Models folder:
public class ApplicationUser : IdentityUser
{
}
Also, I had to tinker with my application's context to inherit from IdentityDbContext - this way I don't need two separate connection strings and my app context is now all inclusive.

What is the workflow in code-first EF to update database models without breaking published site?

Let's say I have a simple site with one context
public class DataContext : DbContext
{
public DbSet<Stuff> Stuff { get; set; }
}
and one model
public class Stuff
{
public int ID {get;set; }
public string Name {get;set;}
}
And I'm using code-first EF model, and I hit update-database and it creates my tables and then I publish my site. Everything is great. However, then I decide I want to add a property to my model
public class Stuff
{
public int ID {get;set; }
public string Name {get;set;}
public int StuffType {get;set;}
}
And now I hit update-database and it updates the database with the new property, but the published site is now broken because its model is out of date with the database model.
Other than frantically trying to republish the site before anyone notices, is there any other way work with code-first migrations without breaking a published site?
Do I need to have two databases? If so, how do I then maintain the models between the two? This is further compounded by the fact that I usually have (at least) two git branches, and the published site is running the code from the master branch and I'm working on a develop branch - which is where I'd be modifying my models.
What is the usual workflow for avoiding these sorts of problems?
Try disabling the database initializer for your DataContext.
See this answer Entity Framework 6.1.1 disable model compatibility checking

EF migration shows empty Up() Down() methods

I have a local database that is currently in it's second version and should now go to it's third version.
The code for the previous migrations was generated by another programmer so I am assuming I am doing something wrong here.
In my model there are around 30 classes, and inside the model folder there is a mapping folder and it contains the mappings for those 30 classes.
So now I added 1 new class in the same manner as those previous classes and then run the add-migration command in the Package Manager Console.
Infortunately I get an empty migration Up() and Down() method.
When I look in the database there is a __migrationHistory available with the previous 2 migrations. If I run my application now, the third migration is also added but obviously the new table is not being created because it's not in the Up() method.
What could I be doing wrong?
I think something is going wrong when scaffolding the previous migrations... It's like it can't find the new Code-First classes I have added.
This is my command:
add-migration "1.2" -verbose -ProjectName "MyEFproject"
I am assuming that the scaffolding doesn't know where to look for the new class... or is this by convention that all model classes are just expected to be in the project?
Result of add-migration:
namespace MyProject.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class _1002 : DbMigration
{
public override void Up()
{
}
public override void Down()
{
}
}
}
Sample of new Model Class:
using System;
using System.Collections.Generic;
namespace MyProject.Models
{
public partial class MyTable
{
public string SomeId { get; set; }
public string SomeText { get; set; }
}
}
Sample of new Mapping class
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
namespace MyProject.Models.Mapping
{
public class MyTableMap : EntityTypeConfiguration<MyTable>
{
public MyTableMap()
{
// Primary Key
this.HasKey(t => t.SomeId);
// Properties
this.Property(t => t.SomeText)
.IsRequired()
.HasMaxLength(30);
// Table & Column Mappings
this.ToTable("MyTable", "database");
this.Property(t => t.SomeId).HasColumnName("SomeId");
this.Property(t => t.SomeText).HasColumnName("SomeText");
}
}
}
Thank you,
You need to add your table to your implementation of the DbContext class, e.g.
public class MyDatabaseEntities : DbContext {
public virtual DbSet<MyTable> MyTable { get; set; }
}
While rolling back an existing EF Core Data Context back to empty, my migrations wouldn't generate until I removed the ApplicationDbContextModelSnapshot that accompanied the migrations.
This class is auto-generated and needs to align with your current migration level.
I was able to fix this issue by deleting a record of last migration from _MigrationHistory table.
This record had been incorrectly created before I added DbSet for new model object to DbContext class.
After this deletion new migration was created with correct Up() and Down() methods.
I had this problem because I forgot to add {get; set;} after my variable names
You need to add your table to your implementation of the DbContext class, e.g. While rolling back an existing EF Core Data Context back to empty, my migrations wouldn't generate until I REMOVED the ApplicationDbContextModelSnapshot that accompanied the migrations.
In my case, the datacontext project is a class lib project. It is different from the startup project which is asp.net mvc 5 project. Now by mistake the connection string in the startup project is pointing to a different database.
So ensure that datacontext project and startup project point to the same database. Also use the full command as mentioned in the question like the following. You can include -Force as well.
add-migration "InitialMigration" -verbose -ProjectName "MyEFproject" -Force
Also: Make sure any new properties you've added are public!
In my case I was doing a migration where I added fields to an existing table and was ending up with empty Up and Down methods,
I had something like this:
public bool ExistingField { get; set; }
bool NewField { get;set; }
Can you spot the difference...?
If you make this mistake rerun the migration with the same name (you probably will need to add the -Force parameter to scaffold it full).
PS. Always make sure your project builds fully before attempting to do any kind of EF command. If your project doesn't already build you're asking for trouble.
You need to add your MyTable in Dbset and your issue will be resolved:
public DbSet<MyTable> MyTables { get; set; }
I was getting empty migrations added when I had mistakenly related two tables using a 1-many relationship rather than a many-many (i.e. i forgot one of the navigation properties). I had a seeding file that was expecting a many-many relationship and was subsequently failing during the migration causing the migration to fail. Unfortunately there was no output that made it obvious that was the problem and it was only by using the Entity Framework Power Tools (v4 but installed in VS2015) did i visually see the incorrect relationship and realize it was probably the cause.
I had to Update-Database with the latest migration before the empty one appending this parameter -TargetMigration:"{your-migration-name}".
Probably it will tell you that there will be data loss from the next buggy one we tried. If you can afford it append -Force to it.
Then I tried to add my new Add-Migration and it wasn't empty.
Final thing that you may need to do if above is throwing exception is to go SQL Server Management Studio and delete the last Automatic migration and try to add it again.
if new tables added to Context
just remove new table in "Migration/ExampleContextModelSnapshot"
I had the same issue on EFcore. When renaming Phone -> mobile, the migration came up empty.
My DbContext :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyUser>()
.Property(c => c.Mobile)
.HasColumnName("phone");
}
Problem was using .HasColumnName("phone") was overriding the actual property name so EF probably couldn't see any change.
Changing string value made it work.
I had this exact issue after I wanted to add an extra column to my database. Because my data would not seed unless the tables were empty, I deleted all the tables and the migrations to recreate the tables. When I tried to migrate, the migration had empty up and down methods.
I solved this by deleting the snapshot file as this was creating the issue. So I deleted all the migrations and the snapshot file, added the migration again and ran update database. The tables and migrations were successfully updated with my new column.
A better way to do this though is to run the down method and drop the tables like that if you are working on test data. Obviously this is bad in the real world to drop tables.
To me the problem was that Id property that should correspond to table id was named FeedbackId.
I changed to "Id" and then Up/Down weren't empty anymore.
Dunno if that can help somehow
If your project is small, i.e. you do not have too many migrations yet, you can delete all from your Migration folder. After that, add the migrations again.
I think this also happens when u try to do migration without any changes in the models. eg when you do migration one and succeed, when u try to do migration2 without doing any changes in any of the models, it will create empty UP and Down.
From the perspective of a complete Entity Framework (Core) beginner:
Create your class which will become your table
You can have subclasses with many-to-many or one-to-one relationships.
In step 3 you see the context where both properties have a one-to-one relationship.
Ensure you have one DbContext
If you have more than one DbContext you need to specify which context you want to add the migration to with the -Context parameter.
Add your class to your DbContext as shown by #CondingIntrigue
As a reference The Entity Framework Core DbSet
public class AccountContext : DbContext
{
public DbSet<Account> Accounts { get; set; }
public DbSet<SecretIdentity> SecretIdentity { get; set; }
}
Enter Add-Migration
In my case, I was encountering similar problems with Visual Studio Code.
I have fixed these by doing the following:
Check inside your ContextModelSnapshot : ModelSnapshot
Comment Model Entity Definition…
Delete your migration files related to these entity
Delete the migrations from the dbo.__EFMigrationsHistory table
Compile your solution.
Run the following commands:
dotnet ef migrations add migrationName -p ProjectContainer/
dotnet watch run
Temprorary remove
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
and then do initial create
Add-Migration InitialCreate
If after adding your class in the dbContext and your migration is still empty: do the following:
In your DbContextModelSnapshot class, remove every related code to that class name that you are trying to apply add-migration on. Save the DbContextModelSnapshot.cs and use the Add-Migration "Added_filename"
This work for me.
In my case ,I deleted Migration folder completely. As long as I didn't remove the "ApplicationDbContextModelSnapshot" and all previous migrationas it didn't work.
For me it was because I hadn't add Configuration files.
onModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
and add configurations in same assembly derived from IEntityTypeConfiguration<T> where T is your model.
I missed adding
{get;set}
After adding getter and setter, up and down methods are not empty.

Categories