Hello World Ready 示例演示了创建、部署和测试基于简单世界的公共语言运行时(CLR)集成存储过程所涉及的基本作。 无需更改组件的源代码,就可以轻松地将世界通用组件本地化为世界各地的不同市场的不同语言。 此示例还演示了如何通过输出参数和记录返回数据,该记录由存储过程动态构造并返回到客户端。此示例与 Hello World 示例几乎完全相同,只是本地化此应用程序要容易、更安全。 若要更改本地化文本,需要满足以下条件:
更改资源目录中特定区域性的 XML 文件(.
resx文件)使用
生成区域性的资源文件 为该区域性生成更新的附属 DLL
在 SQL Server 中删除和添加该程序集
CLR 存储过程本身的源代码和程序集不会更改。 提供了一个 build.cmd 脚本,演示如何编译和链接资源程序集。尽管应用程序的源代码基于当前正在执行的程序集创建资源管理器,但不必在包含存储过程的 DLL 中嵌入非特定区域性资源。 允许 System.Resources.NeutralResourcesLanguage attribute 非区域性资源存在于附属 DLL 中。 最好为此使用单独的 DLL,以便在需要添加或更改本地化文本时,包含 CLR 存储过程的主 DLL 不必更改。 这对于 CLR 用户定义类型特别有用,这些类型可能包含列和其他依赖项,这使得很难删除和重新添加类型。通常,附属 DLL 版本必须与主程序集版本相同。 但是,可以使用该 SatelliteContractVersion 属性来允许更新主程序集,而无需更新附属程序集。 有关详细信息,请参阅 ResourceManager Microsoft .NET 文档中的类。
先决条件
此示例仅适用于 SQL Server 2005 及更高版本。
若要创建并运行此项目,必须安装以下软件:
SQL Server 或 SQL Server Express。 可以从 SQL Server Express 文档和示例网站免费获取 SQL Server Express
SQL Server 开发人员网站提供的 AdventureWorks 数据库
.NET Framework SDK 2.0 或更高版本或Microsoft Visual Studio 2005 或更高版本。 可以免费获取 .NET Framework SDK。
此外,必须满足以下条件:
正在使用的 SQL Server 实例必须启用 CLR 集成。
若要启用 CLR 集成,请执行以下步骤:
启用 CLR 集成
- 执行以下 Transact-SQL 命令:
sp_configure 'clr enabled', 1GORECONFIGUREGO注释
若要启用 CLR,必须具有
ALTER SETTINGS由服务器角色成员sysadminserveradmin隐式持有的服务器级别权限。AdventureWorks 数据库必须安装在正在使用的 SQL Server 实例上。
如果你不是正在使用的 SQL Server 实例的管理员,则必须有管理员授予 CreateAssembly 权限才能完成安装。
生成示例
使用以下说明创建并运行示例:
打开 Visual Studio 或 .NET Framework 命令提示符。
如有必要,请为示例创建目录。 对于此示例,我们将使用 C:\MySample。
在 c:\MySample 中,创建
HelloWorld.vb(针对 Visual Basic 示例)或HelloWorld.cs(对于 C# 示例),并将相应的 Visual Basic 或 C# 示例代码(如下所示)复制到文件中。在 c:\MySample 中,创建文件
messages.resx并将示例代码复制到文件中。在 c:\MySample 中,通过将文件保存为
messages.de.resx更改行后创建文件messages.de.resxmessages.resx<value xml:space="preserve">Hello, World!</value>读取
<value xml:space="preserve">Hallo Welt!</value>
在 c:\MySample 中,通过将文件保存为
messages.es.resx更改行后创建文件messages.es.resxmessages.resx<value xml:space="preserve">Hello, World!</value>读取
<value xml:space="preserve">Hola a todos</value>
在 c:\MySample 中,通过将文件保存为
messages.fr.resx更改行后创建文件messages.fr.resxmessages.resx<value xml:space="preserve">Hello, World!</value>读取
<value xml:space="preserve">Bonjour !</value>
在 c:\MySample 中,通过将文件保存为
messages.fr-FR.resx更改行后创建文件messages.fr-FR.resxmessages.resx<value xml:space="preserve">Hello, World!</value>读取
<value xml:space="preserve">Bonjour de France!</value>
在 c:\MySample 中,通过将文件保存为
messages.it.resx更改行后创建文件messages.it.resxmessages.resx<value xml:space="preserve">Hello, World!</value>读取
<value xml:space="preserve">Buongiorno</value>
在 c:\MySample 中,通过将文件保存为
messages.ja.resx更改行后创建文件messages.ja.resxmessages.resx<value xml:space="preserve">Hello, World!</value>读取
<value xml:space="preserve">ã"ã‚"ã«ã¡ã¯</value>
在 c:\MySample 中,创建文件
build.com并将示例代码复制到文件中通过在命令提示符处执行文件生成来生成附属程序集。
通过执行下列代码之一,从命令行提示符编译示例代码:
Vbc /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll,C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll,C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /out:HelloWorldReady.dll /target:library HelloWorld.vbCsc /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.XML.dll /out:HelloWorldReady.dll /target:library Hello.csCopy the tsql installation code into a file and save it as Install.sql in the sample directory.
如果示例安装在其他
C:\MySample\目录中,则按指示编辑该文件Install.sql以指向该位置。通过执行来部署程序集和存储过程
sqlcmd -E -I -i install.sql
将 Transact-SQL 测试命令脚本复制到文件中,并将其
test.sql另存为示例目录中。使用以下命令执行测试脚本
sqlcmd -E -I -i test.sql
将 Transact-SQL 清理脚本复制到文件中,并将其
cleanup.sql另存为示例目录中。使用以下命令执行脚本
sqlcmd -E -I -i cleanup.sql
示例代码
下面是此示例的代码列表。
C#(编程语言)
using System;
using System.Data;
using System.Data.Sql;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Globalization;
using System.Threading;
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: System.Resources.NeutralResourcesLanguage("", System.Resources.UltimateResourceFallbackLocation.Satellite)]
[assembly: System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.RequestMinimum)]
[assembly: System.Runtime.ConstrainedExecution.ReliabilityContract(System.Runtime.ConstrainedExecution.Consistency.MayCorruptInstance, System.Runtime.ConstrainedExecution.Cer.None)]
public sealed partial class StoredProcedures
{
private StoredProcedures()
{
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters"), Microsoft.SqlServer.Server.SqlProcedure]
public static void HelloWorldReady(string culture, out string greeting)
{
ResourceManager rm
= new ResourceManager("Messages",
Assembly.GetExecutingAssembly());
string message = rm.GetString("HelloWorld", CultureInfo.GetCultureInfo(culture));
Microsoft.SqlServer.Server.SqlMetaData columnInfo
= new Microsoft.SqlServer.Server.SqlMetaData("Column1", SqlDbType.NVarChar, 24);
SqlDataRecord greetingRecord
= new SqlDataRecord(new Microsoft.SqlServer.Server.SqlMetaData[] { columnInfo });
greetingRecord.SetString(0, message);
SqlContext.Pipe.Send(greetingRecord);
greeting = message;
}
}
Visual Basic
Imports System
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
Imports System.Globalization
Imports System.Resources
Imports System.Reflection
Imports System.Runtime.InteropServices
<Assembly: AssemblyVersion("1.0.*")>
<Assembly: System.Runtime.InteropServices.ComVisible(False)>
<Assembly: System.CLSCompliant(True)>
<Assembly: System.Resources.NeutralResourcesLanguage("", System.Resources.UltimateResourceFallbackLocation.Satellite)>
<Assembly: System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.RequestMinimum)>
<Assembly: System.Runtime.ConstrainedExecution.ReliabilityContract(System.Runtime.ConstrainedExecution.Consistency.WillNotCorruptState, Runtime.ConstrainedExecution.Cer.None)>
Partial Public NotInheritable Class StoredProcedures
Private Sub New()
End Sub
<Microsoft.SqlServer.Server.SqlProcedure()> _
Public Shared Sub HelloWorldReady(ByVal culture As String, ByRef greeting As String)
Dim rm As New ResourceManager("Messages", Assembly.GetExecutingAssembly())
Dim message As String = rm.GetString("HelloWorld", CultureInfo.GetCultureInfo(culture))
Dim columnInfo As New Microsoft.SqlServer.Server.SqlMetaData("Column1", _
SqlDbType.NVarChar, 24)
Dim greetingRecord As New SqlDataRecord(New _
Microsoft.SqlServer.Server.SqlMetaData() {columnInfo})
greetingRecord.SetString(0, message)
SqlContext.Pipe.Send(greetingRecord)
greeting = message
End Sub
End Class
这是 build.com生成附属程序集的。
resgen Messages.resx
resgen Messages.de.resx
resgen Messages.es.resx
resgen Messages.fr.resx
resgen Messages.fr-Fr.resx
resgen Messages.it.resx
resgen Messages.ja.resx
if not exist de/ mkdir de
if not exist es/ mkdir es
if not exist fr/ mkdir fr
if not exist fr-FR/ mkdir fr-FR
if not exist it/ mkdir it
if not exist ja/ mkdir ja
al /t:lib /culture:de /embed:Messages.de.resources /out:de\HelloWorldReady.resources.dll
al /t:lib /culture:es /embed:Messages.es.resources /out:es\HelloWorldReady.resources.dll
al /t:lib /culture:fr /embed:Messages.fr.resources /out:fr\HelloWorldReady.resources.dll
al /t:lib /culture:fr-FR /embed:Messages.fr-FR.resources /out:fr-FR\HelloWorldReady.resources.dll
al /t:lib /culture:it /embed:Messages.it.resources /out:it\HelloWorldReady.resources.dll
al /t:lib /culture:ja /embed:Messages.ja.resources /out:ja\HelloWorldReady.resources.dll
al /t:lib /culture:"" /embed:Messages.resources /out:HelloWorldReady.resources.dll
这是 Transact-SQL 安装脚本(Install.sql),用于部署程序集并在数据库中创建存储过程。
USE AdventureWorks
GO
-- Drop existing sproc and assembly if any.
IF EXISTS (SELECT * FROM sys.procedures WHERE [name] = 'usp_HelloWorldReady')
DROP PROCEDURE usp_HelloWorldReady;
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady')
DROP ASSEMBLY HelloWorldReady;
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.neutral')
DROP ASSEMBLY [HelloWorldReady.resources.neutral]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.de')
DROP ASSEMBLY [HelloWorldReady.resources.de]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.es')
DROP ASSEMBLY [HelloWorldReady.resources.es]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.fr')
DROP ASSEMBLY [HelloWorldReady.resources.fr]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.fr-FR')
DROP ASSEMBLY [HelloWorldReady.resources.fr-FR]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.it')
DROP ASSEMBLY [HelloWorldReady.resources.it]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.ja')
DROP ASSEMBLY [HelloWorldReady.resources.ja]
GO
DECLARE @SamplesPath nvarchar(1024)
-- You may need to modify the value of this variable if you have installed the sample someplace other than the default location.
Set @SamplesPath = N'C:\MySample\'
-- Add the assembly and CLR integration based stored procedure
CREATE ASSEMBLY HelloWorldReady
FROM @SamplesPath + 'HelloWorldReady.dll'
WITH permission_set = Safe;
CREATE ASSEMBLY [HelloWorldReady.resources.neutral]
FROM @SamplesPath + 'HelloWorldReady.resources.dll'
WITH permission_set = Safe;
CREATE ASSEMBLY [HelloWorldReady.resources.de]
FROM @SamplesPath + '\de\HelloWorldReady.resources.dll'
WITH permission_set = Safe;
CREATE ASSEMBLY [HelloWorldReady.resources.es]
FROM @SamplesPath + '\es\HelloWorldReady.resources.dll'
WITH permission_set = Safe;
CREATE ASSEMBLY [HelloWorldReady.resources.fr]
FROM @SamplesPath + '\fr\HelloWorldReady.resources.dll'
WITH permission_set = Safe;
CREATE ASSEMBLY [HelloWorldReady.resources.fr-FR]
FROM @SamplesPath + '\fr-FR\HelloWorldReady.resources.dll'
WITH permission_set = Safe;
CREATE ASSEMBLY [HelloWorldReady.resources.it]
FROM @SamplesPath + '\it\HelloWorldReady.resources.dll'
WITH permission_set = Safe;
CREATE ASSEMBLY [HelloWorldReady.resources.ja]
FROM @SamplesPath + '\ja\HelloWorldReady.resources.dll'
WITH permission_set = Safe;
GO
CREATE PROCEDURE usp_HelloWorldReady
(
@Culture NVarchar(12),
@Greeting NVarchar(24) OUTPUT
)
AS EXTERNAL NAME HelloWorldReady.StoredProcedures.HelloWorldReady;
GO
USE master;
GO
这是 test.sql,它通过在每个区域设置上执行函数来测试示例。
USE AdventureWorks
GO
DECLARE @GreetingDe nvarchar(24);
DECLARE @GreetingDe_CH nvarchar(24);
DECLARE @GreetingEn nvarchar(24);
DECLARE @GreetingEs nvarchar(24);
DECLARE @GreetingFr nvarchar(24);
DECLARE @GreetingFr_FR nvarchar(24);
DECLARE @GreetingIt nvarchar(24);
DECLARE @GreetingJa nvarchar(24);
--German as spoken anywhere in the world (the neutral German culture)
EXEC usp_HelloWorldReady 'de', @GreetingDe OUTPUT;
--German as spoken in Switzerland. Because we don't have a specific assembly
--for this case, the .NET Framework will automatically fall back to the neutral German culture DLL.
EXEC usp_HelloWorldReady 'de-CH', @GreetingDe_CH OUTPUT;
EXEC usp_HelloWorldReady 'en', @GreetingEn OUTPUT;
EXEC usp_HelloWorldReady 'es', @GreetingEs OUTPUT;
--French as spoken anywhere in the world (the neutral French culture)
EXEC usp_HelloWorldReady 'fr', @GreetingFr OUTPUT
--French as spoken in France. Since we do have a specific assembly for this case, a specific
--greeting is provided from that DLL. The neutral French culture DLL is not used in this case.
EXEC usp_HelloWorldReady 'fr-FR', @GreetingFr_FR OUTPUT
EXEC usp_HelloWorldReady 'it', @GreetingIt OUTPUT;
EXEC usp_HelloWorldReady 'ja', @GreetingJa OUTPUT;
SELECT @GreetingDe AS OUTPUT_PARAMETER_DE;
SELECT @GreetingDe_CH AS OUTPUT_PARAMETER_De_CH;
SELECT @GreetingEn AS OUTPUT_PARAMETER_EN;
SELECT @GreetingEs AS OUTPUT_PARAMETER_ES;
SELECT @GreetingFr AS OUTPUT_PARAMETER_FR;
SELECT @GreetingFr_FR AS OUTPUT_PARAMETER_Fr_FR;
SELECT @GreetingIt AS OUTPUT_PARAMETER_IT;
SELECT @GreetingJa AS OUTPUT_PARAMETER_JA;
GO
以下 Transact-SQL 从数据库中删除程序集和存储过程。
USE AdventureWorks;
GO
-- Drop existing sproc and assembly if any.
IF EXISTS (SELECT * FROM sys.procedures WHERE [name] = 'usp_HelloWorldReady')
DROP PROCEDURE usp_HelloWorldReady;
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady')
DROP ASSEMBLY HelloWorldReady;
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.neutral')
DROP ASSEMBLY [HelloWorldReady.resources.neutral]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.de')
DROP ASSEMBLY [HelloWorldReady.resources.de]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.es')
DROP ASSEMBLY [HelloWorldReady.resources.es]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.fr')
DROP ASSEMBLY [HelloWorldReady.resources.fr]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.fr-FR')
DROP ASSEMBLY [HelloWorldReady.resources.fr-FR]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.it')
DROP ASSEMBLY [HelloWorldReady.resources.it]
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'HelloWorldReady.resources.ja')
DROP ASSEMBLY [HelloWorldReady.resources.ja]
GO
USE master;
GO