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.
Related
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.
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.
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>();
}
}
We have a ef project using an existing legacy database, but adding new tables to it using ef-migrations. For these entities, we create tables using a new schema, to separate them from the legacy tables. We use the convention with plural form of the class name on the db tables.
However, when we add a new class to be mapped to a legacy table (without a plural table name), ef seems to ignore the mapping.
The entity class:
public class Aktor:IVersionedEntityWithId
{
public int Id { get; set; }
public string Navn { get; set; }
public byte[] Version { get; set; }
}
The mapping code:
protected virtual void MapAktor(EntityTypeConfiguration<Tilsyn.Domain.Aktor> config){
config.ToTable("dbo.Aktor");
config.Property(v=>v.Version).IsConcurrencyToken().IsRowVersion();
config.HasKey(e=>e.Id);
}
The exception:
System.Data.EntityCommandExecutionException: An error occurred while
executing the command definition. See the inner exception for details.
---> System.Data.SqlClient.SqlException: Invalid object name 'dbo.Aktors'.
It seems like the sql generated still ad an s to the class name to get the table name. What is missing in this picture? Am I using the ToTable method wrong?
Update: When changing the class name to something other than the table name, it seems to work. When changing the name back again, the problem has vansihed. Is there a EF cache or hidden mapping file somwehere?
Try overriding OnModelCreating() method in your DBContext subclass to create your mappings.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Tilsyn.Domain.Aktor>().ToTable("dbo.Aktor");
}
I have some urgent issue which I could not find answer for across the web.
I am using CodeFirst EF 4.3.1 and I am getting an error:
Violation of PRIMARY KEY constraint 'PK_T_CRProviders'. Cannot insert duplicate key in object 'dbo.T_CRProviders'.
My code is:
Models:
public enum CRProviderEnums
{
PE_Abcd = 0,
PE_Efgh
}
[Table("T_CRProviders")]
public class CRProvider
{
[Key]
[Required]
public int Enum { get; set; }
[Required]
public string Name { get; set; }
}
[Table("T_CRSupportedResources")]
public class CRSupportedResource
{
[Key]
public Guid SupportedResourceId { get; set; }
[Required]
public CRProvider Provider { get; set; }
}
DbContext:
public class RSContext : DbContext
{
public DbSet<CRProvider> CRProviders { get; set; }
public DbSet<CRSupportedResource> CRSupportedResources { get; set; }
}
Table T_CRProviders looks like this: Enum (PK), Name
Table T_CRSupportedResources looks like this: SupportedResourceId (PK), Provider_Enum (FK).
In the database table T_CRProviders I already have a provider with the following values:
Enum: 0 (which is PE_Abcd)
Name: "PE_Abcd"
Now my main() calls a method AddSupportedResource. This method adds to table T_CRSupportedResources a new CRSupportedResource which refers to provider 0 (PE_Abcd). The method looks like this:
public void AddSupportedResource()
{
CRSupportedResource supportedResource = new CRSupportedResource()
{
SupportedResourceId = Guid.NewGuid(),
Provider = new CRProvider()
{
Enum = (int)CRProviderEnums.PE_Abcd,
Name = "PE_Abcd"
}
};
using (RSContext myContext = new RSContext())
{
myContext.CRSupportedResources.Add(supportedResource);
myContext.SaveChanges();
}
}
I expect that this method will leave table T_CRProviders untouched, and add a new row to table T_CRSupportedResources which will look like this:
SupportedResourceId: DE532083-68CF-484A-8D2B-606BC238AB61
Provider_Enum (FK): 0 (which is PE_Abcd).
Instead, upon SaveChanges, Entity framework also tries to add Provider to the T_CRProviders table, and since such a provider already exists it throws the following exception:
An error occurred while updating the entries.
Violation of PRIMARY KEY constraint 'PK_T_CRProviders'. Cannot insert duplicate key in object 'dbo.T_CRProviders'.
The statement has been terminated.
My question:
How can I instruct the EF not to update table T_CRProviders upon updating table T_CRSupportedResources?
Btw, in the SQL Server I see that table T_CRSupportedResources has a foreign key named FK_RW_TCRSupportedCloudResources_RW_TCRCloudProviders_Provider_Enum and its Update Rule has the value of No Action.
I expect that this method will leave table T_CRProviders untouched,
and add a new row to table T_CRSupportedResources
No it will not happen. You are creating detached entity graph consisting of existing entity a and new entity. EF doesn't know about the existence of your entity until you inform it about it - there are no DB queries validating existence performed by EF on behind.
If you call Add method all entities in your entity graph are added as new. If you don't want to insert all of them you can start with using Attach and manually change state for new ones. For example like:
myContext.CRSupportedResources.Attach(supportedResource);
myContext.Entry(supportedResource).State = EntityState.Added;
Actually, there is a way to do this.
See the answer to my question in the following link:
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/62f3e5bc-c972-4622-b830-e7d7fe710101