Code First migrations with multiple DbContexts fail when setting default schema - entity-framework-6

I have two projects each with their own DbContext class that write to the same SQL Server 2012 database.
The DbContext classes are of the form:
public class BlogDbContext : DbContext
{
public BlogDbContext()
: base("CodeFirstTestConnString")
{
}
public BlogDbContext(string connectionString)
: base(connectionString)
{
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//modelBuilder.HasDefaultSchema("blogtest");
}
}
Using Code First Migrations I can create my tables in the (existing) database by executing the following in each project:
Enable-Migrations
Add-Migration Initial
Update-Database
This works fine, however if I add in the line commented out above to set the default schema (both projects use the same schema), Update-Database fails in the second project with the error "There is already an object named '__MigrationHistory' in the database.".
By running "Update-Database -Verbose" I can see that with the default schema changed, "CREATE TABLE [blogtest].[__MigrationHistory]" is executed for the second project; if I don't change the default schema it only tries to create this table the first time.
Is this a bug in Entity Framework, or am I missing something?

Turns out that this is reported in Codeplex as a bug in EF6.
https://entityframework.codeplex.com/workitem/1685
The workaround shown there is to use a custom HistoryContext to set the default schema:
internal sealed class Configuration : DbMigrationsConfiguration<ConsoleApplication11.MyContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
SetHistoryContextFactory("System.Data.SqlClient", (c, s) => new MyHistoryContext(c, s));
}
}
internal class MyHistoryContext : HistoryContext
{
public MyHistoryContext(DbConnection existingConnection, string defaultSchema) : base(existingConnection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("foo");
}
}

Related

.NET Core Entity Framework throws Invalid object name when accessed as entity, but works with SQL

I'm working with Entity Framework Core, v6.2. I'm getting an error
SqlException: Invalid object name 'Cdef.CellDefinition'
when I try to access the DbSet directly, but using the same DbContext object, I can query the object directly using the FromSql command.
I've seen other answers saying to modify the conventions to remove PluralizingTableNameConvention, however since I'm doing a EntityFrameworkCore.DbContext that ModelBuilder does not have that option, and I don't see any evidence it is try to access a Pluralized name.
My entity is setup like:
[Table("Cdef.CellDefinition")]
public partial class CellDefinition
{
[Key]
public int Id { get; set; }
}
And my DbContext is like:
public class CDefContext : Microsoft.EntityFrameworkCore.DbContext
{
public virtual Microsoft.EntityFrameworkCore.DbSet<CellDefinition> CellDefinition { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
When I try to access the object as an entity directly, I get an error:
Invalid Object Name
but if I issue the SQL with the same object name it works properly.
// This fails with Invalid Object Name
return cDefContext.CellDefinition.ToList();
// This succeeds
return cDefContext.CellDefinition.FromSql("select * from CDef.CellDefinition").ToList()
I found the solution. You can't put the schema in the table name.
//This Does NOT work
[Table("Cdef.CellDefinition")]
public partial class CellDefinition{}
//But this DOES work
[Table("CellDefinition",Schema = "Cdef")]
public partial class CellDefinition{}

EF Core empty migration issue

I am using .Net Core 2.1 and EF Core 2.1. I successfully created 2 tables with EF Code First Migrations and dotnet CLI also seeded data into it. However when I try to add a new model Feature, and run the migration, the file generated has empty Up() and Down() methods and there is no entry in the EF_MigrationsHistory table of Database and the modelSnapshot.cs file also doesn't contain any references to the Feature model. I cross checked the ApplicationDbContext to see if I accidentally missed the declaration of the model in the class but I hadn't. I am not sure of the issue. Can anyone help me out with this? Posting the codes from my project:
Feature.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ProjectName.Models
{
public class Feature
{
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
}
}
ApplicationDbContext.cs
using Microsoft.EntityFrameworkCore;
using ProjectName.Models;
namespace ProjectName.Persitance{
public class ApplicationDbContext: DbContext{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> context)
: base(context){ }
public DbSet<Make> Makes { get; set; }
public DbSet<Feature> Features { get; set; }
}
}
20180906063933_AddFeature.cs(Migration File):
using Microsoft.EntityFrameworkCore.Migrations;
namespace ProjectName.Migrations
{
public partial class AddFeature : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
ApplicationDbContextModelSnapshot.cs:
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Application.Persitance;
namespace Application.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.2-rtm-30932")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Application.Models.Make", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(255);
b.HasKey("Id");
b.ToTable("Makes");
});
modelBuilder.Entity("Application.Models.Model", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("MakeId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(255);
b.HasKey("Id");
b.HasIndex("MakeId");
b.ToTable("Models");
});
modelBuilder.Entity("Application.Models.Model", b =>
{
b.HasOne("Application.Models.Make", "Make")
.WithMany("Models")
.HasForeignKey("MakeId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}
__EFMigrationsHistory DB Image:
Usually this happens when you include context.Database.EnsureCreated(); in your seed data file. This method doesn't allow to create a migrations table and cannot be used with migrations. You need to remove migrations with Remove-Migration command or delete the Migration folder and the database in your server and create new migration. Check Microsoft docs for better understanding.
I was having a similar issue. The Up method was empty despite cleaning the migrations folder and running dotnet ef migrations add Initial multiple times.
The solution in my case was to change the DbSet properties and add virtual to each of them.
Instead of
public DbSet<Make> Makes { get; set; }
try
public virtual DbSet<Make> Makes { get; set; }

EF Core + MVC 6 + .NET Core RC2 - EF not returning results

Entity Framework Core is not returning any results. I've been searching near and far. I find some tutorials saying one thing and others saying another. Here is what I have so far:
Buyer.cs
[Table("DealerCustomer", Schema = "dbo")]
public class Buyer
{
[Key]
public int DealerCustomerKey { get; set; }
public int DealerId { get; set; }
}
BuyerContext.cs
public class BuyerContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer("db connection string here");
}
public DbSet<Buyer> Buyers { get; set; }
}
Startup.cs > ConfigureServices function
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddDbContext<BuyerContext>(options =>
options.UseSqlServer("db connection string here");
services.AddMvc();
}
Now I am trying to load the Buyers data from my BuyerController.cs:
[Route("api/[controller]")]
public class BuyersController : Controller
{
private BuyerContext _context;
public BuyersController(BuyerContext context)
{
_context = context;
}
[HttpGet]
public IEnumerable<Buyer> Get()
{
System.Diagnostics.Debug.WriteLine("getting buyers");
System.Diagnostics.Debug.WriteLine(_context.Buyers);
return _context.Buyers;
}
}
This is all returning empty brackets when I load the page, instead of a list of Buyers. However there are over 1000 rows in that table (dbo.DealerCustomer). I know I have two places adding the db connection string but tutorials kept show both ways of doing it and when I only did it in startup.cs I was getting errors about the _context. I can make everything look pretty later, right now I just want a good connection so I have a starting place to work from.
I found there was a timeout because one of the decimal fields was returning null.
EF Core Timing out on null response

EF6 OnModelCreating() event doesnt

I am following along to this tutorial on EF6 and Codefirst. http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application
I started a solution and just added the Models, the contexts and the initializer
namespace TestContoso.DAL
{
public class SchoolContext : DbContext
{
public SchoolContext()
: base("Name=SchoolContextDB")
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
and created a dummy Home Controller and Index view.
namespace TestContoso.Controllers
{
public class HomeController : Controller
{
private SchoolContext db = new SchoolContext();
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
}
I have also set the Connection String and set the initializer in web.config
<entityFramework>
<contexts>
<context type="TestContoso.DAL.SchoolContext, TestContoso">
<databaseInitializer type="TestContoso.DAL.SchoolInit, TestContoso"/>
</context>
</contexts>
When I run the solution, my expectation is that the database would get created, however I notice that the OnModelCreating event never fires so the db is never created. Why isn't the db getting created? How would I cause the Code First migration to fire?
The article gives you an alternative. Try the alternative method of adding the initialization strategy via code to see if that works.
As an alternative to setting the initializer in the Web.config file is to do it in code by adding a Database.SetInitializer statement to the Application_Start method in in the Global.asax.cs file.
Database.SetInitializer(new CreateDatabaseIfNotExists<SchoolContext>());
And as specified in the comments. Run a query against the data or follow the section on 'Set up EF to initialize the database with test data' to automatically seed the database.
The OnModelCreating event only seemed to get fired when there was some query querying that model, to test that I just added a dummy query in the Index controller and it worked.
public class HomeController : Controller
{
private SchoolContext db = new SchoolContext();
//
// GET: /Home/
public ActionResult Index()
{
var students = from s in db.Students
select s;
return View();
}
}
}

Asp.net Seeding db Entity Framework is not working

I want to seed the database with default data but I get an exception thrown.
I added this initialize class in the DAL namespace
public class MyModelInitialise : DropCreateDatabaseIfModelChanges<MyModelContext>
{
protected override void Seed(MyModelContext context)
{
base.Seed(context);
var MyMarks = new List<MyMark>
{
new Mark{ Name="Mark1", Value="250"},
new Mark{ Name="Mark2", Value="350"},
new Mark{ Name="Mark3", Value="450"}
};
Marks.ForEach(bm => context.Marks.Add(bm));
context.SaveChanges();
}
}
I added this initialiser to app startup
protected void Application_Start()
{
Database.SetInitializer<MyModelContext>(new MyModelInitialise());
}
I get the following error while invoking this
Model compatibility cannot be checked because the DbContext instance was not created
using Code First patterns. DbContext instances created from an ObjectContext or using
an EDMX file cannot be checked for compatibility.
Use the IDatabaseInitializer<TContext> interface instead since DropCreateDatabaseIfModelChanges only works with Code-First models:
public class MyModelInitialise : IDatabaseInitializer<MyModelContext>
{
public void InitializeDatabase(MyModelContext context)
{
// Runs everytime so just avoid if the table already has records.
if (context.Marks.Count() == 0)
{
var MyMarks = new List<MyMark>
{
new Mark{ Name="Mark1", Value="250"},
new Mark{ Name="Mark2", Value="350"},
new Mark{ Name="Mark3", Value="450"}
};
Marks.ForEach(bm => context.Marks.Add(bm));
context.SaveChanges();
}
}
}
Leave the Database.SetInitializer exactly the same. You probably should wrap it in a pre-processor directive to ensure it never ends up in production code.
#if DEBUG
Database.SetInitializer<MyModelContext>(new MyModelInitialise());
#endif
Make sure you have public DbSet<MyMark> MyMarks { get; set; } in the MyModelContext class.

Resources