Entity Framework - how to create sql script with "GO" statements - sql-server

In EF 6.1 you can run the command:
update-database -script
This generates the full SQL script for one or many migrations. The problem we found is that it does not generate the required "GO" statements for sql server. This is causing the SQL script to fail when it would otherwise succeed if run directly without the "-script" parameter.
Has anyone else run into this?

You can override the script generation behavior by creating a wrapper class around the SqlServerMigrationSqlGenerator class. This class contains an overloaded Generate() method which takes in arguments that represent the type of script block it's generating. You can override these methods to add "GO" statements before starting new script blocks.
public class ProdMigrationScriptBuilder : SqlServerMigrationSqlGenerator
{
protected override void Generate(HistoryOperation insertHistoryOperation)
{
Statement("GO");
base.Generate(insertHistoryOperation);
Statement("GO");
}
protected override void Generate(CreateProcedureOperation createProcedureOperation)
{
Statement("GO");
base.Generate(createProcedureOperation);
}
protected override void Generate(AlterProcedureOperation alterProcedureOperation)
{
Statement("GO");
base.Generate(alterProcedureOperation);
}
protected override void Generate(SqlOperation sqlOperation)
{
Statement("GO");
base.Generate(sqlOperation);
}
}
You will also need to set this class as the Sql Generator in your Configuration class constructor.
public Configuration()
{
AutomaticMigrationsEnabled = false;
// Add back this line when creating script files for production migration...
SetSqlGenerator("System.Data.SqlClient", new ProdMigrationScriptBuilder());
}
One gotcha to this approach is that while it works great for creating re-usable script files for SQL Server Enterprise Manager, the GO statements do not work when executing migrations locally. You can comment out the SetSqlGenerator line when working locally, then simply add it back when you are ready to create your deployment scripts.

If you are trying to alter a view using Sql('Alter View dbo.Foos As etc'), then you can avoid the
should be the first statement in a batch file error
without adding GO statements by putting the sql inside an EXEC command:
Sql(EXEC('Alter View dbo.Foos As etc'))
Reference:
https://stackoverflow.com/a/20352867/150342

Related

How can you use an Apex class from the hello.apex script

How can you use an Apex class from the hello.apex script in a newly created standard project with VS Code?
This is probably really simple, I thought I should use some 'using' or 'include' statement but I cannot find the information. This is what I did:
I am working from VS Code.
sfdx create project.
chosen: Standard.
In the directory scripts/apex I open the file hello.apex.
Log in to org, vscodeOrg.
sfdx Execute Anonymous Apex with Editor contents, this succeeds.
sfdx Create Apex Class, called NewClass with static function
right under the constructor I add a static method, the complete class code is now:
public with sharing class NewClass {
public NewClass() {
}
public static void Test() {
System.debug('call from apex class');
}
}
The file is located in: ...\NewStandardProject\force-app\main\default\classes\NewClass.cls
Go back to hello.apex, execute still succeeds.
I add in the hello.apex file:
NewClass.Test();
Making the script code this:
string tempvar = 'Enter_your_name_here';
System.debug('Hello World!');
System.debug('My name is ' + tempvar);
NewClass.Test();
The editor gives me 'Intelligent code completion', so it seems my new class is known.
I try to execute again with 'Execute Anonymous Apex with Editor contents', it fails with this feed back:
Error: Variable does not exist: NewClass
If I replace the static call with e.g.
NewClass nc = new NewClass();
It gives the same error.
If I replace public with global in NewClass, that also does not help.
What am I doing wrong here?
Answering my own question then to make this complete:
Install sfdx
Install vs sfdx plug in
Press Ctrl-Shift P to call the sfdx from vs code
sfdx create project -> Standard
In the directory scripts/apex, open the file hello.apex
Log in to org, vscodeOrg (or if you have no standard org yet call control shift p, sfdx create a default scratch org)
sfdx Execute Anonymous Apex with Editor contents, this succeeds
sfdx Create Apex Class, called NewClass with static function
right under the constructor I add a static method, the complete class code is now:
public with sharing class NewClass {
public NewClass() {
}
public static void Test() {
System.debug('call from apex class');
}
}
In the left pain click on the file in the project directory and choose 'sfdx deploy source to org'.
(And that step as a developer customed to working with local executables, I forgot).
Add in the hello.apex file: NewClass.Test();
Making the code there:
// Use .apex files to store anonymous Apex.
// You can execute anonymous Apex in VS Code by selecting the
// apex text and running the command:
// SFDX: Execute Anonymous Apex with Currently Selected Text
// You can also execute the entire file by running the command:
// SFDX: Execute Anonymous Apex with Editor Contents
string tempvar = 'Enter_your_name_here';
System.debug('Hello World!');
System.debug('My name is ' + tempvar);
NewClass.Test();
I try to execute again with 'Execute Anonymous Apex with Editor contents'.
In the output amongst a few other lines you should see: 'call from apex class'.

SSIS 2008 Script task compile failure: Cannot load script for execution

I'm running SSIS 2008, and I'm getting 'Cannot load script for execution.' error when running the script below. I'm able to build this script when editing script task, but it crashes when I run a package. I've tried multiple variations of this script, for example, instead of declaring c# variable dtss below, I've used DTS.Variables default object, it gave me the same issue. Please note I'm not .NET expert, but I need to create folder programmatically only if it doesn't exist, so I need to use script component. As I can see, it's a very "popular" issue, so I've tried some solutions suggested on this forum, but nothing worked for me.
using System;
using System.IO;
using System.Data;
using Microsoft.SqlServer.Dts;
using Microsoft.SqlServer.Dts.Runtime;
class Test
{
/*protected static Microsoft.SqlServer.Dts.Tasks.ScriptTask.ScriptObjectModel CurrDtsContext;*/
protected static Microsoft.SqlServer.Dts.Runtime.Variables dtss;
public static void Main()
{
// Specify the directory you want to manipulate.
string path = (string)(dtss["ArchivePath"].Value + "\\" + dtss["FacilityName"]);
try
{
// Determine whether the directory exists.
if (!Directory.Exists(path))
{
// Try to create the directory.
DirectoryInfo di = Directory.CreateDirectory(path);
}
}
catch (Exception e)
{
Console.WriteLine("The process failed: ", e.ToString());
}
finally { }
}
}
I've eventually used File System task with property UseDirectoryIfExist=TRUE, this way it doesn't overwrite existing folder and doesn't seem to throw any error if folder already exist.

Entity Framework 6 - How to update customer's database with CodeFirst migration

My team develops an application which deploys MSSQL database at customer's system. We encountered a problem with using migrations to update the customer's database structure .
We can't use automated migrations because more than one instance of the app can run on the same database so if one of instances gets updated and therefore changes the model and therefore the structure of database the others change it back so neither of them can work on the database.
We can't use nonautomated migrations because we have no access to customer's database to run the update-database command.
The question is what's the best approach to keep the database and the model always up to date on the level of code ?
You have to use the migrate to latest version strategy. This apporach allows you to automatically update the database when the model is changed:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, MyMagicDatabaseConfiguration>()`);
public class MyMagicDatabaseConfiguration : DbMigrationsConfiguration<MyDbContext>
{
public MyMagicDatabaseConfiguration()
{
this.AutomaticMigrationsEnabled = true;
this.AutomaticMigrationDataLossAllowed = true;
}
}
// !!Force the initialization. this will execute the update!!
MyDb.Context.Database.Initialize(true);
This works fine if you are using MS SQL Server
The problem you are using MySql you have to do everything by your self!:
var migrator = new DbMigrator(new DbMigrationsConfiguration ());
migrator.Update();
// In the migrtaions directory:
public partial class MyMigration : DbMigration
{
public override void Up()
{
AddColumn("dbo.MyTable", "AnyName", c => c.Boolean(nullable: false));
}
public override void Down()
{
DropColumn("dbo.MyTable", "AnyName");
}
}
This is not an easy work and I do not recommeded you to do it. Just use SQL Server apporach this will safe your time.
one note more: Magic migration sometimes does not working(Complex changes with keys), if the changes can not be handled automatically.
You can also use migration to a target version:
var configuration = new DbMigrationsConfiguration();
var migrator = new DbMigrator(configuration);
migrator.Update("HereMigrationId");
var scriptor = new MigratorScriptingDecorator(migrator);
var migrationScript = scriptor.ScriptUpdate(sourceMigration: null, targetMigration: "HereMigrationId");
In your case you need migration to target version.
with the decrator you can modifiy the migration script during the migration.

Insert data into external MSSQL database through Yii Framework

I'm using Yii Framework to create my project. I need to export some data from MySQL (my project) to an external Microsoft SQL server which is on the same network.
Basically, the user needs to click on a button (which will do the export-insert) in my view and the results should be displayed - Success (if the query has been successful) or Failure (if something went wrong).
The results part is quite easy as I'll be using 'setFlash' to display the appropriate message but I want to know how to insert data into an external database through Yii.
Do you have any idea how this can be done?
Well, I agree with #SuVeRa on the first part of defining two db instances in the config.php but i don't think the sql Commands part is necessary (Plus i hate writing sql :D )
Instead you can do:
class SomeModel extends CActiveRecord
{
...
// Override the getDbConnection() function to use the ms sql db connection
public function getDbConnection()
{
return Yii::app()->ms_sql_db_connection; // The name of the connection in config.php
}
public function transfer()
{
// Here you can do all the transferring logic using normal Yii Active Record functions
}
}
Check out the docs on getDbConnection().

"Cannot drop database because it is currently in use". How to fix?

Having this simple code I get "Cannot drop database "test_db" because it is currently in use" (CleanUp method) as I run it.
[TestFixture]
public class ClientRepositoryTest
{
private const string CONNECTION_STRING = "Data Source=.;Initial Catalog=test_db;Trusted_Connection=True";
private DataContext _dataCntx;
[SetUp]
public void Init()
{
Database.SetInitializer(new DropCreateDatabaseAlways<DataContext>());
_dataCntx = new DataContext(CONNECTION_STRING);
_dataCntx.Database.Initialize(true);
}
[TearDown]
public void CleanUp()
{
_dataCntx.Dispose();
Database.Delete(CONNECTION_STRING);
}
}
DataContext has one property like this
public DbSet<Client> Clients { get; set; }
How can force my code to remove database?
Thanks
The problem is that your application probably still holds some connection to the database (or another application holds connection as well). Database cannot be deleted where there is any other opened connection. The first problem can be probably solved by turning connection pooling off (add Pooling=false to your connection string) or clear the pool before you delete the database (by calling SqlConnection.ClearAllPools()).
Both problems can be solved by forcing database to delete but for that you need custom database initializer where you switch the database to single user mode and after that delete it. Here is some example how to achieve that.
I was going crazy with this! I have an open database connection inside SQL Server Management Studio (SSMS) and a table query open to see the result of some unit tests. When re-running the tests inside Visual Studio I want it to drop the database always EVEN IF the connection is open in SSMS.
Here's the definitive way to get rid of Cannot drop database because it is currently in use:
Entity Framework Database Initialization
The trick is to override InitializeDatabase method inside the custom Initializer.
Copied relevant part here for the sake of good DUPLICATION... :)
If the database already exist, you may stumble into the case of having
an error. The exception “Cannot drop database because it is currently
in use” can raise. This problem occurs when an active connection
remains connected to the database that it is in the process of being
deleted. A trick is to override the InitializeDatabase method and to
alter the database. This tell the database to close all connection and
if a transaction is open to rollback this one.
public class CustomInitializer<T> : DropCreateDatabaseAlways<YourContext>
{
public override void InitializeDatabase(YourContext context)
{
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction
, string.Format("ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE", context.Database.Connection.Database));
base.InitializeDatabase(context);
}
protected override void Seed(YourContext context)
{
// Seed code goes here...
base.Seed(context);
}
}
This is a really aggressive database (re)initializer for EF code-first with migrations; use it at your peril but it seems to run pretty repeatably for me. It will;
Forcibly disconnect any other clients from the DB
Delete the DB.
Rebuild the DB with migrations and runs the Seed method
Take ages! (watch the timeout limit for your test framework; a default 60 second timeout might not be enough)
Here's the class;
public class DropCreateAndMigrateDatabaseInitializer<TContext, TMigrationsConfiguration>: IDatabaseInitializer<TContext>
where TContext: DbContext
where TMigrationsConfiguration : System.Data.Entity.Migrations.DbMigrationsConfiguration<TContext>, new()
{
public void InitializeDatabase(TContext context)
{
if (context.Database.Exists())
{
// set the database to SINGLE_USER so it can be dropped
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, "ALTER DATABASE [" + context.Database.Connection.Database + "] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
// drop the database
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, "USE master DROP DATABASE [" + context.Database.Connection.Database + "]");
}
var migrator = new MigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration>();
migrator.InitializeDatabase(context);
}
}
Use it like this;
public static void ResetDb()
{
// rebuild the database
Console.WriteLine("Rebuilding the test database");
var initializer = new DropCreateAndMigrateDatabaseInitializer<MyContext, MyEfProject.Migrations.Configuration>();
Database.SetInitializer<MyContext>initializer);
using (var ctx = new MyContext())
{
ctx.Database.Initialize(force: true);
}
}
I also use Ladislav Mrnka's 'Pooling=false' trick, but I'm not sure if it's required or just a belt-and-braces measure. It'll certainly contribute to slowing down the test more.
None of those solutions worked for me. I ended up writing an extension method that works:
private static void KillConnectionsToTheDatabase(this Database database)
{
var databaseName = database.Connection.Database;
const string sqlFormat = #"
USE master;
DECLARE #databaseName VARCHAR(50);
SET #databaseName = '{0}';
declare #kill varchar(8000) = '';
select #kill=#kill+'kill '+convert(varchar(5),spid)+';'
from master..sysprocesses
where dbid=db_id(#databaseName);
exec (#kill);";
var sql = string.Format(sqlFormat, databaseName);
using (var command = database.Connection.CreateCommand())
{
command.CommandText = sql;
command.CommandType = CommandType.Text;
command.Connection.Open();
command.ExecuteNonQuery();
command.Connection.Close();
}
}
I try adding Pooling=false like Ladislav Mrnka said but always got the error.
I'm using Sql Server Management Studio and even if I close all the connection, I get the error.
If I close Sql Server Management Studio then the Database is deleted :)
Hope this can helps
I got the same error. In my case, I just closed the connection to the database and then re-connected once the in my case the new model was added and a new controller was scaffolded. That is however a very simple solution and not recommended for all scenarios if you want to keep your data.
I got the same problem back then. Turns out the solution is to close the connection in Server Explorer tab in Visual Studio. So maybe you could check whether the connection is still open in the Server Explorer.
Its simple because u're still using the same db somewhere, or a connection is still open.
So just execute "USE master" first (if exist, but usually is) and then drop the other db. This always should work!
Grz John

Resources