I am trying to return the physical file path of a database's mdf/ldf files.
I have tried using the following code:
Server srv = new Server(connection);
Database database = new Database(srv, dbName);
string filePath = database.PrimaryFilePath;
However this throws an exception "'database.PrimaryFilePath' threw an exception of type 'Microsoft.SqlServer.Management.Smo.PropertyNotSetException' - even though the database I'm running this against exists, and its mdf file is located in c:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL
What am I doing wrong?
Usually the problem is with the DefaultFile property being null. The default data file is where the data files are stored on the instance of SQL Server unless otherwise specified in the FileName property. If no other default location has been specified the property will return an empty string.
So, this property brings back nothing (empty string) if you didn't set the default location.
A workaround is to check the DefaultFile property, if it returns an empty string use SMO to get the master database then use the Database.PrimaryFilePath property to retrieve the Default Data File Location (since it hasn't changed)
Since you say the problem is with your PrimaryFilePath:
Confirm that your connection is open
Confirm that other properties are available
This is how I do it, prepared for multiple file names. Access database.LogFiles to get the same list of log file names:
private static IList<string> _GetAttachedFileNames(Database database)
{
var fileNames = new List<string>();
foreach (FileGroup group in database.FileGroups)
foreach (DataFile file in group.Files)
fileNames.Add(file.FileName);
return fileNames;
}
Server srv = new Server(connection);
DatabaseCollection dbc = svr.Databases;
Database database = dbc["dbName"];
string filePath = database.PrimaryFilePath;
I think the easiest approach would be to run sql script on your sql server instance which will always return you correct data and log file paths. The following sql will do the trick
SELECT
db.name AS DBName,
(select mf.Physical_Name FROM sys.master_files mf where mf.type_desc = 'ROWS' and db.database_id = mf.database_id ) as DataFile,
(select mf.Physical_Name FROM sys.master_files mf where mf.type_desc = 'LOG' and db.database_id = mf.database_id ) as LogFile
FROM sys.databases db
order by DBName
You can still execute this sql using SMO if you want to, which will return you a dataset and then you can extract that information.
var result = new List();
var server = new Server( serverInstanceName );
var data = server.Databases[ "master" ].ExecuteWithResults(sql);
foreach ( DataRow row in data.Tables[ 0 ].Rows )
result.Add( new DatabaseInfo( row[ "DBName" ].ToString(), row[ "DataFile" ].ToString(), row[ "LogFile" ].ToString() ) );
return result;
If you will use this snippet then make sure to create a DatabaseInfo class which will store the information returned from Sql server instance.
using Smo = Microsoft.SqlServer.Management.Smo;
public string GetDataBasePath(string strDatabaseName)
{
ServerConnection srvConn = new ServerConnection();
srvConn.ConnectionString = "<your connection string goes here>";
Server srv = new Server(srvConn);
foreach (Smo.Database db in srv.Databases)
{
if (string.Compare(strDatabaseName, db.Name, true) == 0)
{
return db.PrimaryFilePath;
}
}
return string.Empty;
}
Related
Let's say I inserted a file into a varbinary(max) field like so:
CREATE TABLE myTable
(
FileName nvarchar(60),
FileType nvarchar(60),
Document varbinary(max)
)
GO
INSERT INTO myTable(FileName, FileType, field_varbinary)
SELECT
'Text1.txt' AS FileName,
'.txt' AS FileType,
*
FROM
OPENROWSET(BULK N'C:\Text1.txt', SINGLE_BLOB) AS Document
GO
Of course my file now looks like this:
0xFFD8FFE000104A46494600010101004800....
Is there a simple and elegant way to retrieve this file?
My preference is to open it right away in a temp folder rather than saving and then viewing and deleting. In MS Access, this is as simple as using an Attachment field and double clicking to upload/download.
Since there is no built-in functionality in SSMS for this task I usually write a simple LINQPad script that extracts my varbinary column and writes it to the file system.
Something like this:
var results = from p in myTable
where p.ID == ... //your condition here
select p;
foreach (var item in results)
{
File.WriteAllBytes("C:\\" + item.FileName + item.FileType , item.Document.ToArray());
}
I am working with C# and ASP.NET, and I ended up doing this using a Generic Handler, later linking to it elsewhere in my website project:
public class ImageProvider : IHttpHandler {
public string connString = "...";
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/jpeg";
string sqlSelectQuery = "select img from table"
SqlConnection conn = new SqlConnection(connString);
conn.Open();
SqlCommand cmd = new SqlCommand(sqlSelectQuery, conn);
byte[] img = (byte[])cmd.ExecuteScalar();
context.Response.BinaryWrite(img);
}
I have used SQLdependency with SignalR to show alerts to users.. The code is as follows:
public IEnumerable<AlertInfo> GetData(long UserId)
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["yafnet"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT [AlertID],[AlertNote],[AlertDetails],[AlertDate],[Location]
FROM [dbo].[Alerts] where [UserID]=" + UserId + " AND [IsViewed]=0", connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
SqlDependency.Stop(ConfigurationManager.ConnectionStrings["yafnet"].ConnectionString);
SqlDependency.Start(ConfigurationManager.ConnectionStrings["yafnet"].ConnectionString);
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
using (var reader = command.ExecuteReader())
return reader.Cast<IDataRecord>()
.Select(x => new AlertInfo()
{
AlertID = x.GetInt64(0),
AlertNote = x.GetString(1),
AlertDetails = x.GetString(2),
AlertDate = x.GetDateTime(3),
Location = x.GetString(4)
}).ToList();
}
}
}
It is working fine on localhost. But after uploading to Azure server, this method throws the following error:
Message":"An error has occurred.","ExceptionMessage":"Statement 'RECEIVE MSG' is not supported
in this version of SQL Server.","ExceptionType":"System.Data.SqlClient.SqlException","StackTrace":"
\r\nServer stack trace: \r\n at System.Data.SqlClient.SqlConnection.OnError(SqlException exception
, Boolean breakConnection, Action`1 wrapCloseInAction)
What could be the issue?
Actually your SQL Server database must have is_broker_enabled = 1.
You need to check whether it's enabled or not.
To verify this, use the command SELECT name, is_broker_enabled FROM sys.databases.
If your database shows result "1" it's okay and if "0" then you must enable it using this command ALTER DATABASE yourdb SET ENABLE_BROKER.
But the bad news is Azure SQL database shows it enabled but it no longer supports is_broker_enabled.
To do this, you need to install the full instance of SQL Server to Azure VM.
If I restore from Sql Server no problem, but if I do it through my application, the database is stuck in "restoring state".
I found some advice saying to put noRecovery = false, but this didn't change anything.
If I remove the "with move" option, it works: after the restore the DB is in a normal state.
The thing that I would like to understand is: does "with move" modify a sql server table?
Because if I launch the restore the first time without "with move" it says that he could not find the specified path. Otherwise, if I launch the restore with this option, and then one second time without it, it works. So there must be some tables that sql server uses to map the logical name with a physical path, how can I modify this table?
Here is the code:
SqlConnection sqlConnection = new SqlConnection(string.Format("Data Source={0};Initial Catalog={1};Integrated Security=True", database.SqlServerId, database.Name));
ServerConnection connection = new ServerConnection(sqlConnection);
Server sqlServer = new Server(connection);
Restore rstDatabase = new Restore();
rstDatabase.Action = RestoreActionType.Database;
rstDatabase.Database = backupFile.Name;
BackupDeviceItem bkpDevice = new BackupDeviceItem(backupFile.FileName, DeviceType.File);
rstDatabase.Devices.Add(bkpDevice);
rstDatabase.ReplaceDatabase = true;
rstDatabase.NoRecovery = false;
string dbLogicalName = "";
string logLogicalName = "";
sqlConnection.Open();
SqlCommand command = new SqlCommand(string.Format("RESTORE FILELISTONLY FROM DISK = '{0}'", backupFile.FileName), sqlConnection);
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
if (reader.GetString(2) == "D")
dbLogicalName = reader.GetString(0);
if (reader.GetString(2) == "L")
logLogicalName = reader.GetString(0);
}
}
reader.Close();
rstDatabase.RelocateFiles.Add(new RelocateFile(dbLogicalName, backupFile.DatabaseFile));
rstDatabase.RelocateFiles.Add(new RelocateFile(logLogicalName, backupFile.LogsFile));
//Restore
rstDatabase.SqlRestore(sqlServer);
rstDatabase.Devices.Remove(bkpDevice);
sqlConnection.Close();
connection.Disconnect();
The thing that I would like to understand is: does "with move" modify a sql server table?
Not necessarily, unless you modify the default databases path in the instance, which you could do in SSMS following these steps:
https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/view-or-change-the-default-locations-for-data-and-log-files
But as someone said, try to fix that on your own application. Do not attempt to modify this on the system database. It could bring unexpected results if you do that.
I think you know that, but WITH MOVE tells the SQL Server to reallocate the restored database in different path than the default path.
I tried a few solutions and below is the most straight forward one but I get the error below
Logical file 'RestoredProcessMananger' is not part of database 'RestoredProcessMananger'. Use RESTORE FILELISTONLY to list the logical file names.
RESTORE DATABASE is terminating abnormally.
What am I doing wrong ? The idea is to create DLL to be used by other programs the allow a baseline database to be reloaded on a server overwriting whatever is there..
ServerConnection connection = new ServerConnection("xxx", "sa", "srv$xxx");
Server svr =new Server(connection);
Restore res = new Restore();
res.Database = "RestoredProcessMananger";
res.Action = RestoreActionType.Database;
res.Devices.AddDevice(#"C:\temp\ProcessManager.bak", DeviceType.File);
res.ReplaceDatabase = true;
res.RelocateFiles.Add(new RelocateFile("RestoredProcessMananger", _
#"c:\ProcessManager2.mdf"));
res.RelocateFiles.Add(new RelocateFile("RestoredProcessMananger_Log", _
#"c:\ProcessManager2_log.ldf"));
res.SqlRestore(svr);
svr.Refresh();
EDIT 1: fixed
public static void RestoreDatabase(string Server, //sqlserver //from CONFIG
string BackupFilePath, //where the bak file I want to restore //from CONFIG
string destinationDatabaseName, //what the restored database will be called //from CONFIG
string DatabaseFolder, //where the data/log files for the destination (break into 2 variables (2 different locations)) (get from GetDatabaseMDFFilePathName)
string DatabaseFileName, //the destination MDF file name (get from GetDatabaseMDFFilePathName)
string DatabaseLogFileName) //the destination LDF file name (get from GetDatabaseMDFFilePathName)
{
Server myServer = GetDatabases("xxx");
Restore myRestore = new Restore();
myRestore.Database = destinationDatabaseName;
Database currentDb = myServer.Databases[destinationDatabaseName];
if (currentDb != null)
myServer.KillAllProcesses(destinationDatabaseName);
myRestore.Devices.AddDevice(BackupFilePath, DeviceType.File);
string DataFileLocation = DatabaseFolder + "\\" + destinationDatabaseName + ".mdf";
string LogFileLocation = DatabaseFolder + "\\" + destinationDatabaseName + "_log.ldf";
myRestore.RelocateFiles.Add(new RelocateFile(DatabaseFileName, DataFileLocation));
myRestore.RelocateFiles.Add(new RelocateFile(DatabaseLogFileName, LogFileLocation));
myRestore.ReplaceDatabase = true;
myRestore.PercentCompleteNotification = 10;
myRestore.SqlRestore(myServer);
currentDb = myServer.Databases[destinationDatabaseName];
currentDb.SetOnline();
}
The error indicates that RestoredProcessMananger is not the name of a logical file contained in your backup file.
Have you used RESTORE FILELISTONLY ... to list the files contained in the .BAK file?
For instance, if your backup file is named C:\MyBackupFile.bak you would need to run the following query from SQL Server Management Studio (or SQLCMD, etc):
RESTORE FILELISTONLY FROM DISK='C:\MyBackupFile.bak';
This will provide a list of the logical files contained in the backup file. You will then need to pass the name of one of these files into your RelocateFile code.
I created a empty project in ASP.Net and then I manually add it view,controller and model. Now I want to connect SQL Server to Model class. How can I do it ?
try with this code, this code contain select query for just example
// Is your string connection to your database and server
string connectionString = ""; //connection to your database
//Create a connection to your database
//using bloc , in order to destruct connection object in the end of treatment
using (SqlConnection connection = new SqlConnection(connectionString))
{
//Create object command who contain your query sql, permit you to get data or //insert on update or delete data
using (SqlCommand command= connection.CreateCommand())
{
//for example a query, who select data from table in your databse,
command.CommandText = "SELECT * FROM Table where col = #ParameterTest";
//for exxample a parameter for your query
command.Parameters.Add("#ParameterTest", 123); //your test value
//after create connection you must open this
command.Connection.Open();
//get data in reader, structure for readin datas
var reader = command.ExecuteDataReader();
}
}