· Ordinarily, all client-side access to the Dynamics SQL server is blocked either by locking down access ports or disabling client remote connections. This protects company data from rogue client-side requests from ODBC or MS Query capable applications. Server-to-server communications is better regulated through the use of firewall rules. Data access is from vetted, verified and trusted sources and can be implemented through robust development processes.
TSQL Scalar function: GetHttp |
sp_configure'show advanced options', 1; GO RECONFIGURE; GO sp_configure'Ole Automation Procedures', 1; GO RECONFIGURE; GO CREATEFUNCTIONGetHttp ( @urlvarchar(8000) ) RETURNSvarchar(8000) AS BEGIN DECLARE@winint DECLARE@hr int DECLARE@textvarchar(8000) EXEC@hr=sp_OACreate'WinHttp.WinHttpRequest.5.1',@winOUT IF@hr<> 0 EXECsp_OAGetErrorInfo@win EXEC@hr=sp_OAMethod@win,'Open',NULL,'GET',@url,'false' IF@hr<> 0 EXECsp_OAGetErrorInfo@win EXEC@hr=sp_OAMethod@win,'Send' IF@hr<> 0 EXECsp_OAGetErrorInfo@win EXEC@hr=sp_OAGetProperty@win,'ResponseText',@textOUTPUT IF@hr<> 0 EXECsp_OAGetErrorInfo@win EXEC@hr=sp_OADestroy@win IF@hr<> 0 EXECsp_OAGetErrorInfo@win SET@text=REPLACE(@text,'<','<'); SET@text=REPLACE(@text,'>','>'); RETURN@text END |
Class: LNPS_SqlServerCLRClass |
using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Net; using System.IO; publicpartialclassLNPS_SqlServerCLRClass { [Microsoft.SqlServer.Server.SqlProcedure] publicstaticvoid HTTPGet(SqlString weburl, [SqlFacet(MaxSize = -1)] outSqlString returnval) { string url = Convert.ToString(weburl); string feedData = string.Empty; try { HttpWebRequest request = null; HttpWebResponse response = null; Stream stream = null; StreamReader streamReader = null; request = (HttpWebRequest)WebRequest.Create(url); request.Method = "GET"; request.ContentLength = 0; response = (HttpWebResponse)request.GetResponse(); stream = response.GetResponseStream(); streamReader = newStreamReader(stream); feedData = streamReader.ReadToEnd(); response.Close(); stream.Dispose(); streamReader.Dispose(); } catch (Exception ex) { SqlContext.Pipe.Send(ex.Message.ToString()); } feedData = feedData.Replace("<", "<"); feedData = feedData.Replace(">", ">"); returnval = feedData; } } |
Give yourself the authority to make the necessary changes on the database. |
ALTERAUTHORIZATIONONDatabase::[DATABASE] TO [DOMAIN\USER] |
Note: Alternatively, login to SQL as the database administrator. |
Enable your database for CLR. |
EXECsp_configure'clr enabled', 1 GO RECONFIGURE GO EXECsp_configure'clr enabled' GO |
Allow the SQL instance to trust the database and its contents. |
ALTERDATABASE [DATABASE] SETTRUSTWORTHYON |
Note: Check with your SQL administrator to confirm that this doesn’t contravene policy. |
Install the assembly into the current database. |
CREATEASSEMBLY LNPS_SqlServerCLR FROM'D:\Program Files\Microsoft SQL Server\100\Tools\Binn\LNPS_SqlServerCLR.dll' WITHPERMISSION_SET=UNSAFE; |
Note: Choose the path where you installed your custom assembly. |
Create a stored procedure wrapper to invoke the CLR method |
CREATEPROCEDURE [dbo].[HTTPGet] @WebUrl [nvarchar](4000), @ReturnVal [nvarchar](MAX)OUTPUT WITHEXECUTEASCALLER AS EXTERNAL NAME [LNPS_SqlServerCLR].[LNPS_SqlServerCLRClass].[HTTPGet] |
Note: Fully qualified path is [namespace].[class].[method] |
Test the new CLR by performing a HTTP GET against any website |
DECLARE @response NVARCHAR(MAX) EXECUTE HTTPGet'http://www.microsoft.com',@Response OUT SELECT @response |
Note: This will return the HTML from the chosen website. Your SQL server will need internet access for this to work. |
X++ |
CustTable custTable; whileselect * from custTable where custTable.CustGroup == 'EU' { info(strFmt("AccountNum = %1, Name = %2, Balance = %3", custTable.AccountNum, custTable.name(), custTable.balanceAllCurrency('GBP'))); } |
CustTable custTable; str result = '<Root>'; whileselect * from custTable where custTable.CustGroup == 'EU' { result += '<CustTable AccountNum="' + custTable.AccountNum + '" Name="' + custTable.name() + '" Balance="' + num2str(custTable.balanceAllCurrency('GBP'), 0, 2, 1, 0) + '" />'; } result += '</Root>'; return result; |
Sample results of the above script are shown below: |
<?xmlversion="1.0"encoding="utf-8"?> <Root> <CustTableAccountNum="00000066"Name="EU Customer 1"Balance="2866.50" /> <CustTableAccountNum="00000070"Name="EU Customer 2"Balance="10710.00" /> <CustTableAccountNum="00000085"Name="EU Customer 3"Balance="684.00" /> <CustTableAccountNum="00000087"Name="EU Customer 4"Balance="2374.00" /> </Root> |
· The OPENXML command provides a rowset view over an XML document, allowing it to be used in TSQL statements. More information on the OPENXML command can be found here: [http://technet.microsoft.com/en-us/library/ms186918.aspx]
1.) Transfer the X++ to SQL.
2.) Invoke the passthru X++ using our REST service.
3.) Convert the XML response into a SQL DataSet.
Transfer the X++ to SQL. |
DECLARE @idoc ASINT, @script ASNVARCHAR(2000), @request ASNVARCHAR(4000), @response ASNVARCHAR(MAX); SET @script =N'<![CDATA[ CustTable custTable; str result = ''<Root>''; while select * from custTable where custTable.CustGroup == ''DOM'' { result += ''<CustTable AccountNum="'' + custTable.AccountNum + ''" Name="'' + custTable.name() + ''" Balance="'' + num2str(custTable.balanceAllCurrency(''GBP''), 0, 2, 1, 0) + ''" />''; } result += ''</Root>''; return result; ]]>'; SET @script =REPLACE(@script,'+','%2B'); |
Invoking the passthru X++ using the REST service. |
SET @request ='http://vmssp-01/GenericBroker/Service.asmx/ProcessImmediate?requestXml=<brokerRequest><direction>SQL->L1</direction><serviceClass>DynamicsClass</serviceClass><invocationMethod>ExecuteScript</invocationMethod><data>'+ @script +'</data><resultOnly>true</resultOnly></brokerRequest>'; EXECUTE HTTPGet@request, @response OUT |
Convert the XML result into a SQL DataSet. |
-- Create an internal representation of the XML document. EXECsp_xml_preparedocument@idoc OUTPUT, @response; -- Execute a SELECT statement that uses the OPENXML rowset provider. SELECT * FROM OPENXML (@idoc,'/Root/CustTable',1) WITH (AccountNum nvarchar(20), Name varchar(50), Balance decimal(13,2)); |
Note: Choose the Xpath and components of the OpenXML dataset must match the attribute tags within the XML. |