ASP.NET Entity Framework Code First Migrarion - asp.net-mvc

This a ASP.NET MVC with Entity Framework 6.1 Code First application.
I need my application to CREATE the database if it doesn't exists and seed the tables with some data. Also I need to Migrate the database on application start if the model has changed using MigrateDatabaseToLatestVersion.
On the Application_Start i have:
protected void Application_Start()
{
Database.SetInitializer(new MyAppDataInitializer());
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyAppDataContext, MyApp.Model.Migrations.Configuration>());
}
The initializer:
public class MyAppDataInitializer: CreateDatabaseIfNotExists<MyAppDataContext>
{
protected override void Seed(MyAppDataContext context)
{
Code to add some "CREATION DATA"
}
}
Here is the Migration Configuration class:
public class Configuration : DbMigrationsConfiguration<MyApp.Model.DataContext.MyAppDataContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
ContextKey = "MyApp.Model.DataContext.MyAppDataContext";
}
protected override void Seed(MyApp.Model.DataContext.MyAppDataContext context)
{
Code to add some "MIGRATION DATA"
}
}
When my application starts it works as expected except for one thing:
If the database exists, it works perfect, it changes the database according to the new model and it seeds the database with the "MIGRATION DATA".
If the database doesn't exists it creates the database but it only seeds the database with the "MIGRATION DATA" but not with the "CREATION DATA". The SEED method on the inicializer is not called.
I'm sure there is something I'm doing wrong here, but I can't figure out how can I create the database when it doesn't exists and seed some "init" data that it only insert for the first time.
Then when changing the model it only changes the database and add the data I setup in the migration seed method in case I need it.
Could you help me with this?
Thanks in advance.

You're overwriting the initializer. It's a "set" not an "add" initializer and there can only be one..
protected void Application_Start()
{
Database.SetInitializer(new MyAppDataInitializer());
// Now the initializer gets "re-set" again and MyAppDataInitializer is not used at all
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyAppDataContext, MyApp.Model.Migrations.Configuration>());
}

Related

Cannot connect to my edmx with my data context

I've not used VS MVC for a while but I'm writing a project which requires connecting to a Sql database which I've installed as an edmx file SwitchDB.edmx in my DAL folder. In the past I've set up my data context file which I then use to reference the data in my controller, the model help me to order the data in the correct way.
This is how my data context file looks
namespace Switches.DAL
{
public class SwitchContext : DbContext
{
public SwitchContext()
: base("DefaultConnection")
{ }
public DbSet<Switch_List> SwitchList { get; set; }
}
}
I've set up the "DefaultConnection" in my Web.config under connectionStrings and my model Switch_List.cs has the file settings. When I declare the DB context in my controller as below
private SwitchContext db = new SwitchContext();
Then I would expect to reference the SwitchContext to get my data, like this
var switches= db.SwitchList .ToList();
However, when I run the project and reference db in debug I get the following error message 'the function evaluation requires all threads to run'. The DB context SwitchContext is clearly not getting access to the Switch.edmx so what am I forgetting?
I had a similar problem, but you should see the connection properties using an IDE button (to re-evaluate the expression).
However, when you get to the part of db.SwitchList.ToList() does it generate any exceptions?

How do I specify the name of a joiner table in an EF many to many relationship?

I am using EF Code First 6.2 With Dev Express XAF
In DevExpress.Persistent.BaseImpl.EF the Event class has a many to many relationship with the Resource class.
If I make a new project using the Dev Express Wizard with Code First then the joiner table will be called ResourceEvents
However somehow in some migration my joiner table got renamed to be EventResources.
How do I set it back to what it should be?
I tried adding the following to the DBContext
modelBuilder.Entity<Event>().HasMany(x => x.Resources).WithMany(x => x.Events)
.Map(
x =>
{
x.MapLeftKey("Event_ID");
x.MapRightKey("Resource_Key");
x.ToTable("ResourceEvents");
});
this causes a migration to want to create
public partial class ev : DbMigration
{
public override void Up()
{
DropPrimaryKey("dbo.ResourceEvents");
AddPrimaryKey("dbo.ResourceEvents", new[] { "Event_ID", "Resource_Key" });
}
public override void Down()
{
DropPrimaryKey("dbo.ResourceEvents");
AddPrimaryKey("dbo.ResourceEvents", new[] { "Resource_Key", "Event_ID" });
}
}
Running the migration causes an error
Cannot find the object "dbo.ResourceEvents" because it does not exist or you do not have permissions.
The trick was to add code to the migration to rename the table
RenameTable(name: "dbo.EventResources", newName: "ResourceEvents");
Having the fluent API seems important in letting EF know what the name of the table should be.

EF migration Seeding with large dataset

I am using EF Code First Migration with ASP.NET MVC5 project. I want to seed the database with about 5000 records. The examples on the asp.net web site and blogs all have the seed function within the configuration method.
internal sealed class Configuration : DbMigrationsConfiguration<foo.ApplicationDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
MigrationsDirectory = #"Migrations\ApplicationDbContext";
}
protected override void Seed(foo.ApplicationDbContext context)
{
SeedProducts(context);
SeedFoods(context);
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
What are some options to seed large data sets when using EF Migration?
Currently The configuration file is over 5000 lines long and is different to manage.
I would prefer to store them in another database or excel spreadsheet and then import them in using the seed function. I am not sure how to go about importing data from external sources within the Seed method.
I also tried to break up the data set into several files but when I try to call the function
SeedProducts(context);
SeedFoods(context);
outside of the Configuration Class I get a build error: "The name does not exists in the current context". (I am not sure what this means?
You can store the sql file in the base directory and use it. You can create multiple sql files. I used following code to execute all sql files stored on base directory.
protected override void Seed(CRM.Data.DbContexts.CRMContext context)
{
var sqlfiles = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory+"\\initialdata", "*.sql");
sqlfiles.ToList().ForEach(x=> context.Database.ExecuteSqlCommand(File.ReadAllText(x)));
}
Why we need to have a seed data for 5000 records. If you prefer this way it will take lot of manual work also. where as, its not required here.
Instantly you can Create Scripts and execute that into you db. via Code as well as Manually.
Db Scripts can be taken for entire db as well as each table, Store Procedure wise also. So, that you will get records particularly.
Follow the steps from this link OR MSDN
Note: After Creating the Database Script. You can read the file from Seed Function and Execute the query from function itself. Or Manually you can go and execute when ever you need it.
I ended up using a CSV (comma delimited file) and storing it as a domain resource. Then reading the CSV file and adding database records:
I am able to Seed the database using EF Migration Seed method and a CSV file as defined as follows in the Migration.cs file. Note: the CSV file in the project in Visual Studio are set to the Build Action to Embedded Resource.
public void SeedData(WebApp.Models.ApplicationDbContext Context)
{
Assembly assembly = Assembly.GetExecutingAssembly();
string resourceName = "WebApp.SeedData.Name.csv";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
CsvReader csvReader = new CsvReader(reader);
csvReader.Configuration.WillThrowOnMissingField = false;
var records = csvReader.GetRecords<Products>().ToArray();
foreach (Product record in records)
{
Context.Products.AddOrUpdate(record);
}
}
}
Context.SaveChanges();
}

Making DropCreateDatabaseAlways work with custom DbMigrationsConfiguration

Using ef6 code first and mysql I got the following exception:
Specified key was too long; max key length is 767 bytes.
To fix it I created custom HistoryContext and shortened ContextKey length.
And to use MyHistoryContext I created Configuration class inherited from DbMigrationsConfiguration<MyContext>.
After creation of Configuration class DropCreateDatabaseAlways db initializer stopped to work throwing:
The DropCreateDatabaseAlways initializer did not drop or create the database backing context 'MyContext' because Migrations are enabled for the context.
A̶n̶d̶ ̶s̶e̶t̶t̶i̶n̶g̶ ̶A̶u̶t̶o̶m̶a̶t̶i̶c̶M̶i̶g̶r̶a̶t̶i̶o̶n̶s̶E̶n̶a̶b̶l̶e̶d̶ ̶t̶o̶ ̶f̶a̶l̶s̶e̶ ̶d̶i̶d̶n̶t̶ ̶h̶e̶l̶p̶. (I've understood that its unrelated.)
Edit.
I want to reformulate the problem: creating Configuration class inherited from DbMigrationsConfiguration<MyDbContext> (to use custom HistoryContext wich solves Specified key was too long problem in mysql) made ef think that I've enabled migrations via Enable-Migrations command and DropCreateDatabaseAlways stopped to work (but this is intended behaviour as far as I understand). But afterall this is not a big problem because I can create custom initializer with
context.Database.Create();
context.Database.Drop();
So, did I understand it correct?
Custom HistoryContext:
public class MyDbHistoryContext : HistoryContext
{
public MyDbHistoryContext(DbConnection existingConnection, string defaultSchema)
: base(existingConnection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<HistoryRow>()
.Property(x => x.ContextKey)
.HasMaxLength(255);
}
}

Is there an object model for automating migrations?

Using EF migrations I wish to automate running a migration against an environment. I know I can do this via powershell call to Update-Database, but is there a way of doing this via the framework without having to launch an external process?
Yes, in your DbContext:
public static void SetInitializer()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DataContext, Configuration>());
}
Where Configuration is:
internal sealed class Configuration : DbMigrationsConfiguration<DataContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
}
You'll still need to release a Migration file for migrations that will make changes that will result in dataloss (unless you set AutomaticMigrationDataLossAllowed to true in the Configuration class - danger!) but you can make these files using the Add-Migration command in PMC.

Resources