Mukil Deepthi
Mukil Deepthi

Reputation: 6482

Entity Framework Core 2.0 migrations update database with seed data throwing error

I am using EF Core 2.0 code first migration. I am trying to run the migration sin to the new database. I am using seed data. I am getting error if i use seed data and no error if i comment out the seed data method. Not sure what is the problem. Can anyone please help?

Error:

System.Data.SqlClient.SqlException (0x80131904): There is already an object named 'WorkflowBranches' in the database.

The tables are created, but no records appear in the migrationhistory table, even though I have around 5 migration scripts. Also the seed data is inserted.

I think the database is getting created twice. I am using migrations script and also in the seed method, there is dbContext.Database.EnsureCreatedAsync(). This could be the reason but i am not sure.

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    //Other code
    services.AddTransient<DbSeeder>();
}

public void Configure(IApplicationBuilder app, 
                      IHostingEnvironment env,
                      DbSeeder dbSeeder)
{
    app.UseMvc();

    dbSeeder.SeedAsync(app.ApplicationServices).Wait();
}

DbSeeder.cs:

using Workflow.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Workflow.DBContext
{
    public class DbSeeder
    {
        readonly ILogger _Logger;

        public DbSeeder(ILoggerFactory loggerFactory)
        {
            _Logger = loggerFactory.CreateLogger("DbSeederLogger");
        }

        public async Task SeedAsync(IServiceProvider serviceProvider)
        {
            //Based on EF team's example at https://github.com/aspnet/MusicStore/blob/dev/samples/MusicStore/Models/SampleData.cs
            using (var serviceScope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var dbContext = serviceScope.ServiceProvider.GetService<WorkflowDBContext>();
                if (await dbContext.Database.EnsureCreatedAsync())
                {
                    if (!await dbContext.Alert.AnyAsync())
                    {
                        await InsertAlertsSampleData(dbContext);
                    }
                }
            }
        }

        public async Task InsertAlertsSampleData(WorkflowDBContext dbContext)
        {
            //Insert Risktypes
            var riskTypes = GetRiskTypes();
            dbContext.RiskType.AddRange(riskTypes);

            try
            {
                int numAffected = await dbContext.SaveChangesAsync();
                _Logger.LogInformation($@"Saved {numAffected} riskTypes");
            }
            catch (Exception exp)
            {
                _Logger.LogError($"Error in {nameof(DbSeeder)}: " + exp.Message);
                throw;
            }

            //Insert categories
            var categories = GetCategories(riskTypes);
            dbContext.Category.AddRange(categories);

            try
            {
                int numAffected = await dbContext.SaveChangesAsync();
                _Logger.LogInformation($"Saved {numAffected} categories");
            }
            catch (Exception exp)
            {
                _Logger.LogError($"Error in {nameof(DbSeeder)}: " + exp.Message);
                throw;
            }

            //var alerts = GetAlerts(categories);
        }

        private List<Alert> GetAlerts(List<Category> categories)
        {
            return new List<Alert>();
        }

        private List<RiskType> GetRiskTypes()
        {
            var riskTypes = new List<RiskType>
            {
                new RiskType { Name = "Risk1"},
                new RiskType { Name = "Risk2"}
            };

            return riskTypes;
        }

        private List<Category> GetCategories(List<RiskType> riskTypes)
        {
            var categoryList = new List<Category>();

            var categories = new string[]
            {
                "Category1Risk1",
                "Category2Risk1",
                "Category3Risk1",
                "Category1Risk2"
            };

            //Low Risk
            foreach (var category in categories.Take(3))
            {
                categoryList.Add(new Category
                {
                    Name = category,
                    RiskType = riskTypes.FirstOrDefault(rt=>rt.Name=='Risk1')
                });
            }

            //High Risk
            foreach (var category in categories.Skip(3))
            {
                categoryList.Add(new Category
                {
                    Name = category,
                    RiskType = riskTypes.FirstOrDefault(rt=>rt.Name=='Risk2')
                });
            }

            return categoryList;
        }
    }
}

Thanks

Upvotes: 0

Views: 1827

Answers (1)

German.Kuber
German.Kuber

Reputation: 59

If you are in the Ef Core 2.0 you need to kwno this:

In 2.0 projects using EF Core 2.0, Program.BuildWebHost is invoked to obtain the application services. Unlike 1.x, this has the additional side effect of invoking Startup.Configure. If your 1.x app invoked database initialization code in its Configure method, unexpected problems can occur. For example, if the database doesn't yet exist, the seeding code runs before the EF Core Migrations command execution. This problem causes a dotnet ef migrations list command to fail if the database doesn't yet exist. Consider the following 1.x seed initialization code in the Configure method of Startup.cs:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

SeedData.Initialize(app.ApplicationServices);

In 2.0 projects, move the SeedData.Initialize call to the Main method of Program.cs:+

var host = BuildWebHost(args);

using (var scope = host.Services.CreateScope())
{
    var services = scope.ServiceProvider;

    try
    {
        // Requires using RazorPagesMovie.Models;
        SeedData.Initialize(services);
    }
    catch (Exception ex)
    {
        var logger = services.GetRequiredService<ILogger<Program>>();
        logger.LogError(ex, "An error occurred seeding the DB.");
    }
}

host.Run();

As of 2.0, it's bad practice to do anything in BuildWebHost except build and configure the web host. Anything that is about running the application should be handled outside of BuildWebHost — typically in the Main method of Program.cs.

Today i changed my code for this code, and mi solution worked properly.

I am leaving you the reference link:

https://learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#move-database-initialization-code

Upvotes: 0

Related Questions