Set database collation in Entity Framework Code-First Initializer - sql-server

I want to set the default collation for a database, when Entity Framework Code First creates it.
I've tried the following:
public class TestInitializer<T> : DropCreateDatabaseAlways<T> where T: DbContext
{
protected override void Seed(T context)
{
context.Database.ExecuteSqlCommand("ALTER DATABASE [Test] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
context.Database.ExecuteSqlCommand("ALTER DATABASE [Test] COLLATE Latin1_General_CI_AS");
context.Database.ExecuteSqlCommand("ALTER DATABASE [Test] SET MULTI_USER");
}
}
This appears to run OK when SQL Server is already set to the same default collation Latin1_General_CI_AS.
But if I specify a different collation, say SQL_Latin1_General_CP1_CI_AS this fails with the error,
System.Data.SqlClient.SqlException: Resetting the connection results in a different
state than the initial login. The login fails.
Can anyone advise how I can set the collation please?

Solution with a command interceptor
It is definitely possible, though it's a bit of a hack. You can alter the CREATE DATABASE command with a command interceptor. Il will intercept all the commands sent to the database, recognize the database creation command based on a regex expression, and alter the command text with your collation.
Before database creation
DbInterception.Add(new CreateDatabaseCollationInterceptor("SQL_Romanian_Cp1250_CI_AS_KI_WI"));
The interceptor
public class CreateDatabaseCollationInterceptor : IDbCommandInterceptor
{
private readonly string _collation;
public CreateDatabaseCollationInterceptor(string collation)
{
_collation = collation;
}
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { }
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
// Works for SQL Server
if (Regex.IsMatch(command.CommandText, #"^create database \[.*]$"))
{
command.CommandText += " COLLATE " + _collation;
}
}
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { }
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { }
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { }
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { }
}
Remarks
Since the database is created with the right collation from the start, all the columns will automatically inherit that collation and you wan't have to ALTER them afterwards.
Be aware that it will impact any later database creation occurring inside the application domain. So you might want to remove the interceptor after the database is created.

I was able to change collation with a custom migration (EF6). I have automatic migrations enabled. You need to delete your DB first.
Create the migration code by typing Add-Migration [YourCustomMigration] in Package Manager Console. (Code First Migrations)
First step should create your migration class with current model creation code in the Up() override. Add your ALTER DATABASE code BEFORE the table creation codes so they are created using the database collation you want. Also, note the suppressTransaction flag:
public override void Up()
{
Sql("ALTER DATABASE [YourDB] COLLATE [YourCollation]", suppressTransaction: true);
[...Your DB Objects Creation codes here...]
}
Each update-database command issued from then on creates a new migration class. All migration codes are executed in order.

My solution with EFCore 2.1 was to derive from the SqlServerMigrationsSqlGenerator and override Generate(SqlServerCreateDatabaseOperation, IModel, MigrationCommandListBuilder)
internal class CustomSqlServerMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
internal const string DatabaseCollationName = "SQL_Latin1_General_CP1_CI_AI";
public CustomSqlServerMigrationsSqlGenerator(
MigrationsSqlGeneratorDependencies dependencies,
IMigrationsAnnotationProvider migrationsAnnotations)
: base(dependencies, migrationsAnnotations)
{
}
protected override void Generate(
SqlServerCreateDatabaseOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
base.Generate(operation, model, builder);
if (DatabaseCollationName != null)
{
builder
.Append("ALTER DATABASE ")
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
.Append(" COLLATE ")
.Append(DatabaseCollationName)
.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator)
.EndCommand(suppressTransaction: true);
}
}
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.ReplaceService<IMigrationsSqlGenerator, CustomSqlServerMigrationsSqlGenerator>();
}
then used it in the DbContext by replacing the IMigrationsSqlGenerator service
public class MyDbContext : DbContext
{
//...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.ReplaceService<IMigrationsSqlGenerator, CustomSqlServerMigrationsSqlGenerator>();
}
//...
}

I have had the same problem a while ago. Possible solutions:
It appears that EF creates the database using the server default
collation so one thing you could do is change that.
You cannot change the database collation within the Seed() method
but you can change the collation of individual columns for a table
(NOTE: there is no such thing as table collation, it does relate to
column in a table). You will have to change each column's collation
separately.
If you are using migrations, you could alter the table column
collations within your Up() method.
As you are using the Seed() method, I would suggest the following (modify as appropriate) within the Seed() method:
context.Database.ExecuteSqlCommand(
#"ALTER TABLE MyTable ALTER COLUMN MyColumn NVARCHAR(max) COLLATE MyCollation NOT NULL");
Hope that helps.

I would like to explain why you should not use the seed method for this. If you change your database collation after any columns have been added there is a large risk for collation conflicts like below
Cannot resolve the collation conflict between
"SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_100_CI_AS" in the
equal to operation.
This is due to the fact that if you alter your database with ALTER DATABASE [YourDb] COLLATE [YourCollation] you will only change the databases collation and not previously created columns.
Example in T-SQL:
DECLARE #DBName nvarchar(50), #SQLString nvarchar(200)
SET #DBName = db_name();
SET #SQLString = 'ALTER DATABASE [' + #DBName + '] COLLATE Latin1_General_100_CI_AS'
EXEC(#SQLString)
/* Find Collation of SQL Server Database */
SELECT DATABASEPROPERTYEX(#DBName, 'Collation')
/* Find Collation of SQL Server Database Table Column */
SELECT name, collation_name
FROM sys.columns
WHERE OBJECT_ID IN (SELECT OBJECT_ID
FROM sys.objects
WHERE type = 'U'
AND name = 'AspNetUsers')
AND name = 'FirstName'
Due to this you need to change database collation before any columns are added or change every column separately. Possible solutions:
#MathieuRenda https://stackoverflow.com/a/42576705/3850405
I would put the DbInterception.Add in a class deriving from DbConfiguration or in Application_Start in Global.asax as recommended in the documentation. Note: Wherever you put this code, be careful not to execute DbInterception.Add for the same interceptor more than once, or you'll get additional interceptor instances.
public class ApplicationDbConfiguration: DbConfiguration
{
public ApplicationDbConfiguration()
{
DbInterception.Add(new CreateDatabaseCollationInterceptor("Latin1_General_100_CI_AS"));
}
}
I would also not inherit from the interface but instead use the implementation of DbCommandInterceptor as Microsoft does in their examples.
using System.Data.Common;
using System.Data.Entity.Infrastructure.Interception;
using System.Text.RegularExpressions;
namespace Application.Repositories.EntityFramework
{
public class CreateDatabaseCollationInterceptor : DbCommandInterceptor
{
private readonly string _collation;
public CreateDatabaseCollationInterceptor(string collation)
{
_collation = collation;
}
public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
// Works for SQL Server
if (Regex.IsMatch(command.CommandText, #"^create database \[.*]$"))
{
command.CommandText += " COLLATE " + _collation;
}
}
}
}
More information here: https://learn.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/connection-resiliency-and-command-interception-with-the-entity-framework-in-an-asp-net-mvc-application
#steliosalex: https://stackoverflow.com/a/22895703/3850405. Note that changing every column might not be enough either. You also need to handle metadata and parameters for stored procedure and similar get the collation that the database had when these where created. Changing collation completely requires a create database command with the right collation.
#RahmiAksu https://stackoverflow.com/a/31119371/3850405 NOTE: This is not a good solution in my opinion but if you use it edit the very first migration. Can't be used if the database is already in production. If you have a seed method the exception Resetting the connection results in a different state than the initial login will be thrown.
Your Seed SqlException can be solved by using a plain ADO.Net connection, so the context's connection won't be reset. However as mentioned above this will probably cause a lot of errors later.
using (var conn = new SqlConnection(context.Database.Connection.ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText =
string.Format("ALTER DATABASE [{0}] COLLATE Latin1_General_100_CI_AS",
context.Database.Connection.Database));
conn.Open();
cmd.ExecuteNonQuery();
}
}
SqlException: Resetting the connection results in a different state
than the initial login. The login fails. Login failed for user ''.
Cannot continue the execution because the session is in the kill
state.
Source:
https://stackoverflow.com/a/50400609/3850405

It's simply not possible using current versions of EF (EF6). However, at least EF6+ can now work with already existent database. We've changed our deployment scenario such that the database is already created by our deployment script (incl. the default collation) and let EF6 work with the existing database (using the correct default collation).
If you absolutely have to create the database inside your code and cannot use anything else than EF (e.g. you are not able to create the database using ADO.NET) then you have to go for seliosalex answer. It's the only solution we came up, however, see my comment, it is a lot of work to do it right.

EF 5 now supports creating missing tables in an existing database with Code First, so you can create an empty database and set the collation correct, before running an CF on it.

1):
public class DataBaseContext : System.Data.Entity.DbContext
{
public DataBaseContext() : base("MyDB") { }
static DataBaseContext()
{
System.Data.Entity.Database.SetInitializer(new MyInitializer());
}
....
}
2):
public class MyInitializer : DropCreateDatabaseIfModelChanges<DataBaseContext>
{
public MyInitializer() { }
protected override void Seed(DataBaseContext context)
{
base.Seed(context);
using (var conn = new SqlConnection(context.Database.Connection.ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText =
string.Format("ALTER DATABASE [{0}] COLLATE Persian_100_CI_AS",
context.Database.Connection.Database);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
context.Database.Connection.Close();
}
}
}

Related

EF Core Stored Procedure FromSqlRaw Does Not Give Updated Values

When running a procedure on EF Core 3 using FromSqlRaw that updates values in the table, EF DOES NOT return the updated values when I query the database for those changed values.
I have been able to reproduce this behavior. To reproduce create a new console app c# with .net core 3.1.
Copy paste the code below into your main Program.cs file:
using System;
using System.Linq;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
namespace EfCoreTest
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
// testing proc
var dbContext = new TestContext();
var tables = dbContext.TestTables.ToList();
var updated = dbContext.TestTables
.FromSqlRaw("execute testProc #Id=#Id, #Comments=#Comments", new object[]
{
new SqlParameter("Id", 1),
new SqlParameter("Comments", "testing comments 2"),
})
.ToList();
var again = dbContext.TestTables.ToList();
}
}
public class TestTable
{
public int TestTableId { get; set; }
public string Comment { get; set; }
}
public class TestContext : DbContext
{
public DbSet<TestTable> TestTables { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=localhost\SQLEXPRESS;Database=TestDb;Trusted_Connection=True");
}
}
}
Ensure that the following packages are installed:
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.SqlServer.Design
Change your connection string if necessary.
Run dotnet ef migrations add initial
Run dotnet ef database update
Run the following code in your db:
drop procedure if exists testProc
go
create procedure testProc
#Id int,
#Comments nvarchar(max)
as
begin
update dbo.TestTables
set Comment = #Comments
where TestTableId = #Id;
select * from dbo.TestTables;
end
go
INSERT INTO [dbo].[TestTables]
(Comment) VALUES ('Test Comment');
So when you run the Main program on debug and put a breaker, you'll notice that NONE of the objects return values that were updated by the procedure when go to inspect it. While in debug if you run a select statement on the table you will see that the "Comment" field is indeed updated.
Why is this?
This is not specific to FromSql, but the way EF Core (all versions) tracking queries work.
Here is an excerpt from EF Core How Queries Work documentation topic:
The following is a high level overview of the process each query goes through.
The LINQ query is processed by Entity Framework Core to build a representation that is ready to be processed by the database provider
The result is cached so that this processing does not need to be done every time the query is executed
The result is passed to the database provider
The database provider identifies which parts of the query can be evaluated in the database
These parts of the query are translated to database specific query language (for example, SQL for a relational database)
One or more queries are sent to the database and the result set returned (results are values from the database, not entity instances)
For each item in the result set
If this is a tracking query, EF checks if the data represents an entity already in the change tracker for the context instance
If so, the existing entity is returned
If not, a new entity is created, change tracking is setup, and the new entity is returned
Note the last bullet. What they do is basically an implementation of the so called client wins strategy (as opposed to database wins which you are looking for), and currently there is no way of changing that other than using no-tracking query.
In your example, insert AsNotTracking() somewhere in the queries (before ToList, after dbContext.TestTables - it really doesn't matter because it applies to the whole query), or just
dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
and now you'll see the updated values (from your SP call or from other sessions to the same database).

How to execute stored procedure on ODBC Snowflake Destinastion?

I'am building new package for move data from aws sql server instance to snowflake odbc destination. If i found rows which was updated i must change them on snowflake as well. In common's i found only 'OLE DB Command' for execute procedure for update diffrent rows.
The problem is i need something like "ODBC Command" for execute procedure to update diffrent rows between SQL Server&Snowflake.
OK, I do it.
So if u need UPDATE rows on ODBC destination in SSIS u have only one way to do that u need to use Script Component. Before I thought it will be something like ODBC Command and we will need to write stored procedure to change rows in the destination. I link that for ppl who care in the future.
The OLE DB Command transformation runs an SQL statement for each row in a data flow. For example, you can run an SQL statement that inserts, updates, or deletes rows in a database table.
Microsoft OLE DB Command description
I wrote a simple code in c# to Update Rows and it works perfectly. U can simple rebuild it for execute procedure or do whatever u need.
public class ScriptMain : UserComponent
{
OdbcConnection odbcConn;
OdbcCommand odbcCmd;
OdbcParameter odbcParam;
public override void AcquireConnections(object Transaction)
{
/// Create a String base on that which u define on package for connection and
adding a password
string connectionString;
connectionString = this.Connections.SFConnection.ConnectionString;
odbcConn = new OdbcConnection(connectionString + "PWD=YOURPASSWORD");
odbcConn.Open();
}
public override void PreExecute()
{
///Create command which we wanna execute
base.PreExecute();
odbcCmd = new OdbcCommand("UPDATE klienci SET IMIE= ?,NAZWISKO= ? ,NUMER_TELEFONU= ? ,EMAIL= ? ,ULICA= ? ,MIASTO= ? ,STATE= ? ,ZIP_CODE = ? WHERE CUSTOMER_ID= ?", odbcConn);
}
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
///Adding parameters and connecting them with our input column from package
odbcCmd.Parameters.AddWithValue("#IMIE", Row.Sourcefirstname);
odbcCmd.Parameters.AddWithValue("#NAZWISKO", Row.Sourcelastname);
odbcCmd.Parameters.AddWithValue("#NUMER_TELEFONU", Row.Sourcephone);
odbcCmd.Parameters.AddWithValue("#EMAIL", Row.Sourceemail);
odbcCmd.Parameters.AddWithValue("#ULICA", Row.Sourcestreet);
odbcCmd.Parameters.AddWithValue("#MIASTO", Row.Sourcecity);
odbcCmd.Parameters.AddWithValue("#STATE", Row.Sourcestate);
odbcCmd.Parameters.AddWithValue("#ZIP_CODE", Row.Sourcezipcode);
odbcCmd.Parameters.AddWithValue("#CUSTOMER_ID", Row.Sourcecustomerid);
odbcCmd.ExecuteNonQuery();
}
}

Audit trail with Entity Framework Core

I have an ASP.NET core 2.0 using Entity Framework core on a SQL Server db.
I have to trace and audit all the stuff made by the users on the data. My goal is to have an automatic mechanism writing all what is happening.
For example, if I have the table Animals, I want a parallele table "Audit_animals" where you can find all the info about the data, the operation type (add, delete, edit) and the user who made this.
I already made this time ago in Django + MySQL, but now the environment is different. I found this and it seems interesting, but I'd like to know if there are better ways and which is the best approach to do this in EF Core.
UPDATE
I'm trying this and something happens, but I have some problems.
I added this:
services.AddMvc().AddJsonOptions(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
public Mydb_Context(DbContextOptions<isMultiPayOnLine_Context> options) : base(options)
{
Audit.EntityFramework.Configuration.Setup()
.ForContext<Mydb_Context>(config => config
.IncludeEntityObjects()
.AuditEventType("Mydb_Context:Mydb"))
.UseOptOut()
}
public MyRepository(Mydb_Context context)
{
_context = context;
_context.AddAuditCustomField("UserName", "pippo");
}
I also created a table to insert the audits (only one to test this tool), but the only thing I got is what you see in the image. A list of json files with the data I created.... why??
Read the documentation:
Event Output
To configure the output persistence mechanism please see Configuration and Data Providers sections.
Then, in the documentation on Configuration:
If you don't specify a Data Provider, a default FileDataProvider will be used to write the events as .json files into the current working directory. (emphasis mine)
Long and short, follow the documentation to configure the data provider you'd like to use.
If you are going to map the audit table (Audit_Animals) to the same EF context as the audited Animals table, you can use the EntityFramework Data Provider included on the same Audit.EntityFramework library.
Check the documentation here:
Entity Framework Data Provider
If you plan to store the audit logs in
the same database as the audited entities, you can use the
EntityFrameworkDataProvider. Use this if you plan to store the audit
trails for each entity type in a table with similar structure.
There is another library that can audit EF contexts in a similar way, take a look: zzzprojects/EntityFramework-Plus.
Cannot recommend one over the other since they provide different features (and I'm the owner of the audit.net library).
Update:
.NET 6 and Entity Framework Core 6.0 supports SQL Server temporal tables out of the box.
See this answer for examples:
https://stackoverflow.com/a/70017768/3850405
Original:
You could have a look at Temporal tables (system-versioned temporal tables) if you are using SQL Server 2016< or Azure SQL.
https://learn.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables?view=sql-server-ver15
From documentation:
Database feature that brings built-in support for providing
information about data stored in the table at any point in time rather
than only the data that is correct at the current moment in time.
Temporal is a database feature that was introduced in ANSI SQL 2011.
There is currently an open issue to support this out of the box:
https://github.com/dotnet/efcore/issues/4693
There are third party options available today but since they are not from Microsoft it is of course a risk that they won't be supported in future versions.
https://github.com/Adam-Langley/efcore-temporal-query
https://github.com/findulov/EntityFrameworkCore.TemporalTables
I solved it like this:
If you use the included Visual Studio 2019 LocalDB (Microsoft SQL Server 2016 (13.1.4001.0 LocalDB) you will need to upgrade if you use cascading DELETE or UPDATE. This is because Temporal tables with cascading actions is not supported in that version.
Complete guide for upgrading here:
https://stackoverflow.com/a/64210519/3850405
Start by adding a new empty migration. I prefer to use Package Manager Console (PMC):
Add-Migration "Temporal tables"
Should look like this:
public partial class Temporaltables : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
Then edit the migration like this:
public partial class Temporaltables : Migration
{
List<string> tablesToUpdate = new List<string>
{
"Images",
"Languages",
"Questions",
"Texts",
"Medias",
};
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql($"CREATE SCHEMA History");
foreach (var table in tablesToUpdate)
{
string alterStatement = $#"ALTER TABLE [{table}] ADD SysStartTime datetime2(0) GENERATED ALWAYS AS ROW START HIDDEN
CONSTRAINT DF_{table}_SysStart DEFAULT GETDATE(), SysEndTime datetime2(0) GENERATED ALWAYS AS ROW END HIDDEN
CONSTRAINT DF_{table}_SysEnd DEFAULT CONVERT(datetime2 (0), '9999-12-31 23:59:59'),
PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)";
migrationBuilder.Sql(alterStatement);
alterStatement = $#"ALTER TABLE [{table}] SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = History.[{table}]));";
migrationBuilder.Sql(alterStatement);
}
}
protected override void Down(MigrationBuilder migrationBuilder)
{
foreach (var table in tablesToUpdate)
{
string alterStatement = $#"ALTER TABLE [{table}] SET (SYSTEM_VERSIONING = OFF);";
migrationBuilder.Sql(alterStatement);
alterStatement = $#"ALTER TABLE [{table}] DROP PERIOD FOR SYSTEM_TIME";
migrationBuilder.Sql(alterStatement);
alterStatement = $#"ALTER TABLE [{table}] DROP DF_{table}_SysStart, DF_{table}_SysEnd";
migrationBuilder.Sql(alterStatement);
alterStatement = $#"ALTER TABLE [{table}] DROP COLUMN SysStartTime, COLUMN SysEndTime";
migrationBuilder.Sql(alterStatement);
alterStatement = $#"DROP TABLE History.[{table}]";
migrationBuilder.Sql(alterStatement);
}
migrationBuilder.Sql($"DROP SCHEMA History");
}
}
tablesToUpdate should contain every table you need history for.
Then run Update-Database command.
Original source, a bit modified with escaping tables with square brackets etc:
https://intellitect.com/updating-sql-database-use-temporal-tables-entity-framework-migration/
Testing Create, Update and Delete will then show a complete history.
[HttpGet]
public async Task<ActionResult<string>> Test()
{
var identifier1 = "OATestar123";
var identifier2 = "OATestar12345";
var newQuestion = new Question()
{
Identifier = identifier1
};
_dbContext.Questions.Add(newQuestion);
await _dbContext.SaveChangesAsync();
var question = await _dbContext.Questions.FirstOrDefaultAsync(x => x.Identifier == identifier1);
question.Identifier = identifier2;
await _dbContext.SaveChangesAsync();
question = await _dbContext.Questions.FirstOrDefaultAsync(x => x.Identifier == identifier2);
_dbContext.Entry(question).State = EntityState.Deleted;
await _dbContext.SaveChangesAsync();
return Ok();
}
Tested a few times but the log will look like this:
This solution has a huge advantage IMAO that it is not Object Relational Mapper (ORM) specific and you will even get history if you write plain SQL.
The History tables are also read only by default so less chance of a corrupt audit trail. Error received: Cannot update rows in a temporal history table ''
If you need access to the data you can use your preferred ORM to fetch it or audit via SQL.

Entity Framework Migration update-database succeeded but database column is not added

I am using Entity Framework in VS2012 with SQL Server 2008 R2. I have migration enabled, and I am adding a string field (ie. DropboxUrl) to one of my database classes (ie. Designs).
// Design.cs
public class Design
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[StringLength(Constants.DESIGN_NAME_MAX_LENGTH)]
public string Name { get; set; }
[StringLength(Constants.DESIGN_DESC_MAX_LENGTH)]
public string Description { get; set; }
public ItemDate Dates { get; set; }
public string DropboxUrl { get; set; } // Added this line
}
// SedaContext.cs:
public class SedaContext : DbContext
{
public DbSet<Company> Companies { get; set; }
public DbSet<Design> Designs { get; set; }
…
}
// Global.aspx
protected void Application_Start()
{
// Application initialize
// https://stackoverflow.com/questions/3600175/the-model-backing-the-database-context-has-changed-since-the-database-was-crea
Database.SetInitializer<SedaContext>(null);
In Package Manager Console, when I run
PM> update-database, it complains that there is already an object named 'Companies' in the database. 'Companies' is a table that currently exists in the existing database that I am trying to update.
ie.
PM> update-database -verbose
Using StartUp project 'UI'.
Using NuGet project 'UI'.
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Target database is: 'SedaDev' (DataSource: ., Provider: System.Data.SqlClient, Origin: Configuration).
No pending explicit migrations.
Applying automatic migration: 201405311730564_AutomaticMigration.
CREATE TABLE [dbo].[Companies] (
[Id] [uniqueidentifier] NOT NULL DEFAULT newsequentialid(),
[Name] [nvarchar](max),
[Description] [nvarchar](max),
[Owner_UserId] [int],
CONSTRAINT [PK_dbo.Companies] PRIMARY KEY ([Id])
)
System.Data.SqlClient.SqlException (0x80131904): There is already an object named 'Companies' in the database.
...
at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)
at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run()
at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
at System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration, Boolean force)
at System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0()
at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
ClientConnectionId:fa9e9e62-aba0-435f-9309-e9fc8fbe19d5
There is already an object named 'Companies' in the database.
Attempt 1: After searching for this error, I came across this workaround:
http://christesene.com/entity-framework-4-3-code-first-with-automatic-migrations/
It recommended that I run first
PM> Add-migration initial
Scaffolding migration 'initial'.
The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration initial' again.
And removed the Up/Down method:
ie. I could see that DropboxUrl was a field in the Up method but I removed it as suggested.
public override void Up()
{
/*
CreateTable(
"dbo.Companies",
c => new
{
Id = c.Guid(nullable: false, identity: true),
Name = c.String(),
Description = c.String(),
Owner_UserId = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.UserProfiles", t => t.Owner_UserId)
.Index(t => t.Owner_UserId);
...
CreateTable( "dbo.Designs",
c => new
{
Id = c.Guid(nullable: false, identity: true),
Name = c.String(maxLength: 100),
Description = c.String(maxLength: 1000),
Dates_Create = c.DateTime(nullable: false),
Dates_LastUpdate = c.DateTime(nullable: false),
DropboxUrl = c.String(),
Project_Id = c.Guid(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Projects", t => t.Project_Id)
.Index(t => t.Project_Id);
*/
}
Afterwards, I run update-database again, and it appears to be successful.
PM> update-database -verbose
Using StartUp project 'UI'.
Using NuGet project 'UI'.
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Target database is: 'SedaDev' (DataSource: phobos.spxis.com, Provider: System.Data.SqlClient, Origin: Configuration).
Applying explicit migrations: [201406020449030_initial].
Applying explicit migration: 201406020449030_initial.
INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
VALUES (N'201406020449030_initial', N'Delecs.Seda.DataAccess.Migrations.Configuration', 0x1F8B0800000000000400ED1DCB72DCB8F19EAAFCC3D49C92544523D9F166
...
7B7C117028FAD9D8632C54E5F87C13A0D36590D83B7A73FA9F8AD368F7FFE3F0347EA807B340100 , N'6.0.2-21211')
Running Seed method
Issue 1: My table was not altered after the update (ie. the DropboxUrl column that is present in code wasn't added to the database).
Issue 2: And I also could not get the database back to its initial state:
ie.
PM> update-database -TargetMigration $InitialDatabase
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Reverting migrations: [201406020449030_initial].
Reverting automatic migration: 201406020449030_initial.
Automatic migration was not applied because it would result in data loss.
PM> update-database -TargetMigration $InitialDatabase -force
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Reverting migrations: [201406020449030_initial].
Reverting automatic migration: 201406020449030_initial.
System.Data.SqlClient.SqlException (0x80131904): Could not drop object 'dbo.UserProfiles' because it is referenced by a FOREIGN KEY constraint.
Attempt 2: I also tried using the -IgnoreChanges flag when I add-migration:
Automatic Migrations for ASP.NET SimpleMembershipProvider
PM> Add-migration initial -IgnoreChanges
Re-scaffolding migration 'initial'.
PM> update-database -verbose
Again, I saw the same thing, where the update-database succeeds but the database column DropboxUrl was not added to the Designs table. However, if I create a new database, then the DropboxUrl column is present as expected.
Question: How could I get around the there is already an object named 'Companies' in the database error while doing update-database, and still successfully add my column? It seems like it should be a basic scenario that just works.
Thanks.
When you commented out the Up() and Down() methods on the migration you removed the code to add the database column.
You should change the Up() and Down() as follows for your initial :
public override void Up() {
AddColumn("Companies", "DropboxUrl", x => x.String());
}
public override void Down() {
DropColumn("Companies", "DropboxUrl");
}
For Entity Framework to figure this out for you in the future, you need to do a Initial Migration to make it understand there is an existing table before you add the property to the code. e.g. Add-Migration -force -ignore.
Then you add the column and go Add-Migration AddedDropboxUrlColumn, and it will generate the Up() and Down() methods as I have described in a new migration.

Merge aspnetdb.mdf with my own database (automatically generated)

I've searched the internet thoroughly but couldn't find a clear answer to the problem. I have got the aspnet.db database. But i want to add my own tables and data to this database. If i try to connect to it with the connection string:
<add name ="ToernooiCompanionDBContext" connectionString ="Data Source= .\SQLEXPRESS; Integrated Security = SSPI; Trusted_Connection=True; Initial Catalog= aspnetdb" providerName ="System.Data.SqlClient"/>
A new database will be created (aspnetdb.mdf) in C:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\DATA.
I want the database (which is automatically generated by codefirst) to merge with the existing one in my APP_DATA folder. What am I doing wrong?
I've tried adding AttachDbFilename=|DataDirectory|aspnetdb.mdf and User Instance=true to my connection string, or using the LocalSqlServer connection string which is defined in machine.config, but in all cases this overwrites the existing database. If I remove Initial Catalog=aspnetdb then I get an error that the initial catalog is needed.
I had the same problem but this link got me on the track to something that worked at least for me. I hope this helps someone at least! :)
Create a database
Add the aspnet tables to the new database
Fix the database connections in web.config so they point to the same database
Write some sql that removes all tables except the ones that start with "aspnet_"
Add the sql to the database initializer you write by your self
Add a call to the database initializer in Global.asax.cs
1. Create a database
I usually do this with SQL Server Management Studio. The database I used for this example code is SQL Server 2008R2 but I have done the same with SQL Server Express that you use.
2. Add the aspnet tables to the new database
I use the following tool which if you use it without any command line arguments works like a wizard.
%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_regsql.exe
3. Fix the database connections so they point to the same database
The following two lines are from the test application I made. Notice that the name of the second connectionstring (MyHealthContext) is identical to the name of the DbContext I am using for my code first classes.
DbContext:
public class MyHealthContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<PersonAttribute> PeopleAttributes { get; set; }
}
Web.config
<add name="ApplicationServices" connectionString="Server=localhost\mssql2008r2;Database=MyHealth;Integrated Security=True;" providerName="System.Data.SqlClient"/>
<add name="MyHealthContext" connectionString="Server=localhost\mssql2008r2;Database=MyHealth;Integrated Security=True;" providerName="System.Data.SqlClient"/>
4. SQL that removes all but the aspnetdb-tables
DECLARE #cmdDropConstraints VARCHAR(4000)
DECLARE #cmdDropTables VARCHAR(4000)
-- ======================================================================
-- DROP ALL THE FOREIGN KEY CONSTRAINTS FROM THE TABLES WE WANT TO DROP
-- ======================================================================
DECLARE cursorDropConstraints CURSOR FOR
SELECT
'ALTER TABLE ['+ s.name + '].[' + t.name + '] DROP CONSTRAINT [' + f.name +']'
FROM
sys.foreign_keys f
INNER JOIN sys.tables t ON f.parent_object_id=t.object_id
INNER JOIN sys.schemas s ON t.schema_id=s.schema_id
WHERE
t.is_ms_shipped=0
AND t.name NOT LIKE 'aspnet_%'
AND t.name <> 'sysdiagrams'
OPEN cursorDropConstraints
WHILE 1=1
BEGIN
FETCH cursorDropConstraints INTO #cmdDropConstraints
IF ##fetch_status != 0 BREAK
EXEC(#cmdDropConstraints)
END
CLOSE cursorDropConstraints
DEALLOCATE cursorDropConstraints;
-- ======================================================================
-- DROP ALL THE RELEVANT TABLES SO THAT THEY CAN BE RECREATED
-- ======================================================================
DECLARE cursorDropTables CURSOR FOR
SELECT
'DROP TABLE [' + Table_Name + ']'
FROM
INFORMATION_SCHEMA.TABLES
WHERE
Table_Name NOT LIKE 'aspnet_%'
AND TABLE_TYPE <> 'VIEW'
AND TABLE_NAME <> 'sysdiagrams'
OPEN cursorDropTables
WHILE 1=1
BEGIN
FETCH cursorDropTables INTO #cmdDropTables
IF ##fetch_status != 0 BREAK
EXEC(#cmdDropTables)
END
CLOSE cursorDropTables
DEALLOCATE cursorDropTables;
5. Code for the database initializer:
Replace the "SQL CODE GOES HERE" below with the sql from step 4
public class MyHealthInitializerDropCreateTables : IDatabaseInitializer<MyHealthContext>
{
public void InitializeDatabase(MyHealthContext context)
{
bool dbExists;
using (new TransactionScope(TransactionScopeOption.Suppress))
{
dbExists = context.Database.Exists();
}
if (dbExists)
{
// Remove all tables which are specific to the MyHealthContext (not the aspnetdb tables)
context.Database.ExecuteSqlCommand(#"SQL CODE GOES HERE");
// Create all tables which are specific to the MyHealthContext (not the aspnetdb tables)
var dbCreationScript = ((IObjectContextAdapter)context).ObjectContext.CreateDatabaseScript();
context.Database.ExecuteSqlCommand(dbCreationScript);
Seed(context);
context.SaveChanges();
}
else
{
throw new ApplicationException("No database instance");
}
}
protected virtual void Seed(MyHealthContext context)
{
//TODO: Add code for seeding your database with some initial data...
}
}
6. Code that hooks in your new database initializer
To make sure that the custom database initializer isn't accidentily run in the production environment i added a #if DEBUG statement since I always compile my code in release mode before publishing.
protected void Application_Start()
{
//TODO: Comment out this database initializer(s) before going into production
#if DEBUG
Database.SetInitializer<MyHealthContext>(new MyHealthInitializerDropCreateTables()); // Create new tables in an existing database
#endif
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Open ASPNetDB db in sql server by attaching as new database
Make Creation scripts of tables / Stored proce3dures from ASPNetDB and run in your own database to create tables in your database
Open web.config of your application and attach application to your own database. Copy the name of connection string
Go to membership area and replace connectionstring name with copied one
Do above step with area as well

Resources