Update database of a deployed web app - asp.net-mvc

I'm using Entity Framework and I had to add some migrations. Now I'd like to update a customer's app and database.
I considered using update-database -script -SourceMigration migrationName command which doesn't work because I create a view in one of my migrations and it gives me the following exception in VS Console.
There is already an object named 'viewName' in the database.
It seems migrate.exe can be used too, but I'm not much familiar with its function and I prefer not to install anything on the customer's computer (because of limited access).
What's the best way to apply those migrations? I prefer to do it with a SQL script, but I can't get ride of that exception.
The migration code of the view:
public override void Up()
{
string script = #"CREATE VIEW [dbo].[viewName]
AS
SELECT *
FROM dbo.existingTable
WHERE (subquery)";
using (var db = new EFDbContext())
{
db.Database.ExecuteSqlCommand(script);
}
}
public override void Down()
{
var script = #"DROP VIEW [dbo].[viewName]";
using (var db = new EFDbContext())
{
db.Database.ExecuteSqlCommand(script);
}
}

The better way to add your views is using Sql inside Up and Down which ‘Up’ applies the changes to the target database and ‘Down’ reverts them:
public override void Up()
{
Sql("EXEC ('CREATE View [dbo].[viewName] AS --etc"
}
public override void Down()
{
Sql(#"IF EXISTS (SELECT
*
FROM sys.views
WHERE object_id = OBJECT_ID(N'dbo.viewName'))
DROP VIEW dbo.viewName)")
}

Related

Model backing the Db context has changed but run anyway. Is there a way to turn off migrations?

I want my windows application to warn that there are migrations to run but to give the user the option to continue without running them.
At the moment I have code first projects with no migrations that access the database
However in the project where I do the database migrations I must either allow the migration to run or delete the migration and undo my changes to the model.
For the configuration I have
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
}
For the migration check I have
using (var db = new MyDbContext())
{
var compatible = db.Database.CompatibleWithModel(false);
if (!compatible)
{
compatible= RunMigrations(db);
}
return compatible;
}
If I don't allow the migration then I get the model backing error when I try to access data
var n = db.mytable.count() // will error
If I comment out the Configuration class and code that calls it I can run the program without error.
The trick is to create a new dbContext inheriting from the usual
public class MigrationDbContext : MyDbContext
{
}
Then the configuration class should use the MigrationDContext
internal sealed class Configuration : DbMigrationsConfiguration<MigrationDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
}
and the migration check should use the MigrationDbContext

How to permanently delete values from the database

I have a method that i call from a javascript, the method suppose to delete the records permanently but it does not go in to the method if the method has DataContext db = new DataContext();, it gives the error Internal Server Error
public void PermanantlyDeleteComment(GetCommentInput input)
{
DataContext db = new DataContext();
//Follow by the code to delete the comment
}
If i comment out DataContext db = new DataContext(); the breakpoint does go in.
I think the problem is with the datacontext but i do know know where
Here is the datacontext
public DataContext() : base("name=Default")
{
this.Configuration.AutoDetectChangesEnabled = true;
this.Configuration.LazyLoadingEnabled = true;
}
I'm using DataContext because abp boilerplate does not want to permanently delete, only soft delete, if you have a way that i can hard delete with boilerplate please let me know.
Answered in this topic: https://forum.aspnetboilerplate.com/viewtopic.php?p=6180#p6193
You can override CancelDeletionForSoftDelete method in your DbContext and prevent cancellation conditionally.
So, like this:
protected override void CancelDeletionForSoftDelete(EntityEntry entry)
{
if (IsSoftDeleteFilterEnabled)
{
base.CancelDeletionForSoftDelete(entry);
}
}
Usage:
public void PermanantlyDeleteComment(GetCommentInput input)
{
using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.SoftDelete))
{
// The code to delete the comment
}
}
I found out that the DataContext was correct, is just i had different EntityFramework version on my Database Library (which has DataContext.cs) and my Web Library

Code First Migration deletes all data

I am using Code First Migrations in my Web API (Mobile Service .NET backend) and whenever my model changes, all data is deleted from my database. This is what I am doing:
I add a new property to my model
I run "Add-Migration AddTestProperty" in the Package Manager console
The new migration is added to the Migrations folder (see code below)
public partial class AddTestProperty: DbMigration
{
public override void Up()
{
AddColumn("testdb.TestTable", "Test", c => c.String());
}
public override void Down()
{
DropColumn("testdb.TestTable", "Test");
}
}
My configuration looks like this:
public Configuration()
{
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = false;
}
Now if I run my webservice, the property is added to my TestTable but all data in all my tables is deleted. What am I doing wrong?
The reason my data was deleted from my database is this: If you use the service project downloaded from the Azure portal, you will find the following code in the App_Start/WebsApiConfig:
public static class WebApiConfig
{
public static void Register()
{
// Use this class to set configuration options for your mobile service
ConfigOptions options = new ConfigOptions();
// Use this class to set WebAPI configuration options
HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options));
Database.SetInitializer(new todoInitializer());
}
}
public class todoInitializer : ClearDatabaseSchemaIfModelChanges<todoContext>
{
.....
To enable Code First Migrations on a Mobile Services .NET backend you need to replace the Database.SetInitializer(new todoInitializer()); with this:
//Database.SetInitializer(new todoInitializer());
var migrator = new DbMigrator(new Configuration());
migrator.Update();
This requires the following using statements:
using System.Data.Entity.Migrations;
using todoService.Migrations;
More information on how to enable Code First Migrations on a Mobile Service .NET backend.

Entity Framework Migrations Integration Tests

Is there a way to write test for specific versions of the migrations?
Lets say that i have done "add-migration 01 Added column...." -> "08 Deleted column...".
And in my tests i want to see how each and every migration from 01 to 08 is doing.
So, to begin with i want to run all the migrations to the version "01 Added column..." and do a test in that state.
After that i may want to upgrade the database to "05..." and do some testing there.
And last, i want to upgrade the database all the way, and do some finishing tests.
Is this posible with EF Migrations Code first?
Sorry, I don't have an access to EF right now, so my code could not work, but I hope that it can give you a general direction of what you can do.
You can create a custom database initializer:
public sealed class MigrationInitializer : IDatabaseInitializer<MyContext>
{
private readonly string _targetMigration;
public MigrationInitializer()
{
}
public MigrationInitializer(string targetMigration)
{
if (string.IsNullOrEmpty(targetMigration))
{
throw new ArgumentException("targetMigration");
}
_targetMigration = targetMigration;
}
public void InitializeDatabase(MyContext context)
{
var migrationsConfiguration = new DbMigrationsConfiguration<MyContext>
{
AutomaticMigrationsEnabled = false
};
var migrator = new DbMigrator(migrationsConfiguration);
if (_targetMigration == null)
{
migrator.Update();
}
else
{
migrator.Update(_targetMigration);
}
}
}
Then force EF to use it in your tests:
System.Data.Entity.Database.SetInitializer<MyContext>(new MigrationInitializer("MyMigration"));
To migrate your database to the latest version you simply use default constructor of MigrationInitializer class.
Note that you can perform both upgrading and downgrading of your database using this method.

Possible to default DateTime field to GETDATE() with Entity Framework Migrations?

I added EntityFramework.Migrations (Beta 1) to an existing Code-First app that is going through some changes (for both migration capabilities and more fine-tuning of the tables I am generating from my code-first API) and ran into the GETDATE() scenario.
I was already using a custom initializer class in my DbContext to run SQL scripts to set some fields and create indexes in my database. A handful of my AlterTable scripts are primary just to setup fields with default values(such as certain DateTime fields being set to GETDATE()). I was really hoping EntityFramework.Migrations would have an answer for this since you can easily specify defaultValue, but so far I'm not seeing one.
Any ideas? I was really hoping that doing the following would magically work. (It is 'magic unicorn', after all)
DateCreated = c.DateTime(nullable: false, defaultValue: DateTime.Now)
Unfortunately, and logically, it set my default value to the time when the Update-Database command was executed.
You can use
DateCreated = c.DateTime(nullable: false, defaultValueSql: "GETDATE()")
Usage:
public partial class MyMigration : DbMigration
{
public override void Up()
{
CreateTable("dbo.Users",
c => new
{
Created = c.DateTime(nullable: false, defaultValueSql: "GETDATE()"),
})
.PrimaryKey(t => t.ID);
...
Update 2012-10-10:
As requested by Thiago in his comment, I add a little extra context.
The code above is a migration-file generated by EF Migrations by running Add-Migration MyMigration as a command in the package manager console. The generated code is based on the models in the DbContext associated with migrations. The answer suggests that you modify the generated script so that a default value is added when the database is created.
You can read more about Entity Framework Code First Migrations here.
I recently ran in to this issue in EF6 (since they still haven't fixed it). The easiest way I found to do it without having to manually modify the Migration class is to override the CodeGenerator in your Configuration class.
By creating a class that implements MigrationCodeGenerator and then overriding the Generate method you can iterate through all of the operations and apply what ever modifications you want.
Once your modifications have been made, you can then initialize your the CSharpMigrationCodeGenerator and return its default value.
public class ExtendedMigrationCodeGenerator : MigrationCodeGenerator
{
public override ScaffoldedMigration Generate(string migrationId, IEnumerable<MigrationOperation> operations, string sourceModel, string targetModel, string #namespace, string className)
{
foreach (MigrationOperation operation in operations)
{
if (operation is CreateTableOperation)
{
foreach (var column in ((CreateTableOperation)operation).Columns)
if (column.ClrType == typeof(DateTime) && column.IsNullable.HasValue && !column.IsNullable.Value && string.IsNullOrEmpty(column.DefaultValueSql))
column.DefaultValueSql = "GETDATE()";
}
else if (operation is AddColumnOperation)
{
ColumnModel column = ((AddColumnOperation)operation).Column;
if (column.ClrType == typeof(DateTime) && column.IsNullable.HasValue && !column.IsNullable.Value && string.IsNullOrEmpty(column.DefaultValueSql))
column.DefaultValueSql = "GETDATE()";
}
}
CSharpMigrationCodeGenerator generator = new CSharpMigrationCodeGenerator();
return generator.Generate(migrationId, operations, sourceModel, targetModel, #namespace, className);
}
}
internal sealed class Configuration : DbMigrationsConfiguration<Project.Models.Context.DatabaseContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
MigrationsDirectory = #"Migrations";
this.CodeGenerator = new ExtendedMigrationCodeGenerator();
}
}
I hope this helps
You must use custom SQL script in Up method for setting default value:
Sql("ALTER TABLE TableName ADD CONSTRAINT ConstraintName DEFAULT GETDATE() FOR ColumnName");
Setting default value in code allows only static values - no database level functions.
Anyway setting it in POCO constructor is correct way if you are going to use code first. Also if you want to set the value in the application for some special cases you cannot use a default value in the database because the default value in the database requires either DatabaseGeneratedOption.Identity or DatabaseGeneratedOption.Computed. Both these options allow setting the property only in the database.
Edit:
Since the product is still in development my answer is no longer valid. Check #gius answer for actual way to achieve this requirement by using defaultValueSql (it wasn't available in EF Migrations Beta 1 but was added in EF 4.3 Beta 1 which already includes migrations).
Create a migration:
public partial class Table_Alter : DbMigration
{
public override void Up()
{
AddColumn("dbo.tableName", "columnName",
c => c.DateTime(nullable: false, defaultValueSql: "GETDATE()"));
}
public override void Down()
{
DropColumn("dbo.tableName", "columnName");
}
}
For existing records it will set the datetime when you will run Update-Database command, for new records it will be set the datetime of creation
Alternatively if your entities inherit from a common interface you can override the SaveChanges method on the DbContext and set or update properties at that point (great for Created Date and Last Changed Date)
This is the most simple way.
First Add DatabaseGeneratedOption.Computed DataAnnotion to your property
and now you can modify de SqlServerMigrationSqlGenarator, override Genarate method and set the DefaultValueSql = "GETDATE()" or "GETUTCDATE()";
Using Entity Framework with .net 6 I was able to make the migration add the default time by changing the defaultValue to defaultValueSql:
public partial class ReportQueueAddCreatedAt : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "CreatedAt",
table: "ReportQueue",
type: "datetime2",
nullable: false,
defaultValueSql: "GETDATE()");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CreatedAt",
table: "ReportQueue");
}
}
An improvement: check if the constraint exists:
Sql(#"
if not exists (
select *
from sys.all_columns c
join sys.tables t on t.object_id = c.object_id
join sys.schemas s on s.schema_id = t.schema_id
join sys.default_constraints d on c.default_object_id = d.object_id
where
d.name = 'DF_ThubOutputEmail_Created'
)
begin
ALTER TABLE dbo.ThubOutputEmails ADD CONSTRAINT DF_ThubOutputEmail_Created default getdate() for Created;
end");

Resources