I am using entity framework and Codefirst to create a Stored procedure.
As I reed, we should do some steps in package manager to create SP.
1 - enable migration
2- add-migration MYclass
3- update-database
and in Myclass(new created) I write my SP code in UP() method to create SP. works fine!! BUT when I change SP and run update-database again it does not work and I need to do another add-migration command to create new MYclass2. Is that right? I mean every time I should write add-migration?
this is mycode
public override void Up()
{
Sql(#"Create procedure testSp
as
select std.Id as stdName, std.Name as MajorName, mj.Name from dbo.Students as std
inner join dbo.Majors as mj on std.Id = mj.Id
");
}
public override void Down()
{
Sql("drop procedure testSp");
}
when I run update-database again the result is "No pending explicit migration"
even if I change the SQL query to "Alter procedure...." it does not work , no change happens.
thanks
EDITED
consider this scenario, I want to change my SP name(just an example)
so I need to change the query to "Alter store procedure tespSP2 ..."
or any other change, should I run add-migration again?? or update-database is supposed to do it??
in entity framework migrations added to a _migrationHistory table, you can delete the last row of this table [it's not recommended]
or you can use rollback/undo command
Update-Database -TargetMigration:MigrationsName
then use update-database -force
Related
I am trying to add an sql statement to an up-method of a migration for my current project. The database is an Ms Access database. The migrations get applied during run-time.
The situation is as follows:
I have a base Initial-create migration, which in my case is assumed to be already applied. Due to the nature of this application we have a table A which contains some kind of a foreign key, but without any sql-constraints defined. This means the foreign-key relationship is designed via program code and not in sql means an foreign key relationship. The key is an string and if there is no foreign-element the value is empty.
Now we want to add a new migration which enforces this relationship via sql-constraints. This works just fine via standard ef-core migration code, but the problem comes when the migration gets applied to a non empty database. The sql foreign key would need all the empty strings in table A to be null (otherwise we get an exception)
The seemingly easy solution was to add the following statement in the up-method of the new migration:
UPDATE A SET ForeignKeyColumn = NULL WHERE ForeignKeyColumn & \"\" = \"\""
But this results in the following exception:
System.Data.OleDb.OleDbException (0x80040E14): The database engine could not lock table 'A' because it is already in use by another person or process.
at System.Data.OleDb.OleDbCommand.ExecuteCommandTextErrorHandling(OleDbHResult hr)
at System.Data.OleDb.OleDbCommand.ExecuteCommandTextForSingleResult(tagDBPARAMS dbParams, Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteCommandText(Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteCommand(CommandBehavior behavior, Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteReaderInternal(CommandBehavior behavior, String method)
at System.Data.OleDb.OleDbCommand.ExecuteNonQuery()
at EntityFrameworkCore.Jet.Data.JetCommand.ExecuteNonQueryCore()
at EntityFrameworkCore.Jet.Data.JetCommand.<>c.<ExecuteNonQuery>b__40_0(Int32 _, JetCommand command)
at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable`1 source, TAccumulate seed, Func`3 func)
at EntityFrameworkCore.Jet.Data.JetCommand.ExecuteNonQuery()
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject)
at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IEnumerable`1 migrationCommands, IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade)
at X.Infrastructure.Setup.Migrate(IFactory`1 pDatabaseContextFactory, String pDatabasePath)
at X.COM.XCOMWrapper.Setup(ISettingsProvider pSettingsProvider)
However if we remove this sql statement from the migraition code and execute it as follows, before the call of context.Database.Migrate():
var dbConnection = context.Database.GetDbConnection();
dbConnection.Open();
using (var transaction = dbConnection.BeginTransaction())
{
var updateForeignKeyReferences= dbConnection.CreateCommand();
updateForeignKeyReferences.CommandText = "UPDATE A SET ForeignKeyColumn = NULL WHERE ForeignKeyColumn & \"\" = \"\"";
updateForeignKeyReferences.ExecuteNonQuery();
transaction.Commit();
}
dbConnection.Close();
It works just fine.
Is my approach of using the sql code in the up-method completly wrong? What are possible reasons for this? And most important, how can I fix this? The second approach is my current work-around for this problem but I fear that this means in the long run I can not use the migrations mechanism and have to go for a custom solution (or another framework). I would prefer to just stick with ef core.
Important:
This application works with an legacy application and we have to insert the application history via sql code on the inital startup. For this we create an transaction and simple create the history table and insert the initially created table. This works just fine, and the transactions as well as the commands should all be closed. The table A is never touched by this functions.
Using migrationBuilder.Sql("UPDATE `A` SET `ForeignKeyColumn` = NULL WHERE `ForeignKeyColumn` = ''") is the correct procedure.
It should execute fine.
Unfortunately, there seems to be an issue, where Jet still holds a lock on the table used in the UPDATE command, when the CREATE INDEX statement is executed (that has been generated for your new navigation property and is part of the Up() migration method).
This is only an issue, if both statements are executed inside the same transaction (which is the case for migrations by default). Otherwise, no lock is held and the CREATE INDEX statement succeeds.
The simplest way to fix this issue, is to set the migrationBuilder.Sql() parameter suppressTransaction to true.
This will execute the statement outside of the rest of the transaction, and not lock the table:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
"UPDATE `A` SET `ForeignKeyColumn` = NULL WHERE `ForeignKeyColumn` = ''",
suppressTransaction: true);
migrationBuilder.CreateIndex(/* ... */);
migrationBuilder.AddForeignKey(/* ... */);
}
The other way, which is able to execute the UPDATE statement inside a transaction, is to execute the command in its own dedicated migration:
Add an empty migration. Add your migrationBuilder.Sql() call to the empty Up() method of this migration.
Add the actual migration (containing the CreateIndex() and AddForeignKey() operations).
Apply both migrations to your database.
I did a migration after of created my database structure using make:entity command :
$ symfony console make:migration
Now, I want to migrate to a specific schema into my PostgresSQL database.
In the documentation of Symfony says:
MANUAL TABLES. It is a common use case, that in addition to your generated database structure based on your doctrine entities you might need custom tables. By default, such tables will be removed by the doctrine:migrations:diff command. If you follow a specific scheme you can configure doctrine/dbal to ignore those tables. Let’s say all custom tables will be prefixed by t_. In this case, you just have to add the following configuration option to your doctrine configuration:
doctrine: dbal: schema_filter: ~^(?!t_)~
I understand that schema_filter is a filter applied to table names (only tables?) through a regular expression. Well, but schema_filter no reference the schemas created into a PostgreSQL database.
Then how can I migrate to a specific schema into a database?
When I execute the command:
$ symfony console make:migration
it creates a migration file that contains a class with the functions 'up' and 'down', both with a parameter $schema. Here a fragment:
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
$this->addSql('CREATE SEQUENCE division_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
This parameter can't assign a value when it is declared. Must be null.
Is it possible to use it to migrate to a specific schema?
Well, while I find a way, I did it like this:
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on 'postgresql'.');
$this->addSql('CREATE SEQUENCE my_schema_name.division_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
I mean that I write the schema name in all migration code generate by the command.
But I still believe that is possible to do it in another way. If yes then how?
I forgot it. Migration fails when I try with this line before of first sentence:
$this->addSql('SET search_path TO my_schema_name');
Also I try assigning the schema name to the parameter $schema:
$schema = 'my_schema_name'
It didn't work either.
Well, thank you. I read this:
the schema should be set via the db connection dsn, all tables by default end up in that schema.
Then I try set the connection parameters in doctrine.yaml: url, dbname, and connectstring, using this notation: dbname.schema, even: dbname/schema, but it does not work.
Then, a spite of this:
you shouldn't hard-code schema names into migrations.
In the migration file was need set the schema for the connection:
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
$this->connection->exec('SET search_path TO my_schema_name;');
$this->addSql('CREATE SEQUENCE division_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
...
However the table migration_versions did not exists in the schema, so I had to create it.
I have added this computed column inside my data model
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string FullName { get; private set; }
After that I created it inside my database using this query
ALTER TABLE [MyDataBase].[dbo].[User] ADD FullName as ([FirstName] + ' ' + [LastName])
When I run my code I get an error that my database has changed .
My question How to create migration for this computed column (because it's already created using sql query)
Entity Framework doesn't know how to properly handle migrations for computed columns, so you need to help it out.
Firstly, delete the computed column from the table in the database.
Then create a new migration in the package manager console:
add-migration FullNameComputed
Replace the body of the Up() method in the new migration with the following:
Sql("ALTER TABLE [TableName] ADD [FullName] AS ([FirstName] + ' ' + [LastName])");
Finally, run the migration from the package manager console:
update-database
Somehow either, my C# code, or entity framework 6 is dropping and creating a database when I run
PM> update-database -verbose
from an explicit migration with an up() method that looks like this...
public override void Up()
{
AddColumn("dbo.Term", "Term_Id", c => c.Int());
CreateIndex("dbo.Term", "Term_Id");
AddForeignKey("dbo.Term", "Term_Id", "dbo.Term", "Id");
}
-verbose generates this...
Origin: Configuration).
Applying explicit migrations: [201410271927053_addTermsTable2].
Applying explicit migration: 201410271927053_addTermsTable2.
ALTER TABLE [dbo].[Term] ADD [Term_Id] [int]
CREATE INDEX [IX_Term_Id] ON [dbo].[Term]([Term_Id])
ALTER TABLE [dbo].[Term] ADD CONSTRAINT [FK_dbo.Term_dbo.Term_Term_Id] FOREIGN KEY ([Term_Id]) REFERENCES [dbo].[Term] ([Id])
INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
(plus the migration insert statement)
None of which indicates (to me) the db is about to be dropped.
Can someone confirm this does not drop?
Does it matter that
this is an Azure SQL database?
that the columns were added in response to adding this.. to the class?
public virtual ICollection<Term> urls { get; set; }
Any pointers on how to make sure I know when a drop create is going to occur?
Thanks.
I am working with a system that is using EF code first and I would like to use a number of SQL Server sparse columns on a table. My current solution is to drop the table created by EF and re-add via a script during the database initialization. Is this something that can be configured with Fluent API in a class inherited from EntityTypeConfiguration or other means?
If you use Entity Frameworks migrations, you can issue a SQL statement like this in the Up method for the migration that adds the sparse column:
Sql("alter table TableName alter column ColumnName int sparse");
Even if you don't use migrations, any one-time execution of dbContext.Database.ExecuteSqlCommand with the same SQL will work.
Neither way is as nice as if you could explicitly configure the type through EF, but they're still better than dropping and replacing the entire table.
This is now supported in EF Core 6.0 as per the documentation here
This can now be configured using IsSparse in OnModelCreating. For example:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<ForumModerator>()
.Property(e => e.ForumName)
.IsSparse();
}