EF: Seed of Migrations.Configuration vs Seed of DropCreateDatabaseAlways? - c#

My Question was originally: DropCreateDatabaseIfModelChanges fails to update database?
But I changed it because the role of these two methods is more ambiguous for me
.
...................
What I know so far that using DropCreateDatabaseIfModelChanges will recreate the database whenever the schema changes (IfModelChanges). That mean that I don't have to worry about migration, since it will recreate the database from scratch anyway, right?
I first created a class DataInitializer inheriting from DropCreateDatabaseIfModelChanges, and implemented the Seed method with the initial data, and called the Database.SetInitializer(new DataInitializer());
from the Main method, but I still get the error about model changing and I should add a migration (since I dropped a property from the entity to test the data initializing).
this is the initializer class:
public class DataInitializer : DropCreateDatabaseIfModelChanges<BlogContext>
{
protected override void Seed(BlogContext context)
{
var blogs = new List<Blog>
{
new Blog {FollowersCount=456, Name="ABC", Url="abc.com" },
new Blog {FollowersCount=789, Name="DEF", Url="def.com" },
new Blog {FollowersCount=246, Name="GHI", Url="ghi.com" },
new Blog {FollowersCount=135, Name="JKL", Url="jkl.com" },
new Blog {FollowersCount=258, Name="MNO", Url="mno.com" },
};
blogs.ForEach(x => context.Blogs.Add(x));
}
}
These are my questions: Does it work if migration is enabled, and what happens if I'm implementing the Seed method in the Configuration class with initial data- which one will be called (and I have a Seed method initialized with data already in the Configuration class)
After I added a migration, it throws this error (whether I called SetInitializer or not):
There is already an object named 'Blogs' in the database.

You have to tell the web.config file to use your initializer class whenever Model changes. Add the following code in the entityFramework tag in web.config file. Hope it will work..
<contexts>
<context type="YourProjectName.BlogContext,YourProjectName">
<databaseInitializer type="YourProjectName.DataInitializer , YourProjectName" />
</context>
</contexts>

Related

Disabling migrations

I'm writing a web api attached to a database that I created using Model First.
Being new I was confusing Model First with Code First.
As it's my first EF/Web app I didn't realize that migration wasn't yet implemented by Model First and I followed the procedure to implement it. When it ran I received some errors, looked them up and only then realized I'd need to remove it again.
Following somebody's instructions I deleted the migrations folder, plus the _migrationHistory table from the database.
But now I receive the following exception whenever the _migrationhistory table is missing from the database.
protected void Application_Start()
{
// Call initializer
Database.SetInitializer(new PulseDbInitializer());
new ApplicationDbContext().Database.Initialize(true); <- error here
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
"An exception of type 'System.NotSupportedException' occurred in EntityFramework.dll but was not handled in user code
Additional information: 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."
The following is a partial description of my initializer
public partial class PulseDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>, IPulseDbInit
//public partial class PulseDbInitializer :DropCreateDatabaseAlways<ApplicationDbContext>, IPulseDbInit
{
private ApplicationDbContext _context;
protected override void Seed(ApplicationDbContext context)
{
_context = context;
var pid = new PulseDbInitializionData(context);
pid.Init(this);
}
}// class PulseDbInitializer
It seems that the app is trying to use migrations (because it's creating the _migrationhistory table) even though I've removed the migrations subfolder.
Being new at this, I'm not sure where to go. I'm aware that I've created my initializer, see above, so am I missing something?
EDIT - Too long for a comment
Im not sure we're understanding each other.
Very simply, I don't know what's generating the _MigrationHistory table in my database as afaik if I remove the Migration subdirectory from my project and delete the database, then I thought that's all I need to do to drop migrations.
In my model first, the DropCreateDatabaseIfModelChanges is what I've always used for generating the database and is working as expected. It creates a new database, or recreates when the model changes.
Until I added the migration I didn't notice this _MigrationHistory table and I don't have a clue if the add-migration command added any code to my project that I need to delete or change (I really hate things happening and not being told exactly what was done in some kind of log. Please microsoft, for future reference)
Or am I totally wrong and the _MigrationHistory table was always there but I never noticed? It all seems to work, as long as I don't remove the table.
Your initializer is inheriting from DropCreateDatabaseIfModelChanges, which checks for model changes. Since you are not using code first you can't check for model changes in code (since your code doesn't define the model -that'd be code-first-... the model defines the code -that's model-first-)... so you need to implement your own IDatabaseInitializer which only checks (for example) that the database exists:
public partial class PulseDbInitializer : IDatabaseInitializer<ApplicationDbContext>, IPulseDbInit
{
public void InitializeDatabase(ApplicationDbContext context)
{
var exists = new DatabaseTableChecker().AnyModelTableExists(context.InternalContext);
if (exists == DatabaseExistenceState.Exists)
{
// maybe check if certain data exists and call the `Seed` method if
// it doesn't
return;
}
// Throw some error if it doesn't exist
}
protected override void Seed(ApplicationDbContext context)
{
_context = context;
var pid = new PulseDbInitializionData(context);
pid.Init(this);
}
}

EF Code first keeps dropping and creating database in Code First in IIIS

Following is a part of code in my DbContext that creates data and seeds data.
public class labSchedulerDbContext :DbContext
{
static abDbContext()
{
Database.SetInitializer(new abDatabaseInitializer());
}
public abDbContext() :base (nameOrConnectionString:"abDbContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Rest of the code is just declaring models.
In my setinitializer class with seed
public class abDatabaseInitializer : DropCreateDatabaseIfModelChanges<abDbContext>
{
.....
}
Code is working fine. Problem I have is when I created site in IIS and ran it. It runs fine but after few hours it will delete the database and initialize and seed data again. Thus we lose the data added. I tried to comment out lines where setinitilizer file is being called. Still didn't work. I refreshed apppool and site maybe it was cached files. But still no luck.
Can you please tell me how I can disable the whole initializer file once a database is created so no matter what it doesn't go and drops db and create a new one.
Cod
You can use this other initializer:
CreateDatabaseIfNotExists: This is default initializer. As the name suggests, it will create the database if none exists as per the configuration. However, if you change the model class and then run the application with this initializer, then it will throw an exception
The only thing that you have to do is to drop the database yourself before you deploy a new version with a different model. (Provided that's what you want to do).

The entity type <type> is not part of the model for the current context

I am getting into the Entity Framework, but I am unsure if I am missing a critical point in the code-first approach.
I am using a generic repository pattern based on the code from https://genericunitofworkandrepositories.codeplex.com/ and have created my entities.
But when I try to access or modify the entity I run into the following:
System.InvalidOperationException: The entity type Estate is not part
of the model for the current context.
It happens when I am trying to access it from my repository:
public virtual void Insert(TEntity entity)
{
((IObjectState)entity).ObjectState = ObjectState.Added;
_dbSet.Attach(entity); // <-- The error occurs here
_context.SyncObjectState(entity);
}
The database (./SQLEXPRESS) is created just fine, but the entities (tables) is just not created on startup.
I am wondering if I need to explicit set the mapping of the entities? Is EF not able to this by its own?
My Entity is:
public class Estate : EntityBase
{
public int EstateId { get; set; }
public string Name { get; set; }
}
My context is as so:
public partial class DimensionWebDbContext : DbContextBase // DbContextBase inherits DbContext
{
public DimensionWebDbContext() :
base("DimensionWebContext")
{
Database.SetInitializer<DimensionWebDbContext>(new CreateDatabaseIfNotExists<DimensionWebDbContext>());
Configuration.ProxyCreationEnabled = false;
}
public new IDbSet<T> Set<T>() where T : class
{
return base.Set<T>();
}
}
Is there any specific reason why this error occurs? I have tried enable migrations and enable automatic migrations without any help either.
Put this in your custom DbContext class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Estate>().ToTable("Estate");
}
If your tables are not created on startup, this is why. You need to tell the DbContext about them in the OnModelCreating method override.
You can either do custom per-entity mappings here, or separate them out into separate EntityTypeConfiguration<T> classes.
Apparently, this error is very generic, it could have a number of reasons. In my case, it was the following: The connection string (in Web.config) generated by the .edmx was invalid. After almost a day of trying everything, I changed the connection string from the EF string to an ADO.NET string. This solved my issue.
For example, the EF string looks something like this:
<connectionStrings>
<add name="BlogContext"
connectionString="metadata=res://*/BloggingModel.csdl|
res://*/BloggingModel.ssdl|
res://*/BloggingModel.msl;
provider=System.Data.SqlClient
provider connection string=
"data source=(localdb)\v11.0;
initial catalog=Blogging;
integrated security=True;
multipleactiveresultsets=True;""
providerName="System.Data.EntityClient" />
</connectionStrings>
And the ADO.NET string looks like this:
<connectionStrings>
<add name="BlogContext"
providerName="System.Data.SqlClient"
connectionString="Server=.\SQLEXPRESS;Database=Blogging;
Integrated Security=True;"/>
</connectionStrings>
Source: http://msdn.microsoft.com/nl-nl/data/jj556606.aspx
For me the issue was that I had not included the Entity Class within my db set inside the context for entity framework.
public DbSet<ModelName> ModelName { get; set; }
You may try removing the table from the model and adding it again. You can do this visually by opening the .edmx file from the Solution Explorer.
Steps:
Double click the .edmx file from the Solution Explorer
Right click on the table head you want to remove and select "Delete from Model"
Now again right click on the work area and select "Update Model from Database.."
Add the table again from the table list
Clean and build the solution
The problem may be in the connection string. Ensure your connection string is for SqlClient provider, with no metadata stuff related to EntityFramework.
My issue was resolved by updating the metadata part of the connection string. Apparently it was pointing at the wrong .csdl / .ssdl / .msl reference.
I've seen this error when an existing table in the database doesn't appropriately map to a code first model. Specifically I had a char(1) in the database table and a char in C#. Changing the model to a string resolved the problem.
One other thing to check with your connection string - the model name. I was using two entity models, DB first. In the config I copied the entity connection for one, renamed it, and changed the connection string part. What I didn't change was the model name, so while the entity model generated correctly, when the context was initiated EF was looking in the wrong model for the entities.
Looks obvious written down, but there are four hours I won't get back.
For me the issue was that I used the connection string generated by ADO.Net Model (.edmx). Changing the connection string solved my issue.
This can also occur if you are using a persisted model cache which is out of date for one reason or another. If your context has been cached to an EDMX file on a file system (via DbConfiguration.SetModelStore) then OnModelCreating will never be called as the cached version will be used. As a result if an entity is missing from your cached store then you will get the above error even though the connection string is correct, the table exists in the database and the entity is set up correctly in your DbContext.
The message was pretty clear but I didn't get it at first...
I'm working with two Entity Framework DB contexts sysContext and shardContext in the same method.
The entity I had modified\updated is from one context but then I tried to save it to the other context like this:
invite.uid = user.uid;
sysContext.Entry(invite).State = EntityState.Modified;
sysContext.SaveChanges(); // Got the exception here
but the correct version should be this:
invite.uid = user.uid;
shardContext.Entry(invite).State = EntityState.Modified;
shardContext.SaveChanges();
After passing the entity to the correct context this error went away.
I was facing the same issue with EntityFrameworkCore trying to update a range of values.
This approach did not work
_dbSet.AttachRange(entity);
_context.Entry(entity).State = EntityState.Modified;
await _context.SaveChangesAsync().ConfigureAwait(false);
After adding UpdateRange method and removing attach and entry everything work
_dbSet.UpdateRange(entity);
await _context.SaveChangesAsync().ConfigureAwait(false);
Sounds obvious, but make sure that you are not explicitly ignoring the type:
modelBuilder.Ignore<MyType>();
map of the entity (even an empty one) added to the configuration will lead to having the entity type be part of the context. We had an entity with no relationship to other entities that was fixed with an empty map.
if you are trying DB first then be sure that your table has primary key
Visual Studio 2019 seems to cause this for me. I fixed it by generating the edmx model again in 2017.
For me it was caused because I renamed the entity class.When I rolled it back it was Ok.
I had this
using (var context = new ATImporterContext(DBConnection))
{
if (GetID(entity).Equals(0))
{
context.Set<T>().Add(entity);
}
else
{
int val = GetID(entity);
var entry = GetEntryAsync(context, GetID(entity)).ConfigureAwait(false);
context.Entry(entry).CurrentValues.SetValues(entity);
}
await context.SaveChangesAsync().ConfigureAwait(false);
}
This was in an async method, but I've forgot to put await before GetEntryAsync, and so I got this same error...
Make sure you have set up your mapping class to point to your SQL table
I've had the same problem and in my case, the reason why I got this error message was that the property identifiers in my class file did not match the identifiers defined in the database, e.g. I wrote an identifier with a beginning uppercase letter while in the database it was all lowercase.
I've faced this issue after publishing my project using web deploy. It happened because my the metadata in connection string of my publish profile was not same as connection string in my project because I dropped the edmx for some reason and added it back with different Name. To fix it I had to delete the publish profile and redeploy again so that the metadata matches the names.
With models created from database (First Database), it is not possible to replace the connection string (with metadata and providerName="System.Data.EntityClient", from EDMX) by one of SQL.
The only possibility I have found is to create another context that uses the SQL connection (providerName="System.Data.SqlClient")
<connectionStrings>
<add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Max Pool Size=10000;Pooling=true;Data Source=MyIpServer;Initial Catalog=myDatabase;Persist Security Info=True;User ID=MyUser;Password=MyPassword;TrustServerCertificate=False" />
<add name="Entities" providerName="System.Data.EntityClient" connectionString="metadata=res://*/Datos.MyModel.csdl|res://*/Datos.MyModel.ssdl|res://*/Datos.MyModel.msl;provider=System.Data.SqlClient;provider connection string="Max Pool Size=10000;Pooling=true;data source=MyIpServer;initial catalog=myDatabase;persist security info=True;user id=MyUser;password=MyPassword;trustservercertificate=False;MultipleActiveResultSets=True;App=EntityFramework"" />
</connectionStrings>
With the two context options, with Identity:
public partial class ContextWithUsers : IdentityDbContext<MyUser>
{
public ContextWithUsers() : base("name=DefaultConnection")
{
}
}
public class MyUser : IdentityUser
{
// aditional table user data
//public virtual MyUserInfo MyUserInfo { get; set; }
}
Normal context:
public partial class ContextWithoutUsers : DbContext
{
public ContextWithoutUsers () : base("name=Entities")
{
}
}
This solution works, but... Why is it not possible to use the same context with the First Database model + Identity ?
NOTE 1: if you force change connection string show:
System.Data.Entity.Infrastructure.UnintentionalCodeFirstException: 'The context is being used in Code First mode with code that was generated from an EDMX file for either Database First or Model First development. This will not work correctly. To fix this problem do not remove the line of code that throws this exception. If you wish to use Database First or Model First, then make sure that the Entity Framework connection string is included in the app.config or web.config of the start-up project. If you are creating your own DbConnection, then make sure that it is an EntityConnection and not some other type of DbConnection, and that you pass it to one of the base DbContext constructors that take a DbConnection. To learn more about Code First, Database First, and Model First see the Entity Framework documentation here: http://go.microsoft.com/fwlink/?LinkId=394715'
NOTE 2: External Class library with EDMX + Data and ASP.NET webform project
Delete the .edmx file and add it again. Especially, if you have upgraded the Entity Framework.
Could be stupid, but if you only got this error on some Table, dont forget to clean your project and rebuild (could save a lot of time)

How to recreate database in EF if my model changes?

I created a DbContext like so :
public class myDB : DbContext
{
public DbSet<Party> Parties { get; set; }
public DbSet<Booking> Bookings { get; set; }
}
This generated my DataBase and the two tables above..Great
I then decided to add another DbSet into the mix & I got an error:
the model backing the 'Party' context has changed since the database was created
I'm aware of the fixes for this using modelbuilder.IncludeMetadataInDatabase = false; and Database.SetInitializer<ClubmansGuideDB>(null);
1) What's the correct way to add my new classes to the context and have them generated in the DataBase?
2) In my futile attempts to solve this myself I deleted the tables and tried to re-run the app with no success I then deleted the full database and re-run and it doesn't re-generate the DB. How can I start from scratch - is there some cache somewhere?
I believe you're looking for:
Database.SetInitializer<ClubmansGuideDB>(new DropCreateDatabaseIfModelChanges<ClubmansGuideDB>());
As of EF 4.1 and above.
Note that this assumes you have permission to even drop your database. I do this locally for ease of development but disable it in staging/production (I do a manual Schema Compare and push my changes).
By the way, for testing, if you need to force recreate the database, you can do:
using (var context = new ClubmansGuideDB()) {
context.Database.Initialize(force: true);
}
(using if you don't already have a reference to your DB)
You can let the Entity Framework recreate the whole database by using a Database Initializer or if you want to migrate data you can look at the Code First Migrations
The following would recreate your database when the model changes:
Database.SetInitializer<myDB>(new DropCreateDatabaseIfModelChanges<myDB>());
Try putting this in your Global.asax.cs Application_Start() method.
Database.SetInitializer<DatabaseContext>(null);
To reset the database from scratch on app run make a class like this
//Where myDB is your context
public class EntityBase: DropCreateDatabaseAlways<myDB>
{
}
Then in your Application_Start() method you can do this
Database.SetInitializer(new EntityBase());

Entity Framework Code First in class library

I just started trying my hands on EF4 code first this morning and I created my POCO, data context and Initializer classes in a separate class library, I believe it's the regular boiler plate type code. I reference the class in an MVC3 application and set the initializer in the Global.asax. On running the app, I notice the following problems
1. No database is created anywhere (Then I add an entry in the web.config for a connection string named after the Context class, still no result)
2. When I try to access the initalized values, I get a null error, obviously because there is no data.
Can anyone please help me with pointers on how to get thi thing to work (would be a shame if I spent my entire christmas day learning this and I still can't get it to work :( )
Thanks
p.s. I tried inserting break points and I hit the app initialization method, but it never hits the Seed method in the initializer even though I add a break point there as well!!
Thanks.
Initializer class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using F2AController.Models;
namespace F2AController.DataObjects
{
public class F2AInitializer : DropCreateDatabaseAlways<F2AContext>
{
protected override void Seed(F2AContext context)
{
var countries = new List<Country>
{
new Country(){ CountryName="Germany", Active = true},
new Country(){ CountryName="Britain", Active = true}
};
countries.ForEach(s => context.Countries.Add(s));
context.SaveChanges();
var providers = new List<Providers>()
{
new Providers(){ ProviderName="InfoBip", ContactDetails="Rturo Manovic", Active=true, MessageRates= new List<ProviderRates>(){new ProviderRates(){ CountryId=1, DateCreated=DateTime.Now, DateModified=DateTime.Now, Rate=0.05M, Active=true}}}
};
providers.ForEach(p => context.Providers.Add(p));
context.SaveChanges();
var usermobiles = new List<MobileTerminal>()
{
new MobileTerminal(){ Active= true, Credits=200, DateCreated=DateTime.Now, MSISDN="4477565444865"}
};
usermobiles.ForEach(u => context.MobileTerminals.Add(u));
context.SaveChanges();
}
}
}
Context Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
namespace F2AController.Models
{
public class F2AContext : DbContext
{
public DbSet<Country> Countries;
public DbSet<MobileTerminal> MobileTerminals;
public DbSet<Providers> Providers;
public DbSet<ProviderRates> ProviderRates;
public DbSet<Property> Properties;
public DbSet<ShortMessage> ShortMessages;
public DbSet<UserProperties> UserProperties;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
}
Global.asax App initialization method
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
Database.DefaultConnectionFactory = new SqlConnectionFactory(ConfigurationManager.ConnectionStrings["F2AContext"].ConnectionString);
Database.SetInitializer<F2AContext>(new F2AInitializer());
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Eureka..finally!
While searching for a solution, I came across this post Entity Framework Database.SetInitializer simply not working
Applying the solution suggested there to force my database to create worked at start up like I expected, but then while running the seed code, it threw a null pointer exception. While investigating, I realized that any attempt to reference the DBSet collections from the Context class yielded the same exception. Inspecting further I realised that instead of using
public DbSet<MobileTerminal> MobileTerminals { get; set; }
I had used
public DbSet<MobileTerminal> MobileTerminals;
Which meant that I did not get any implicit object initialization, hence the null pointer exception. I removed the forced initialization code and ran the app again, this time the seed code didn't run until I accessed a page which actually queried the data context and it ran perfectly.
Apparently, due to Lazy loading, the initialization code is not run until it is actually needed, i.e. the first time the data context is queried in the application.
I hope this helps anyone who has the same problem in the future.
I wanted to share another issue when using a class library for code first & stumbled across this post. I had my code first POCO and DataContext classes in a library project too and wanted to use this project to create my code first database. I figured out that there is a -ProjectName flag with which one can specify the class library project to look for when creating the database.
add-migration -Name 'InitialCreate' -ProjectName 'MyProject.Data'
update-database -ProjectName 'MyProject.Data'
The issue might be with the connectionstring you are using in your web.config.
For SQL CE use following
<add name="YourContext"
connectionString="Data Source=|DataDirectory|yourDB.sdf"
providerName="System.Data.SqlServerCe.4.0"/>
For SQL Express use following
<add name="YourContext"
connectionString="Data source=.\SQLEXPRESS;Integrated Security=True;Initial Catalog=YourDatabase;"
providerName="System.Data.SqlClient" />
I guess this should make things work.
Also, I think you should look at this article EF Code First DB Initialization Using Web.Config. It's better to initialize the database from web.config rather than from global.asax file

Categories