I have SQL server express 2008 SP1 on windows 7 (Version 6.1 Build 7601: Service Pack 1) and visual studio 2010.
I am attempting to create a Stored Procedure CLR for inserting a file into a file stream using the following code.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.IO;
using System.Security.Principal;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void sp_fileController(String friendlyName, String filePath)
{
SqlParameter fDataParam = new System.Data.SqlClient.SqlParameter开发者_如何转开发("@fData", SqlDbType.VarBinary, -1);
SqlParameter fNameParam = new System.Data.SqlClient.SqlParameter("@fName", SqlDbType.NVarChar, 300);
WindowsIdentity newId = SqlContext.WindowsIdentity;
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
try
{
string cs = @"Server=[myservername];Integrated Security=true";
using (SqlConnection con = new SqlConnection(cs))
{
con.Open();
SqlTransaction objSqlTran = con.BeginTransaction();
//string sql = "INSERT INTO fileStreamTest VALUES ((Cast('' As varbinary(Max))), @fName, default); Select fData.PathName() As Path From fileStreamTest Where fId = SCOPE_IDENTITY()";//OUTPUT inserted.fid
SqlCommand insertFileCommand = con.CreateCommand();
insertFileCommand.Transaction = objSqlTran;
insertFileCommand.CommandText = "INSERT INTO fileStreamTest.dbo.fileStreamTest (RowGuid, fData) VALUES (@FileID, CAST ('' as varbinary(max)))";
Guid newFileID = Guid.NewGuid();
insertFileCommand.Parameters.Add("@FileID", SqlDbType.UniqueIdentifier).Value = newFileID;
insertFileCommand.ExecuteNonQuery();
SqlCommand getPathAndTokenCommand = con.CreateCommand();
getPathAndTokenCommand.Transaction = objSqlTran;
getPathAndTokenCommand.CommandText =
"SELECT fData.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() " +
"FROM fileStreamTest.dbo.fileStreamTest " +
"WHERE rowGuid = @FileID";
getPathAndTokenCommand.Parameters.Add("@FileID", SqlDbType.UniqueIdentifier).Value = newFileID;
SqlDataReader tokenReader = getPathAndTokenCommand.ExecuteReader(CommandBehavior.SingleRow);
tokenReader.Read();
SqlString filePathName = tokenReader.GetSqlString(0);
SqlBinary fileToken = tokenReader.GetSqlBinary(1);
tokenReader.Close();
SqlFileStream sqlFile = new SqlFileStream(filePathName.Value, fileToken.Value, System.IO.FileAccess.ReadWrite);
sqlFile.Close();
objSqlTran.Rollback();
//objSqlTran.Commit();
con.Close();
}
}
finally
{
impersonatedUser.Undo();
}
}
};
However when it gets to the line:
SqlFileStream sqlFile = new SqlFileStream(filePathName.Value, fileToken.Value, System.IO.FileAccess.ReadWrite);
I get:
A .NET Framework error occurred during execution of user-defined routine or aggregate "sp_fileController":
System.ComponentModel.Win32Exception: The request is not supported
System.ComponentModel.Win32Exception:
at System.Data.SqlTypes.SqlFileStream.OpenSqlFileStream(String path, Byte[] transactionContext, FileAccess access, FileOptions options, Int64 allocationSize)
at System.Data.SqlTypes.SqlFileStream..ctor(String path, Byte[] transactionContext, FileAccess access, FileOptions options, Int64 allocationSize)
at System.Data.SqlTypes.SqlFileStream..ctor(String path, Byte[] transactionContext, FileAccess access)
at StoredProcedures.sp_fileController(String friendlyName, String filePath)
Can anyone tell me how to fix this issue? Is simply that I cannot execute the code in this manner with sql 2008 express edition?
I think I have found working solution here:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/f49def09-3b47-4e54-8a53-2dd47762821e/filestream-on-windows-server-2012-the-request-is-not-supported?forum=sqldatabaseengine
To sum up: Adding registry key solved the problem on SQL Server 11.0.7001:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\FsctlAllowlist]
"FSCTL_SQL_FILESTREAM_FETCH_OLD_CONTENT"=dword:0x00092560
As odd as it sounds Microsoft intentionally blocked the use of the SqlFileStream
class usages inside SQL CLR assemblies (even if you grant EXTERNAL_ACCESS
or declare the assembly as UNSAFE
) as you can read in Microsoft Connect issue 768308.
However you can access the FILESTREAM as a stream by the means of the SqlBytes type (a really nice tip found on a blog post).. at least for read-only use, I've never tried writing.
I copy-past the code in case the blog disappears (in a slightly improved version with proper disposal of objects):
using System;
using System.IO;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Security.Cryptography;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, SystemDataAccess = SystemDataAccessKind.None)]
public static SqlBinary Hash(SqlBytes source, SqlString hashAlgorithmName)
{
if (Source.IsNull)
{
return null;
}
using (HashAlgorithm ha = GetHashAlgotithm(hashAlgorithmName.Value))
using (Stream stream = Source.Stream)
{
return new SqlBinary(ha.ComputeHash(source.Stream));
}
}
}
I can confirm this definitely works for read-only access. I've never tried for write access (I'm writing FILESTREAM data from an external C# Windows Service or Web Application).
精彩评论