Undesired Migration for Object Mapped to a View - entity-framework

The Problem
We have an object
public class Foo
{
[Key, Column(Order = 0)]public virtual int A { get; set; }
[Key, Column(Order = 1)]public virtual int B { get; set; }
}
that needs to be mapped to an indexed view in SQL Server. Building on the approaches
EF Code First : Mapping nontable objects with Fluent API and https://stackoverflow.com/a/20887064/141172
we first created an initial migration
public override void Up()
{
CreateTable("dbo.Foos",
c => new { A = c.Int(nullable:false), B = c.Int(nullable:false) })
.PrimaryKey(t => new { t.A, t.B });
}
Then an empty migration into which we added SQL to drop the auto-generated table, and then add the index
public override void Up()
{
Sql(#"DROP TABLE Foos");
Sql(#"CREATE VIEW dbo.Foos As....");
}
finally in our DbContext, Foo is mapped to the view:
modelBuilder.Entity<Foo>().ToTable("Foos");
This worked just fine, until we added another property to Foo:
[Key, Column(Order = 2)]public int C { get; set; }
We added a new migration to redefine the view
public override void Up()
{
Sql(#"ALTER VIEW Foos ....");
}
The Alter View migration is correctly applied, but EF believes that it must create a migration to account for the new property.
Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration.
When I run
Add-Migration WhatIsPending
EF generates
public override void Up()
{
DropPrimaryKey("dbo.Foos");
AddColumn("dbo.Foos", "C", c => c.Int(nullable: false));
AddPrimaryKey("dbo.Foos", new[] {"A", "B", "C" });
}
Question
Is there a better approach to mapping an object to a view, such that changes to the object are painless?
If this is the best approach, how can I inform EF Migrations that it does not need to generate the migration?

We couldn't find a way to tell EF to not create a migration, but this approach might help:
When you create the POCO, don't CreateTable() in the migration. if EF wants to create a migration file, fine, but you can comment the code out so only your Sql(#"Create View ..."); runs.
All we had to do then was create the DbSet for the view, not a modelBuilder.Entity entry.
When you need to make changes, start the same way as any other table. Make changes to the POCO, then run Add-Migration and let it create the migration for you. comment out whatever it wants to do, and add your Alter View script in. make sure you make a corresponding one in the Down.
This way you will just have a single migration file for each change.

Related

EF6 code first and VIEW

I would like to add a VIEW to the database, and query the data from this VIEW using L2E. I use migrations for maintaining database schema.
I added one class that should MAP to a VIEW columns. As an example, this class has only two properties
[Table("View_Data")]
public class ViewData
{
[Key]
public int Id { get; set; }
public int PropertyA { get; set; }
}
public class ViewDataMap : EntityTypeConfiguration<ViewData>
{
public ViewDataMap ()
{
this.ToTable("View_Data");
this.HasKey(t => t.Id);
}
}
I added ViewDataMap to OnModelCreating, as with any other Table mappings. I added DbSet ViewDatas.
When I executed
add-migration preview
it created new migration with CreateTable command. Since I do not want to create a table, but only a view, I replaced in UP() CreateTable with Sql("CREATE VIEW...")
Still, EF complains about pending changes in database, and still wants to create new migration with CreateTable()...
How can prevent EF to create new table, but use VIEW instead?
As Steve suggested in the comment, I forgot to do update-database, then all works as expected.

Unable to query new table from asp.net core web application using EF 1.0

I have developed a new asp.net Core web application using Visual Studio 2015. I am at the point where I am adding user customization options by adding additional tables to my local database. However I have been unable to add whatever EF needs to query a new table correctly. I get the following error when attempting to query the table..
Applying existing migrations for ApplicationDbContext may resolve this issue
There are migrations for ApplicationDbContext that have not been applied to the database
•00000000000000_CreateIdentitySchema
Apply Migrations
In Visual Studio, you can use the Package Manager Console to apply pending migrations to the database:
PM> Update-Database
Alternatively, you can apply pending migrations from a command prompt at your project directory:
dotnet ef database update
My table is a simple table with a few varchar or nvarchar columns. The model looks something like...
namespace MyNamespace.ColorSchemes
{
public class ColorSchemesViewModel
{
[Required]
public string Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string bc { get; set; }
}
Table looks something like this in SQL Server...
CREATE TABLE [dbo].[ColorSchemes](
[Id] [nvarchar](50) NOT NULL,
[Name] [varchar](32) NOT NULL,
[bc] [nchar](7) NOT NULL
)
I have added the table to the application context like such...
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<ColorSchemesViewModel> Colors { get; set; }
I have also used as separate class similarly like..
public DbSet<ColorSchemes> Colors { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
I have added the context to a controller like this...
private ApplicationDbContext _context;
public MyController(IMemoryCache memoryCache, ILoggerFactory loggerFactory, ApplicationDbContext context)
{
_memoryCache = memoryCache;
_logger = loggerFactory.CreateLogger<ChordMVCController>();
_context = context;
}
I have tried to query the table in my controller like this...
var colorSchemes = (from c in _context.Colors
select c).ToList();
I have attempted to use the Package Manager to per instructions from the error...
PM> Update-Database
I always get this error...
System.Data.SqlClient.SqlException: There is already an object named 'AspNetRoles' in the database.
This doesn't make sense since this table is already in the database and the EF definition. How do I get my table added properly to the EF migrations so I can query it?
I was able to solve this myself...
I created a different context rather than trying to embed the dbset in the default ApplicationDbContext and also removed the onModelCreating method.
public class ColorSchemeDbContext : DbContext
{
public ColorSchemeDbContext(DbContextOptions<ColorSchemeDbContext> options) : base(options)
{
}
public DbSet<ColorScheme> ColorSchemes { get; set; }
}
Replaced the ApplicationDBContext with the new context in my controller class...
private readonly ColorSchemeDbContext _context;
public MyController(IMemoryCache memoryCache, ILoggerFactory loggerFactory, ColorSchemeDbContext context)
{
_memoryCache = memoryCache;
_logger = loggerFactory.CreateLogger<ChordMVCController>();
_context = context;
}
After that the query worked. I spent a lot of time attempting to use the EF migrations to create the tables from a class syntax. Nothing seemed to work. I was creating a new .NET CORE web application in VS 2015 with the template and using user authentication which creates the AspNetRoles tables in SqlLite once you do an update-database. It is very confusing how to add additional tables using a code first approach after that. A lot more documentation is needed regarding EF migrations with respect to managing projects over time. I see the benefits of having all of your database updates maintained from your VS project but it is not easy to understand.

Database changes are not vsiisble after executing migrate.exe

I am using entity framework 6.1. I used code-based migration i.e. Migrate.exe to update my database (Add a new property to table). Migrate.exe executed fine and I did refresh on my SQL Server database but the newly added column was not visible.
I inserted a row into the table and checked table and found that now newly column is added to the table.
I would like to know why this newly created column not visible immediately after executing migrate.exe and why I have to hit database to see my update model changes?
Please respond.
I have used below database context and initializer.
public class AMLDBContext : DbContext
{
public DbSet<Template> Templates { get; set; }
public DbSet<Property> Properties { get; set; }
public AMLDBContext()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<AMLDBContext, Migrations.Configuration>());
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Property>().HasKey(s => new { s.PropertyName, s.TemplateId });
modelBuilder.Entity<ReturnAttribute>().HasKey(s => new { s.AttributeName, s.TemplateId });
base.OnModelCreating(modelBuilder);
}
}
I used below command for migrate.exe.
migrate.exe AutoDbUpgrade.exe /StartUpDirectory:"C:\TestProject\AutoDbUpgrade\AutoDbUpgrade\bin\Debug" /startupConfigurationFile=”AutoDbUpgrade.exe.config” /verbose

Entity Framework Migrations: Prevent dropping column or table

If EF migration decides to rename a column (or a table), it fulfills this by dropping the (old) column and then adding a column with the new name. This clearly leads to data loss.
Is there any way to prevent EF migration from dropping a column and force it to use RenameColumn instead?
Well, I didn't find a clear and straightforward solution.
My solution is hiding DbMigration class which every migration that was generated using code-based migration is derived from. I do this by introducing a new class with the same name (DbMigration). Then I put it inside the same assembly and same namespace where code files reside. This way, any reference of code files to the original DbMigration resolves to the new DbMigration type. Then, I can switch dropping a column ON or Off:
namespace MyProject.DAL.Migrations
{
/// <summary>
/// Customized DbMigration which protects columns to be dropped accidentally
/// </summary>
public abstract class DbMigration : global::System.Data.Entity.Migrations.DbMigration
{
public bool AlloDropColumn { get; set; }
protected internal new void DropColumn(string table, string name, object anonymousArguments = null)
{
if (!AlloDropColumn)
throw new Exception("MyProject: Dropping a column while updating database is prohibited. If you really want to drop column(s), set property 'AllowDropColumn' true.");
}
}
}
And in a code file:
public partial class _1 : DbMigration
{
public override void Up()
{
AlloDropColumn = true; // To allow column drop
AddColumn("driver.TruckDriver", "FullName2", c => c.String());
DropColumn("driver.TruckDriver", "FullName");
}
public override void Down()
{
AddColumn("driver.TruckDriver", "FullName", c => c.String());
DropColumn("driver.TruckDriver", "FullName2");
}
}

entity framework code first and view mapping

I have a application using code first; in search section I have to gather information from 3 tables and their related tables so I made a view; and since there is no syntax for code first to create view (I think so; please let me know if I'm wrong) I used pure SQL script;
on model creating to prevent EF to create a table with same name as table (VIEW_SEARCH) I did :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<View_Search>();
}
any ways application works fine until you try to get data from the view then BANG...
The model backing the 'SearchContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269)
This error simply says that what you have in your model file is inconsistent with what you have in your database.
To make it consistent go to Package Manager Console and type Enable-Migrations, then Add-Migration yourMigrationName and Update-Database. The error should disappear.
If you want to combine data from 3 tables you can simply create a ViewModel.
Let's say you have 3 models: Book, Author, BookStore and you want to have all information in one view. You create ViewModel
public class MyViewModel
{
public Book myBook {get; set;}
public Author myAuthor {get; set;}
public BookStore myBookStore {get; set;}
}
Then you add at the top of your all-in-one-view
#model myNamespace.MyViewModel
and access items like
Model.Book.title
Model.Author.name
Model.BookStore.isClosed
I'm actually working with Entity Framework "Code First" and views, the way I do it is like this:
1) Create a class
[Table("view_name_on_database")]
public class ViewClassName {
// View columns mapping
public int Id {get; set;}
public string Name {get; set;}
// And a few more...
}
2) Add the class to the context
public class ContextName : DbContext {
// Tables
public DbSet<SomeTableClassHere> ATable { get; set; }
// And other tables...
// Views
public DbSet<ViewClassName> ViewContextName { get; set; }
// This lines help me during "update-database" command
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
// Remove comments before "update-database" command and
// comment this line again after "update-database", otherwise
// you will not be able to query the view from the context.
// Ignore the creation of a table named "view_name_on_database"
modelBuilder.Ignore<ViewClassName>();
}
}