Lars H
Lars H

Reputation: 173

Problems with RelocateFile property in the Restore-SqlDatabase cmdlet

I trying to restore a database by using the Restore-SqlDatabase cmdlet. I need to relocate the files but I'm getting the following errror

Restore-SqlDatabase : Cannot bind parameter 'RelocateFile'. Cannot convert the 
"Microsoft.SqlServer.Management.Smo.RelocateFile" value of type 
"Microsoft.SqlServer.Management.Smo.RelocateFile" to type 
"Microsoft.SqlServer.Management.Smo.RelocateFile".
At line:25 char:108
+ ... e -RelocateFil $RelocateData
+                    ~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Restore-SqlDatabase], ParameterBindingException
+ FullyQualifiedErrorId CannotConvertArgumentNoMessage,Microsoft.SqlServer.Management.PowerShell.RestoreSqlDatabaseCommand

My powershell code look like this

$RelocateData = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile("MyDB_Data", "c:\data\MySQLServerMyDB.mdf") 
$RelocateLog = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile("MyDB_Log", "c:\data\MySQLServerMyDB.ldf") 
$file = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile($RelocateData,$RelocateLog) 
$myarr=@($RelocateData,$RelocateLog)
Restore-SqlDatabase -ServerInstance DEV\DEMO -Database "test" -BackupFile $backupfile -RelocateFile $myarr

Upvotes: 17

Views: 19811

Answers (6)

sitecorepm
sitecorepm

Reputation: 101

Used @Linhares solution except the Snapin assembly's version of 15.0.0.0 did not match the referenced Microsoft.SqlServer.SmoExtended version 15.100.0.0.

So tweaked this line to get the version directly from the referenced assembly.

$sqlServerSnapinVersion = ((Get-Command Restore-SqlDatabase).ImplementingType.Assembly.GetReferencedAssemblies() | ? { $_.Name -eq "Microsoft.SqlServer.SmoExtended" }).Version.ToString()

Upvotes: 5

Heiner
Heiner

Reputation: 2056

I had the same problem on a build agent where no SQL Server and no Mangement Studio is installed. Only PS module "SqlServer" is available.

Just adding the following line at the beginning of the script solved the issue for me.

(Get-Command Restore-SqlDatabase).ImplementingType.Assembly

After that the assembly is loaded and all types can be used.

Upvotes: 1

Rosberg Linhares
Rosberg Linhares

Reputation: 3687

You can do this in a version-independent way:

$sqlServerSnapinVersion = (Get-Command Restore-SqlDatabase).ImplementingType.Assembly.GetName().Version.ToString()
$assemblySqlServerSmoExtendedFullName = "Microsoft.SqlServer.SmoExtended, Version=$sqlServerSnapinVersion, Culture=neutral, PublicKeyToken=89845dcd8080cc91"

$RelocateData = New-Object "Microsoft.SqlServer.Management.Smo.RelocateFile, $assemblySqlServerSmoExtendedFullName"('MyDB_Data', 'c:\data\MySQLServerMyDB.mdf')
$RelocateLog = New-Object "Microsoft.SqlServer.Management.Smo.RelocateFile, $assemblySqlServerSmoExtendedFullName"('MyDB_Log', 'c:\data\MySQLServerMyDB.ldf')
$myarr=@($RelocateData,$RelocateLog)
Restore-SqlDatabase -ServerInstance DEV\DEMO -Database "test" -BackupFile $backupfile -RelocateFile $myarr

Upvotes: 21

Samuel Dufour
Samuel Dufour

Reputation: 191

For solution #1, you need to specify assembly qualified name when you instanciate relocate file to use correct assembly.

$RelocateData = New-Object 'Microsoft.SqlServer.Management.Smo.RelocateFile, Microsoft.SqlServer.SmoExtended, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91' -ArgumentList "MyDB_Data", "c:\data\MySQLServerMyDB.mdf"
$RelocateLog = New-Object 'Microsoft.SqlServer.Management.Smo.RelocateFile, Microsoft.SqlServer.SmoExtended, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91' -ArgumentList "MyDB_Log", "c:\data\MySQLServerMyDB.ldf"
$file = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile($RelocateData,$RelocateLog) 
$myarr=@($RelocateData,$RelocateLog)
Restore-SqlDatabase -ServerInstance DEV\DEMO -Database "test" -BackupFile $backupfile -RelocateFile $myarr

Hope it helps !

Upvotes: 19

zerocool18
zerocool18

Reputation: 523

I blogged about solving this issue by changing environment path variables. Please check http://powershelldiaries.blogspot.in/2015/08/backup-sqldatabase-restore-sqldatabase.html. As I mentioned above also, the answer by "Samuel Dufour" helped me. I just thought of an another way.

Upvotes: 2

Scott Munro
Scott Munro

Reputation: 13606

This looks like a difference in the version of SMO that you have loaded and the one that Restore-SqlDatabase expects. There are probably two approaches here...

  1. Make sure that the versions match.
  2. Use the Microsoft.SqlServer.Management.Smo.Restore.SqlRestore method instead of the Restore-SqlDatabase cmdlet.

I have extracted the relevant pieces from a larger script below. It is untested in this form and there are a few variables such as $ServerName which are assumed to be available but it should be enough to get you going.

    if($useSqlServerAuthentication)
    {
        $passwordSecureString = ConvertTo-SecureString -String $password -AsPlainText -Force;

        $serverConnection = new-object Microsoft.SqlServer.Management.Common.ServerConnection $ServerName, $UserName, $passwordSecureString;

        $server = new-object Microsoft.SqlServer.Management.Smo.Server $serverConnection;
    }
    else
    {
        $server = new-object Microsoft.SqlServer.Management.Smo.Server $ServerName;
    }

    $dataFolder = $server.Settings.DefaultFile;
    $logFolder = $server.Settings.DefaultLog;

    if ($dataFolder.Length -eq 0)
    {
        $dataFolder = $server.Information.MasterDBPath;
    }

    if ($logFolder.Length -eq 0) 
    {
        $logFolder = $server.Information.MasterDBLogPath;
    }

    $backupDeviceItem = new-object Microsoft.SqlServer.Management.Smo.BackupDeviceItem $Path, 'File';

    $restore = new-object 'Microsoft.SqlServer.Management.Smo.Restore';
    $restore.Database = $DatabaseName;
    $restore.Devices.Add($backupDeviceItem);

    $dataFileNumber = 0;

    foreach ($file in $restore.ReadFileList($server)) 
    {
        $relocateFile = new-object 'Microsoft.SqlServer.Management.Smo.RelocateFile';
        $relocateFile.LogicalFileName = $file.LogicalName;

        if ($file.Type -eq 'D'){
            if($dataFileNumber -ge 1)
            {
                $suffix = "_$dataFileNumber";
            }
            else
            {
                $suffix = $null;
            }

            $relocateFile.PhysicalFileName = "$dataFolder\$DatabaseName$suffix.mdf";

            $dataFileNumber ++;
        }
        else 
        {
            $relocateFile.PhysicalFileName = "$logFolder\$DatabaseName.ldf";
        }

        $restore.RelocateFiles.Add($relocateFile) | out-null;
    }    

    $restore.SqlRestore($server);

Upvotes: 10

Related Questions