I am using the Microsoft Data-Tier Application framework to create a deployment script based on a DacPackage object. I am attempting to use the Microsoft.SqlServer.Management.Smo.Server class to execute this script...
SqlConnection deployConnection = new SqlConnection(connBuilder.ToString());
deployConnection.Open();
Server server = new Server(new ServerConnection(deployConnection));
server.ConnectionContext.ExecuteNonQuery(deployScript);
However, this errors out with...
Unhandled Exception: Microsoft.SqlServer.Management.Common.ExecutionFailureException:
An exception occurred while executing a Transact-SQL statement or batch. --->
System.Data.SqlClient.SqlException: Incorrect syntax near ':'.
I know that the answer to this problem is that I need to be in SQLCMD mode, but I don't know how to tell my ServerConnection to execute in said mode.
I guess my problem isn't as specific as what I state in the title. What I really need to be able to do is execute the script generated from the DacPackage via the .Net framework. Can anyone help me with this?
SQLCMD mode commands are not T-SQL commands; they only work in SQL Server Management Studio (SSMS) / Visual Studio (VS) and SQLCMD.EXE. SQLCMD-mode is inherently how SQLCMD.EXE works and can be manually enabled in SSMS / VS; it is a part of those applications and not something that can be done via a provider.
Those applications interpret the SQLCMD-mode commands and do not pass them through to SQL Server. SQLCMD-mode commands are parsed/executed first (which is how they are able to affect the SQL that is about to be submitted) and then the final version of the SQL is submitted to SQL Server.
Hence, the deployment SQL scripts generated by SQL Server Data Tools (SSDT) / Visual Studio need to be run via one of these three programs.
Since you have a .dacpac file already, Microsoft provides a few ways to publish those that you should check out:
SqlPackage.exe and MSDeploy.exe. They are both described on the MSDN page for Project-Oriented Database Development using Command-Line Tools.
DacServices.Deploy(). This can be done in C# via the DacServices Class.
You can also create a publish SQL script via DacServices.GenerateDeployScript(), but this won't change the situation as stated above since the publish / deploy SQL script, whether generated from Visual Studio "Publish {project_name}" or GenerateDeployScript(), is the same script. Meaning, it will have the SQLCMD-mode colon-commands such as :setvar and :on error exit as well as the SQLCMD-mode variables, which at the very least will be $(DatabaseName) which is used in the following line:
USE [$(DatabaseName)];
While it is possible to comment out the initial :setvar lines by setting the DacDeployOptions property of CommentOutSetVarDeclarations to true, that will still leave the :on error exit line as well as a line for :setvar __IsSqlCmdEnabled "True" that is used to detect whether or not SQLCMD-mode has been enabled. Just above this particular :setvar line is a comment stating:
/*
Detect SQLCMD mode and disable script execution if SQLCMD mode is not supported.
To re-enable the script after enabling SQLCMD mode, execute the following:
SET NOEXEC OFF;
*/
So they really do intend that this script is only run via SQLCMD, whether through DOS -> SQLCMD.EXE or PowerShell -> Invoke-SqlCMD.
Technically, it is possible to generate a string of the deploy script contents (rather than to a stream) and manipulate that string by a) removing any colon-commands, and b) replacing "$(DatabaseName)" with whatever database you intend on deploying to. However, I have not tried this, I am not recommending this, and I am not sure it would work in all situations of what deployment scripts could be generated by SQL Server Data Tools. But it does seem like an option.
Also, minorly related: you don't need SMO to run SQL Scripts. SMO is means of interacting with SQL Server via objects rather than directly through T-SQL commands.
EDIT:
Links where others have tried this and found it did not work:
http://www.sqlservercentral.com/Forums/Topic1161673-22-1.aspx
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/7d3f64b0-f6ed-44ad-99cc-ce4c8324c09e/running-sqlcmd-commands-using-smodatabaseexecutenonquery?forum=sqlsmoanddmo
Possibilities for getting the generated publish SQL script to work programmaticaly:
C# via Process.Start to call SQLCMD.EXE -i filename.sql: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.start(v=vs.110).aspx
C# via open source library to handle "GO" statements and SQLCMD-mode colon-commands (currently handles SQL files, but can be easily updated to accept a string instead): https://github.com/rusanu/DbUtilSqlCmd
PowerShell via Invoke-SqlCMD: http://www.sqlservercentral.com/Forums/Topic1502697-1351-1.aspx
Related
After a few years, I have returned to writing in C# and I am really struggling here - I would like to have my app to have a local SQL database. I have added Service-based database and "Database1.mdf" was added to my project. I created a table and added some data just to see if it is working but I cannot connect to it. I tried numerous connection strings with no success (Server not accessible).
Do I need to run something else in the background? I thought that I might have a local database and with .NET client I can access it, and I hoped it would work whenever I bring my application (also not requiring any SQL server running). Is that wrong?
If you don't require any SQL server, take a look at SQLite. This is lite SQL database engine. Database is just one file. C# has a great library to SQLite on NuGet: https://www.nuget.org/profiles/mistachkin
SQLite is widely used, event in Android (as a native db engine).
here is what i use to connect. it appears as a Data Connection in Server Explorer.
string con2 = #"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=" + Application.StartupPath + "\\jobfile_2017.mdf;Integrated Security=True;Connect Timeout=30";
when i first started working with these, i used this source 1
it works on PC's that i have nothing installed (not even office) but as i said i'd be interested to know of any shortcomings of this method
I experiencing same problem and decided to move mdf file to static c:\db directory. Connection string was changed to incorporate new file location (AttachDbFile).
But AttachDbFile has some issues also (by some reason one table in db is inaccesible and error is access denied).
So I decided to move to Sqlite. LocalDb has many issues to work with. I read good note to resolve problem: in command line stop/remove/start name of instance. But it nuissance.
Wish you good luck to work with these db files.
I use SQL Server CLR Integration to create an ASSEMBLY.
Load Command:
CREATE ASSEMBLY TcpClr FROM 'G:\TcpClrTest.dll' WITH PERMISSION_SET = UNSAFE
Without App.Config
The dll code contains :
string ip=ConfigurationManager.AppSettings["connection"].ToString();
Also the App.config contains :
<appSettings>
<add key="connection" value="127.0.0.1"/>
</appSettings>
But when I execute the PROCEDURE,the SQL Server shows an error System.NullReferenceException
Does SQL Server CLR Integration support App.config files?
You need to place a sqlservr.exe.config file in the \Binn folder of that instance's root folder. For example:
C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Binn
If you are using SQL Server 2008 R2 (SP1) or newer, you should be able to find the exact location via the following query, which shows the full path to sqlservr.exe:
SELECT [filename] FROM sys.dm_server_services WHERE servicename LIKE N'SQL Server (%';
In your code, you need this line at the top:
using System.Configuration;
And then this will work:
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true)]
public static SqlString GetConfig(SqlString WhichOne)
{
ConfigurationManager.RefreshSection("connectionStrings");
return ConfigurationManager.ConnectionStrings[WhichOne.Value].ToString();
}
Contents of the sqlservr.exe.config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="Stuff" connectionString="Trusted_Connection=true; Enlist=false;" />
<add name="ClrTest" connectionString="boo hoo" />
</connectionStrings>
</configuration>
It is important to note that, as stated in the "Using an Application Configuration..." link, changes made to the config file are not immediately available. HOWEVER, you do not need to force a reload by doing one of the methods mentioned in that article (i.e. DBCC FREESYSTEMCACHE, and restarting SQL Server). All that is required to get current information is to reload the particular section you are using via ConfigurationManager.RefreshSection(string sectionName) as shown in the example above. Please see note below regarding usage and performance.
Resources:
Using System.Configuration.dll in .NET sprocs and UDFs
Using an Application Configuration (app.config/web.config) File in SQL Server CLR Integration
Also, unless you absolutely need to, you shouldn't create your assembly as UNSAFE. If you are just trying to make TCP connections to other machines, that should only require EXTERNAL_ACCESS.
USAGE AND PERFORMANCE
As suggested by Joe B in a comment below, there is a slight performance hit for the RefreshSection operation. If the code containing the refresh is going to be called more than once every couple of minutes, then it can have a noticeable impact (an impact that is unnecessary given the lack of frequency of a config file changing). In this case, you will want to remove the call to RefreshSection from the code that is called frequently and handle the refresh independently.
One approach would be to have a SQLCLR Stored Procedure or Scalar Function that just does the refresh and nothing else. This can be executed whenever a change it made to the config file.
Another approach would be to unload the App Domain which will reload the config file upon the next time any SQLCLR object in that database is referenced. One fairly simple method to reload all App Domains in a particular Database (but not across the entire Instance) is to flip the TRUSTWORTHY setting On and then Off again, or Off and then On again, depending on the current state of that setting. The code below will check the current state of that setting and flip it accordingly:
IF (EXISTS(
SELECT sd.*
FROM sys.databases sd
WHERE sd.[name] = DB_NAME() -- or N'name'
AND sd.[is_trustworthy_on] = 0
))
BEGIN
PRINT 'Enabling then disabling TRUSTWORTHY...';
ALTER DATABASE CURRENT SET TRUSTWORTHY ON;
ALTER DATABASE CURRENT SET TRUSTWORTHY OFF;
END;
ELSE
BEGIN
PRINT 'Disabling then enabling TRUSTWORTHY...';
ALTER DATABASE CURRENT SET TRUSTWORTHY OFF;
ALTER DATABASE CURRENT SET TRUSTWORTHY ON;
END;
Please do not use any of the more drastic methods -- DBCC FREESYSTEMCACHE, disabling then enabling the clr enabled system setting, restarting the Instance, etc -- as it is almost never necessary to do so. Especially restarting the Instance, or DBCC FREESYSTEMCACHE which drops all cached data for the entire Instance, which affects much more than just SQLCLR.
UPDATE REGARDING SQL SERVER ON LINUX
SQL Server is now, starting with version 2017, available on Linux (woo hoo!). However, it seems that reading from the app config file does not work on Linux. I have tried many combinations of sqlservr.exe.[Cc]onfig and sqlservr.[Cc]onfig, etc, and the like and have not gotten anything to work. Specifying a config file can't work as that requires EXTERNAL_ACCESS permission and only SAFE Assemblies are allowed on Linux (as of right now, at least). If I find a way to get it working I will post the details here.
You'll obviously be getting a NullReferenceException because ConfigurationManager.AppSettings["connection"] is returning null and then you are calling ToString() on a null.
Until you posted your question the top ranking page for a Google search on CLR Integration app.config was this
Using an Application Configuration (app.config/web.config) File in SQL Server CLR Integration.
In the associated article by Jonathan Kehayias, he states
A common part of programming in .NET is to use an configuration file
to store configuration information in an easily modifiable location.
The app.config or web.config file is an invaluable inclusion in most
.NET projects and developers may need to maintain this functionality
as a part of logic sharing between objects in the database and the
application as well. The problem is that SQL CLR doesn't allow use of
the System.Configuration class in SQLCLR projects.
He then goes on to detail a workaround which involves editing the project file to inject a new key into an ItemGroup that links your app.config to the project. From there you can do some basica wrangling from within your code to create a UserDefinedFunction to return the connection string or appSetting you require.
You are best to read the article posted by him at the aforementioned Url as it's not my area and I would just end up blatantly plagiarizing his content to provide a step by step of how to do.
Are you trying to access the same SQL Server that the CLR is running on? If so, you can use a special type of connection string called a "context connection".
https://msdn.microsoft.com/en-us/library/ms131053.aspx
using(SqlConnection connection = new SqlConnection("context connection=true"))
{
connection.Open();
// Use the connection
}
Basically you tell the code that it should use the underlying SQL Server (in that context) and you won't even need to specify connection info. If you're trying to run arbitrary reusable code as a CLR, you're probably committing some sort of software crime and should just not do that.
This is how I did it. It's working perfectly for me, and I can install my assembly on any instance now and it will work perfectly.
SqlConnectionStringBuilder sqlb = new SqlConnectionStringBuilder("");
sqlb.ContextConnection = true;
string newConnStr = sqlb.ToString();
using (SqlConnection c = new SqlConnection(newConnStr))
{
c.Open();
//This is the database name
DatabaseName = c.Database;
//We need to use some simple SQL to get the server and instance name.
//Returned in the format of SERVERNAME\INSTANCENAME
SqlCommand cmd = new SqlCommand("SELECT ##SERVERNAME [Server]", c);
SqlDataReader rdr = cmd.ExecuteReader();
if (rdr.Read())
{
ServerName = rdr["Server"].ToString();
}
}
I'm using C# in Visual Studio 2008 to loop through MDF Files on my PC and extract data from within them. I'm using a Table Adapter to point to the local MDF file.
Recently one of my PC's refuses to let me attach any new Data Source as it says
System.Data.SqlClient.SqlException: Unable to create/attach any new database because the number of existing databases has reached the
maximum number allowed: 32766
Even if I start a fresh Windows application and try to add an MDF file (on my desktop) to it as a Data Source, I get the above error.
Can anyone tell me how to remove/delete the existing connections ?
My code works fine on another PC and I've re-installed Visual Studio on my PC but still get this error.
C# Table adapter code:
tmTableAdapter.Connection.ConnectionString = "Data Source=.\\SQLEXPRESS;AttachDbFilename='" + pathofmdffile + "';Integrated Security=True;Connect Timeout=30;User Instance=True";
tmTableAdapter.Connection.Open();
Reinstalling VS won't help as the error is coming from SQL Server.
Look at these MSDN pages which have lots of good info:
SQL Server Express User Instances
SQL Server 2005 Express Edition User Instances
User Instances for Non-Administrators
Since you are using "User Instances", I guess the DBs won't show up when connecting to the main SQLEXPRESS instance.
Things to check:
In SSMS, connect to your last attached DB by doing the following:
Go to Object Explorer
New Connection to a Database Engine
In the "Connect to Server" popup:
Click the "Options >>" button
Go to the "Additional Connection Properties" tab
Enter in the following in the text area:
User Instance = true
Click the "Connect" button
Expand "Databases" folder. You should see them all, named as the full path to the MDF file
Right-click on a database
Go to "Tasks >"
First option is "Detach"
Obviously this isn't practical for 32,766 databases, but for a few it is the best option.
By default the sqlservr.exe process hangs around for 60 minutes, and any databases you attach prior to it terminating gets added to the list and likely reset the expiration counter. You can end the process immediately by connecting to the most recent attached DB (as noted above; those steps will work for a New Query, or if connecting via Object Explorer, then right-click on the instance name and go to "New Query"), and run the following:
SHUTDOWN;
This will clear out all connected databases in one shot.
Set the timeout to be less than 60 minutes. According to the SQL Server Express User Instances page (same link as above):
A system administrator on the parent instance can set the duration of the time-out period for a user instance by using sp_configure to change the user instance timeout option. The default is 60 minutes.
In SSMS (make sure you are connected to the parent instance):
-- View current setting (in the "run_value" field)
EXEC sp_configure 'user instance timeout'
-- Documentation says default is 60 but mine was 5
-- If you can't see the option, run the following:
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
-- To change it, run the following:
EXEC sp_configure 'user instance timeout', 5;
Use the "SQL Server Express Utility" to detach one or more databases:
SSEUtil.exe was written in 2005 and has not been updated since (current version is v1.0.2130)
Download from: https://www.microsoft.com/en-us/download/confirmation.aspx?id=3990
Use the -d[etach] <dbpath[*]>|name=<dbname> command
Look in the following directory which will have the 4 system DBs:
On XP / Vista: C:\Documents and Settings{UserName}\Local Settings\Application Data\Microsoft\Microsoft SQL Server Data\SQLEXPRESS
On Windows 7 and newer: C:\Users{UserName}\AppData\Local\Microsoft\Microsoft SQL Server Data\SQLEXPRESS
This is mostly just informational as these files cannot be deleted so long as the SQL Server process is running.
For an on-going basis, if you will be attaching many DBs in a loop, you can programatically get rid of them in that process by running sp_detach_db when you are done using each DB:
USE [master];
ALTER DATABASE [{DatabaseName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
EXEC sp_detach_db #dbname =N'{DatabaseName}';
And just FYI, "User Instances" are now deprecated. You should look into using SQL Server Express LocalDB.
I have an old system which generated me a database in .CDB extension (i run on Firebird-1.5.6.5026-0-Win32) and i can access this database in IBExpert to query and stuff. But i need to write an application in .NET (VS 2010 4.0 framaework) so i can read this database and access some of the data to insert into a table inside SQLServer.
I tried many things, changed the server version and other things but i now all i get is ''Cannot find fbembed.dll'' exception error while trying to open the connection. My FB server doesnt have this file since he uses the 'fbclient.dll' already.
Any thoughts on how to connect my application to this .CDB database?
(this firebird version is the same that the legacy system is running, so i used the 1.7RC firebird .net provider within this server)
The connection string used is:
<add name="FirebirdConnectionString" connectionString="User=SYSDBA;Password=masterkey;
Database=localhost:C:\temp\BD\ECLECTIC.CDB;DataSource=localhost;Port=3051;
Dialect=3;Charset=NONE;Role=;Connection lifetime=15; Pooling=false;
MinPoolSize=0; MaxPoolSize=50; Packet Size=8192; ServerType=1;"
providerName="FirebirdSql.Data.FirebirdClient"/>
Unless you really want to use Firebird embedded (which you don't as you also specify localhost), you should not specify ServerType=1, but either leave it out entirely or set ServerType=0.
As to your other problem you mention in the comments, I suggest you check if this solves it and otherwise create a new question with more information.
I am developing a small business application which uses Sqlserver 2005 database.
Platform: .Net framework 3.5;
Application type: windows application;
Language: C#
Question:
I need to take and restore the backup from my application. I have the required script generated from SSME.
How do I run that particular script (or scripts) from my winform application?
You can run these scripts the same way you run a query, only you don't connect to the database you want to restore, you connect to master instead.
If the machine where your application is running has the SQL Server client tools installed, you can use sqlcmd.
If you want to do it programatically you can use SMO
Tutorial
Just use your connection to the database (ADO I presume?) and send your plain TSQL instructions to the server through this connection.
For the backup you probably want to use xp_sqlmaint. It has the handy ability to remove old backups, and it creates a nice log file. You can call it via something like:
EXECUTE master.dbo.xp_sqlmaint N''-S "[ServerName]" [ServerLogonDetails] -D [DatabaseName] -Rpt "[BackupArchive]\BackupLog.txt" [RptExpirationSchedule] -CkDB -BkUpDB "[BackupArchive]" -BkUpMedia DISK [BakExpirationSchedule]''
(replace the [square brackets] with suitable values).
Also for the backup you may need to backup the transaction log. Something like:
IF DATABASEPROPERTYEX((SELECT db_name(dbid) FROM master..sysprocesses WHERE spid=##SPID), ''Recovery'') <> ''SIMPLE'' EXECUTE master.dbo.xp_sqlmaint N'' -S "[ServerName]" [ServerLogonDetails] -D [DatabaseName] -Rpt "[BackupArchive]\BackupLog_TRN.txt" [RptExpirationSchedule] -BkUpLog "[BackupArchive]" -BkExt TRN -BkUpMedia DISK [BakExpirationSchedule]''
I'd recommend storing the actual commands you're using in a database table (1 row per command) and use some sort of template replacement scheme to handle the configurable values. This would allow for easy changes to the commands, without needing to deploy new code.
For the restore you will need to kill all connections except for internal sql server ones. Basically take the results of "exec sp_who" and for rows that match on dbname, and have a status that is not "background", and a cmd that is not one of "SIGNAL HANDLER", "LOCK MONITOR", "LAZY WRITER", "LOG WRITER", "CHECKPOINT SLEEP" do a "kill" on the spid (eg: ExecuteNonQuery("kill 1283")).
You'll want to trap and ignore any exceptions from the KILL command. There's nothing you can do about them. If the restore cannot proceed because of existing connections it will raise an error.
One danger with killing connections is ADO's connection pool (more for asp.net apps than windows apps). ADO assumes the a connection fetched from the connection pool is valid... and it does not react well to connections that have been killed. The next operation that occurs on that connection will fail. I can't recall the error... you might be able to trap just that specific error and handle it... also with 3.5 I think you can flush the connection pool (so... trap the error, flush the connection pool, open the connection, try the command again... ugly but might be doable).