SQLServer UnitTesting in Visual Studio - DROP DATABASE [MyDb] - sql-server

I've set up a Unit Test project in Visual studio for my SQL Server project.
The test itself work, and the setup includes deploying that database. My problem is that I want to have a "clean slate" test, and every time I run a test, the data accumulates.
I tried manually calling a 'DROP DATABASE' from the SqlDatabaseSetup.cs, but it seems that I don't have a DataConnection at this point.
[TestClass()]
public class SqlDatabaseSetup
{
[AssemblyInitialize()]
public static void InitializeAssembly(TestContext ctx)
{
var q = ctx.DataConnection.CreateCommand();
q.CommandText = "DROP DATABASE MyDb;";
q.ExecuteNonQuery();
// Setup the test database based on setting in the
// configuration file
SqlDatabaseTestClass.TestService.DeployDatabaseProject();
SqlDatabaseTestClass.TestService.GenerateData();
}
}
Is there anyway to indicate that the DB should be flushed first (without manually calling DELETE FROM XX for every table), or is there a way I can pass through a command to do that for me?

The solution was as follows :
[AssemblyInitialize()]
public static void InitializeAssembly(TestContext ctx)
{
// Setup the test database based on setting in the
// configuration file
try
{
var conn = SqlDatabaseTestClass.TestService.OpenExecutionContext();
var cmd = conn.Connection.CreateCommand();
cmd.CommandText = "use master; ALTER DATABASE [MyTestDb] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;DROP DATABASE MyTestDb;";
cmd.ExecuteNonQuery();
}
catch
{
}
SqlDatabaseTestClass.TestService.DeployDatabaseProject();
SqlDatabaseTestClass.TestService.GenerateData();
}

Related

How to rollback transactions SQL Server Scripts?

I am using DbUp (Documentation) package to maintain and execute scripts on the database.
Currently, I am using
var builder = DeployChanges.To
.SqlDatabase(connectionString)
.WithExecutionTimeout(TimeSpan.FromSeconds(300))
.WithTransactionPerScript()
.WithScriptsFromFileSystem(rootPath, new FileSystemScriptOptions { IncludeSubDirectories = true })
I want to rollback all the transactions if any scripts fails while executing a bunch of scripts.
Using,
WithTransactionAlwaysRollback()
Transaction rolls back even if the all the scripts execute successfully. I only want to rollback on failure.
Is there another way to solve this problem?
Use
.WithTransaction()
instead of
.WithTransactionPerScript()
And here's an implementation of these extension methods:
public static UpgradeEngineBuilder WithTransaction(this UpgradeEngineBuilder builder)
{
builder.Configure(delegate (UpgradeConfiguration c)
{
c.ConnectionManager.TransactionMode = TransactionMode.SingleTransaction;
});
return builder;
}
public static UpgradeEngineBuilder WithTransactionPerScript(this UpgradeEngineBuilder builder)
{
builder.Configure(delegate (UpgradeConfiguration c)
{
c.ConnectionManager.TransactionMode = TransactionMode.TransactionPerScript;
});
return builder;
}

Unable to restore database

I'm trying to write a program that will restore many databases, query them using LINQ and then write recieved data to my database.
The first problem I've encountered is that I can't restore any database programatically.
The error says something like this:
"File 'xxx' cannot be restored to 'C:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\DATA\xxx.mdf'. Use WITH MOVE to identify a valid location for the file."
I've checked and that's indeed a problem. I haven't got this folder (\MSSQL11.SQLEXPRESS). How to resolve this?
My code for restoring:
private void RestoreDB(string destPath, string dbName, string fileName)
{
var myServer = new Server(#"LOCALHOST");
var res = new Restore();
res.Database = dbName;
res.Action = RestoreActionType.Database;
res.Devices.AddDevice(destPath, DeviceType.File);
res.PercentCompleteNotification = 10;
res.ReplaceDatabase = true;
try
{
res.SqlRestore(myServer);
}
catch (Exception e)
{
Console.WriteLine($"{dbName} couldn't be restored");
return;
}
}
The thing is, I've recently migrated from once PC to another. It worked before but I recall that I've had some problems with SQL before as well. I've read something about this error but I need explanation step by step how to deal with it.

Entity framework connects to master database for contained database user

I have a contained database user. Since it's contained in the database it's not allowed to connect to any other database including master. Unfortunately Entity Framework seems to connect to the master database anyway.
I've created a new console app with the latest Entity Framework nuget (6.2.0) to make sure nothing else connects to the master database:
static void Main(string[] args)
{
var connectionString = "Server=sql-azure.database.windows.net;Database='Database';User ID=Username;Password=password;Trusted_Connection=False;";
using (var dbContext = new DbContext(connectionString))
{
dbContext.Database.CommandTimeout = 10 * 60;
dbContext.Database.ExecuteSqlCommand("EXEC cleanup #Date", new SqlParameter("#Date", DateTime.UtcNow.AddMonths(-3)));
}
}
How do I force Entity Framework to not connect to the master database? I get failures in the audit logs on the master database which causes azure threat detection to go off.
After researching some more I've disabled the database initializer before the using statement like this:
Database.SetInitializer<DbContext>(null);
With this line of code, the console app doesn't connect to the master database any more. More info about Database.SetInitializer(null).
Full example:
static void Main(string[] args)
{
var connectionString = "Server=sql-azure.database.windows.net;Database='Database';User ID=Username;Password=password;Trusted_Connection=False;";
Database.SetInitializer<DbContext>(null);
using (var dbContext = new DbContext(connectionString))
{
dbContext.Database.CommandTimeout = 10 * 60;
dbContext.Database.ExecuteSqlCommand("EXEC cleanup #Date", new SqlParameter("#Date", DateTime.UtcNow.AddMonths(-3)));
}
}
I can't repro that:
Contained user:
//create user joe with password ='xxxxxx'
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
namespace Ef6Test
{
class Program
{
static void Main(string[] args)
{
var connectionString = "server=xxxxxxxxx.database.windows.net;database=adventureworks;uid=joe;pwd=xxxx";
using (var dbContext = new DbContext(connectionString))
{
dbContext.Database.CommandTimeout = 10 * 60;
Console.WriteLine(dbContext.Database.SqlQuery<string>("select db_name() dbname;").Single());
}
Console.WriteLine("Hit any key to exit");
Console.ReadKey();
}
}
}
The below would work for MS SQL on premises as well as if you are on SQL Azure:
use master;
CREATE LOGIN DemoUser WITH PASSWORD = 'DemoPassword';
GO
CREATE USER DemoUser FOR LOGIN DemoUser
GO
use DemoDB;
CREATE USER DemoUser FOR LOGIN DemoUser WITH DEFAULT_SCHEMA=[dbo]
GO
ALTER ROLE db_datareader ADD MEMBER DemoUser;
ALTER ROLE db_datawriter ADD MEMBER DemoUser;

Setup Database before run ui coded tests on Visual Studio 2010

I'm automating UI tests to my Silverlight App and I'm using Visual Studio 2010 for it. Some tests required a setup to my Oracle Database.
Things i've done:
1 - A setup.sql file where I connect to my Database and perform actions on it. I had this file as an existing item to my project and I add as a Deployment on TestSettings.
Code:
CONNECT USERNAME#DATABASE,
CREATE TABLE TABLE_NAME,
EXIT
2 - A set.bat file where I call the setup.sql file. I had the path of this file on Setup and Cleanup tab on TestSetings.
Code:
sqlcmd -S MARIALISBOA -i setup.sql
3 - I wrote a TestInitilize method on my TestClass.
Code:
[TestInitialize()]
public void Initialize()
{
System.Diagnostics.Process.Start("setup.bat");
}
4 - I connected do my Database throw Visual Studio (Data -> Add New Data Source).
I run a test on Visual Studio but the class isn't created on my database.
Could anyone help me? I'm trying to solve this problem since Monday and I starting to lose my mind
While it does not solve your initial problem, a solution would be to use something similiar to this;
Do not create the table within your tests. this should be created on install of the Test Environment
Clear down the table for each test you want to do using a Helper Method within the test.
For example (Please note that this is SQL Server, use OLE DB connection or similiar);
internal static object FireSqlStatement(string sqlStatement)
{
object result = null;
using (var cn = new SqlConnection(ConfigurationManager.ConnectionStrings[connectionString].ConnectionString))
{
cn.Open();
var cmd = new SqlCommand
{
CommandText = sqlStatement,
CommandType = CommandType.Text,
Connection = cn
};
result = cmd.ExecuteScalar();
cmd.Dispose();
cn.Close();
}
return result;
}
An example of how I use this within my test;
[Test]
public void GetUserTriggers()
{
//Insert Test Record
Helper.FireSqlStatement("INSERT INTO [Users].[Triggers] (Id) VALUES (1)");
var request = new GetTriggersRequest()
{
TriggerType = TriggerType.UserTrigger
};
var response = ServiceInvoker.Invoke<IReports,
GetTriggersRequest, GetTriggersResponse>(
"Reports",
request,
(proxy, req) => proxy.GetTriggers(req));
foreach (var t in response.Triggers)
{
Console.WriteLine(t.Id.ToString());
}
Assert.NotNull(response);
Assert.NotNull(response.Triggers);
Assert.Greater(response.Triggers.Count, 0);
}
In your case, you could call;
Helper.FireSqlStatement("TRUNCATE TABLE tableName");
Any good?

"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