Yoda
Yoda

Reputation: 18068

How avoid adding duplicates to database managed by EntityFramework caused by Seed method?

Every time I run the application same objects are added to the database(duplicates).

My Global.asax:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using WebApplication2.Migrations;
using WebApplication2.Models;


namespace WebApplication2 {
    public class MvcApplication : System.Web.HttpApplication {
        protected void Application_Start() {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>()); 
            //Database.SetInitializer(new DropCreateDatabaseAlways<ApplicationDbContext>());
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}

and My Configuration.cs with Seed method:

namespace WebApplication2.Migrations
{
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Data.Entity.Migrations;
    using System.Linq;
    using WebApplication2.Models;

    internal sealed class Configuration : DbMigrationsConfiguration<WebApplication2.Models.ApplicationDbContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            ContextKey = "WebApplication2.Models.ApplicationDbContext";
        }

        protected override void Seed(WebApplication2.Models.ApplicationDbContext context) {
            var persons = new List<Person> { 
         new Person{FirstName = "John", LastName = "Doe", CellNumber = "123-456-789", SecondaryPhoneNumber = "98873213", Address = "1street 2",BirthDate = DateTime.Now.Date, Pesel = "312312312", Notes = "Annoying"},
         new Person{FirstName = "Anna", LastName = "Doe", CellNumber = "113-456-789", SecondaryPhoneNumber = "98873213", Address = "1street 2",BirthDate = DateTime.Now.Date, Pesel = "548555672", Notes = "Less Annoying"}
        };

        persons.ForEach(person => context.Persons.AddOrUpdate(person));
        context.SaveChanges();

        var meetings = new List<Meeting>{
            new Meeting{PersonId = 1, Body = "Body of meeting", Date = DateTime.Now}
        };

        meetings.ForEach(meeting => context.Meetings.AddOrUpdate(meeting));
        context.SaveChanges();

        var statuses = new List<Status> {
            new Status{Name = "OK"},
            new Status {Name = "NOT_OK"}
        };

        statuses.ForEach(status => context.Statuses.AddOrUpdate(status));
        context.SaveChanges();

        }
    }
}

Every time I run the app Seed adds duplicate records:

enter image description here

I needed to comment contents of Seed method to prevent adding duplicates.

Question: (1) What should I change so Seed method will be run only to recreate database after migration?

EDIT:

In the Seed method there is comment:

  //  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" }
            //    );
            //

but my method is called ALWAYS, not only after migrations. Why is it so?

Upvotes: 5

Views: 13936

Answers (6)

Masoud Darvishian
Masoud Darvishian

Reputation: 3964

First, reset your primary key to be sure that there would be no duplicate keys

// reset identity autoincrement to 0
context.Database.ExecuteSqlCommand("DBCC CHECKIDENT('tableName', RESEED, 0)");

Then use AddOrUpdate method to seed data

context.People.AddOrUpdate(new Person
{
    Id = 1,
    Name = "John Doe"
});

Upvotes: -1

This worked for me

  1. Delete all the rows in the table
  2. Reset the incremental identity to 0 if it is activated (the primary keys specified in the seed() must match those in the database table so that they do not duplicate)
  3. Specify the primary keys in the 'seed' method
  4. Run the seed () method several times and you check if they duplicated

Upvotes: 0

RAJITHA DINESH
RAJITHA DINESH

Reputation: 9

var paidOutType = new List<PaidOutType>
                {
                    new PaidOutType { PaidOutTypeID = 1, Code = "001", Description = "PAID OUT 1", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 },
                    new PaidOutType { PaidOutTypeID = 2, Code = "002", Description = "PAID OUT 2", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 },
                    new PaidOutType { PaidOutTypeID = 3, Code = "002", Description = "PAID OUT 3", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 },
                };
                paidOutType.ForEach(u => smartPOSContext.PaidOutType.AddOrUpdate(u));
                smartPOSContext.SaveChanges();

Upvotes: 0

anIBMer
anIBMer

Reputation: 1209

Question: (1) What should I change so Seed method will be run only to recreate database after migration?

If you only need to seed data when your database is created. In this case, you can create a Database Initialiser from CreateDatabaseIfNotExist Initialiser. Then in the DatabaseInitialiser class, you can override the Seed Method with your data there, instead of the MigrationConfiguration class. Further information can be found in attached link.

Database Initialization Strategies in Code-First:

but my method is called ALWAYS, not only after migrations. Why is it so?

In migration configuration. the seed method will be called every time the database migration happens. That is why your seed method is called all the time.

Upvotes: 1

Anthony Chu
Anthony Chu

Reputation: 37520

You have full access to the context in the Seed method, so you can query to see if data already exists.

For example, you can seed the tables only if they're empty...

protected override void Seed(WebApplication2.Models.ApplicationDbContext context) {

    if (!context.Persons.Any())
    {   
        var persons = new List<Person> { 
            new Person{FirstName = "John", LastName = "Doe", CellNumber = "123-456-789", SecondaryPhoneNumber = "98873213", Address = "1street 2",BirthDate = DateTime.Now.Date, Pesel = "312312312", Notes = "Annoying"},
            new Person{FirstName = "Anna", LastName = "Doe", CellNumber = "113-456-789", SecondaryPhoneNumber = "98873213", Address = "1street 2",BirthDate = DateTime.Now.Date, Pesel = "548555672", Notes = "Less Annoying"}
        };

        persons.ForEach(person => context.Persons.Add(person));
        context.SaveChanges();
    }

    if (!context.Meetings.Any())
    {
        var meetings = new List<Meeting>{
            new Meeting{PersonId = 1, Body = "Body of meeting", Date = DateTime.Now}
        };

        meetings.ForEach(meeting => context.Meetings.Add(meeting));
        context.SaveChanges();
    }

    if (!context.Statuses.Any())
    {
        var statuses = new List<Status> {
            new Status{Name = "OK"},
            new Status {Name = "NOT_OK"}
        };

        statuses.ForEach(status => context.Statuses.Add(status));
        context.SaveChanges();
    }

}

You can also use AddOrUpdate, but you need to tell EF how to check if the record exists using the first parameter...

protected override void Seed(WebApplication2.Models.ApplicationDbContext context) {

    var persons = new List<Person> { 
        new Person{FirstName = "John", LastName = "Doe", CellNumber = "123-456-789", SecondaryPhoneNumber = "98873213", Address = "1street 2",BirthDate = DateTime.Now.Date, Pesel = "312312312", Notes = "Annoying"},
        new Person{FirstName = "Anna", LastName = "Doe", CellNumber = "113-456-789", SecondaryPhoneNumber = "98873213", Address = "1street 2",BirthDate = DateTime.Now.Date, Pesel = "548555672", Notes = "Less Annoying"}
    };

    persons.ForEach(person => context.Persons.AddOrUpdate(p => new { p.FirstName, p.LastName }, person));
    context.SaveChanges();

    var meetings = new List<Meeting>{
        new Meeting{PersonId = 1, Body = "Body of meeting", Date = DateTime.Now}
    };

    meetings.ForEach(meeting => context.Meetings.AddOrUpdate(m => m.Body, meeting));
    context.SaveChanges();

    var statuses = new List<Status> {
        new Status{Name = "OK"},
        new Status {Name = "NOT_OK"}
    };

    statuses.ForEach(status => context.Statuses.AddOrUpdate(s => s.Name, status));
    context.SaveChanges();

}

Upvotes: 8

Joshua Shearer
Joshua Shearer

Reputation: 1120

From this page (about halfway down), which was sourced from this answer

Note: Adding code to the Seed method is one of many ways that you can insert fixed data into the database. An alternative is to add code to the Up and Down methods of each migration class. The Up and Down methods contain code that implements database changes. You'll see examples of them in the Deploying a Database Update tutorial.

You can also write code that executes SQL statements by using the Sql method. For example, if you were adding a Budget column to the Department table and wanted to initialize all department budgets to $1,000.00 as part of a migration, you could add the folllowing line of code to the Up method for that migration:

Sql("UPDATE Department SET Budget = 1000");

You might also look into using the AddOrUpdate method, as referenced in this answer, which should also work for your purposes.

I quickly changed the code I obtained from the answer linked above, so bear with me if there's an issue with the code below. The concept should still be relatively clear, I believe.

context.People.AddOrUpdate(c => c.PK, new Person() { PK = 0, FirstName = "John", ... })
context.People.AddOrUpdate(c => c.PK, new Person() { PK = 1, FirstName = "Anna", ... })

Upvotes: 9

Related Questions