Retrieve Error text from SQL Server 2000 error - asp.net

I need help logging errors from T-SQL in SQL Server 2000. We need to log errors that we trap, but are having trouble getting the same information we would have had sitting in front of SQL Server Management Studio.
I can get a message without any argument substitution like this:
SELECT MSG.description from master.dbo.sysmessages MSG
INNER JOIN sys.syslanguages LANG ON MSG.msglangID=LANG.msglangid
WHERE MSG.error=#err AND LANG.langid=##LANGID
But I have not found any way of finding out the error arguments. I want to see:
Constraint violation MYCONSTRAINT2 on table MYTABLE7
not
Constraint violation %s on table %s
Googling has only turned up exotic schemes using DBCC OUTPUTBUFFER that require admin access and aren't appropriate for production code. How do I get an error message with argument replacement?

In .Net, retrieving error messages (and anything output from print or raiserror) from sql server is as simple as setting one property on your SqlConnection ( .FireInfoMessageEventOnUserErrors = True) and handling the connection's InfoMessage event. The data received by .Net matches what you get in the Messages window in the SQL Server Management Studio results grid.
All the code goes in the function that handles the event, and you can abstract that so that all your connections point to the same method, so there's nothing else to change in the rest of the app aside from the two lines of code when you create new connections to set the property and event (and you have that abstracted away so you only need to do it in one place, right?)
Here is a link to what I consider the definitive error guide for SQL Server.
http://www.sommarskog.se/error-handling-I.html
In certain circumstances SQL Server will continue processing even after an error. See the heading labeled What Happens when an Error Occurs? from the previous link.

Look in Books on-line for Raiserror (Described)
You will find the syntax looks like this:
RAISERROR ( { msg_id | msg_str } { , severity , state }
[ , argument [ ,...n ] ] )
[ WITH option [ ,...n ] ]
and the error arguments are as follows:
d or I Signed integer
o Unsigned octal
p Pointer
s String
u Unsigned integer
x or X Unsigned hexadecimal
Any language from VB onwards has the ability to catch these and let you to take the appropriate action.
Dave J

Any chance you'll be upgrading to SQL2005 soon? If so, you could probably leverage their TRY/CATCH model to more easily accomplish what you're trying to do.
The variables exposed in the catch can give you the object throwing the error, the line number, error message, severity, etc. From there, you can log it, send an email, etc.

FORMATMESSAGE (it also exists in SQL Server 2000) allows you to build up messages into their final format from the sysmessages templates like above.
However, the RAISERROR command (which is pretty much what the database engine itself uses internally calls when you have an error) already sends the completed text which can be trapped and logged in the client. SSMS is a client and does not generate it's own messages: all message come from the database engine.
However, I gather you want to log the T-SQL error using T-SQL. Frankly, you can't on SQL Server 2000. Too many errors are batch and scope aborting to reliably log anything.
You have to be on SQL Server 2005 to use TRY/CATCH/ERROR_MESSAGE, or you trap in the client and then using something like log4net to log back to SQL Server.

Related

BizTalk 2013 - execute stored procedure on send port without orchestration?

A while back I set up BizTalk to pick up a file via FTP and drop it into a network directory. It's all passsthru so I didn't use an orchestration.
Now I've been asked to execute a stored procedure once the file is picked up. The procedure contains no parameters and I do not need the contents of the file.
It seems like such a simple request but I can't figure it out. Is there any way to do this without over complicating things?
This can be accomplished through the use of either the WCF-SQL adapter or the WCF_Custom adapter with a SQL binding. You can do this using messaging only with just a SendPort with a filter/map on it thus no orchestration needed.
For the SOAP action header use TypedProcedure/dbo/name_of_your_stored_procedure and in the messages tab you can specify the paramters to the stored procuders as well as add a payload in the following manner:
<name_of_your_stored_procedure xmlns="http://schemas.microsoft.com/Sql/2008/05/TypedProcedures/dbo">
<parameter1>XXXX</parameter1>
<xml_parameter>
<bts-msg-body xmlns="http://www.microsoft.com/schemas/bts2007" encoding="string"/>
</xml_parameter>
</name_of_your_stored_procedure>
In the above case xml_parameter will have the contents of the message payload passed to it.
The stored procedure should look something like :
CREATE PROCEDURE [dbo].[name_of_your_stored_procedure]
#parameter1 int,
#xml_parameter nvarchar(max)
AS
BEGIN
-- your code goes here
END
More details can be found here
Regards Hasse
This MSDN page describes the process and has this to say: "You must create a BizTalk orchestration to use BizTalk Server for performing an operation on SQL Server."
However if you're really desperate not to use an orchestration I believe you have the option of setting the operation context property in a custom pipeline component. Then you can initialise the message in a map on a port. In theory this should work but I can't guarantee it.

Pool Multiple Messages with BizTalk 2006 SQL Adapter

I have a StoredProcedure that returns a simple table containing several records:
DECLARE #STEPS_TABLE AS TABLE (OrchestrationID uniqueidentifier, [Message] nvarchar(1000));
-- LOADING THE VALUES HERE
SELECT * FROM #STEPS_TABLE As Step FOR XML AUTO, XMLDATA, ELEMENTS
I used the SQL Transport Schema Generation Wizard to create my schema and could configure the port correctly. If I use this schema on my orchestration, it works perfectly. BizTalk starts one instance of the orchestration everytime the #STEPS_TABLE has more than 1 record.
Reading Microsoft technical documentation, they recommend to get several messages in one call and then use the XML pipeline to disassemble the multi-row BizTalk message into a single-row BizTalk message.
I haven't used the XML pipeline before, so I tried the provided steps but couldn't get it to work.
Could somebody provide me a link to a "how to" (didn't find anything until now, after several hours of searching) or give me some hints to succeed.
Thanks in advance.
... some hours later I could figure it out myself. So if anybody comes across the same issue as me, here you have some guidelines to make it work on your environment.
At the end I followed a different walkthrough from Microsoft and avoided the pipeline recommendation altogether. The documentation I found is called "Disassembling Result Sets Using the SQL Adapter" and does exactly what i was looking for. You can just follow the whole walkthrough from Microsoft but avoid the creation of the send port and make some little adjustment on the receive port.
After following the technical document you will end up with two schemas, I will call them message and envelope (contains several messages) for the sake of this excercise. In your orchestration you can create a receiving port that maps to the message and then when you configure it as a SQL Port and you link it to your stored procedure (or select statement), you only have to change the Document Root Element Name to the envelope root name; the XML Receive pipeline (provided by default in BizTalk 2006) will do the magic of disassembling the messages contained in the envelope and instantiating an orchestration for each message.
The Microsoft "Disassembling Result Sets Using the SQL Adapter" walkthrough can be found under:
http://msdn.microsoft.com/en-us/library/aa562098(v=bts.20).aspx
Mission accomplished :)

Reporting in ASP.NET

I have an SQL Database and an ASP.NET website built to put data into the database.
One of the project requirements is to build a system that would let the user upload a Crystal Report to the server and run it as needed. This way, the user could create a customized report (for then turning into management, customers) that wouldn't force them to go through a developer.
I'm looking for suggestions on how to accomplish this goal.
Currently, I'm looking for a way to redirect the database connection in the Crystal Report from the database it was developed with to the database it will eventually run on. However, There doesn't seem to be a simple way to do this.
I'm also investigating the ReportViewer object. However, all the code I have seen involves specifying the query for the report in the code, which isn't acceptable.
One option (which I don't like at all) is to let them write their own queries so they can copy the results into Excel. This would mean a blank textbox and information about the structure of the database. Not a good idea for multiple reasons.
Another option is to create one report for each table (and maybe a few extras), let the user copy the data they want into Excel, and go on their merry way.
tl;dr How do I build a flexible reporting system?
=========================================
Continuation: 08/20/2012
I have decided to go the route of b.pell's extension methods. So far, it has gotten me closer than anything else. My code to bind to the CrystalReportViewer is below:
CrystalReportSource rs = new CrystalReportSource();
rs.Report.FileName = Server.MapPath("ReportFiles/") + Request["reportname"];
string connstring = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;
rs.ReportDocument.ApplyCredentialsFromConnectionString(connstring);
rs.ReportDocument.ApplyNewDatabaseName("myDBName", "mySchemaName");
rs.ReportDocument.Refresh();
CrystalReportViewer1.ReportSource = rs;
This comes very close to working. It works fine on my dev machine, but when I run the code on the server, it gives the following error:
Logon failed.Error in File CrystalReport2 {5D2E82E5-783E-4DFD-A770-C8AE72A51E4E}.rpt:
Unable to connect: incorrect log on parameters. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.Runtime.InteropServices.COMException: Logon failed.Error in File CrystalReport2 {5D2E82E5-783E-4DFD-A770-C8AE72A51E4E}.rpt: Unable to connect: incorrect log on parameters. Source Error: An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
The error is in this line in the code:
crTable.Location = String.Format("{0}{1}", prefix, crTable.Location.Substring(crTable.Location.LastIndexOf(".") + 1))
When I remove the call to ApplyNewDatabaseName, I am asked to enter the Server Name, the Database name, the Username and the Password or to select Integrated Security. I can't enter the Database Name or the Server Name (those fields are disabled).
Any thoughts?
I think what you're looking for is the Reporting Services, part of Business Intelligence
Or maybe you can setup a UI that let the users pick the tables and columns they need for the report (this way you can limit the information they can access) an write a Dinamic Query Builder Function or something like that.
I answer the changing Crystal Reports connection question a lot (it's something I'd think Crystal would make easier, but I wonder if they don't because that's what their server product does). :D Anyway, you can set the database credentials at runtime. Crystal is very particular in the order it's done, but I have some code that I turned into extension methods that do the trick. This code will go through the main report and all sub reports and change the connection information. This assumes that all sub reports connect to the same database that the main report does (if not, you'll need to modify it to handle multiple connections, but this rarely comes up at least with what I do).
Extension methods to change connection info: http://www.blakepell.com/2012-05-22-crystal-reports-extension-methods
It would be used something like this (although, you're binding to a viewer probably and not exporting, so you could ignore that, this is just for example).
Using rd As New ReportDocument
rd.Load("C:\Temp\CrystalReports\InternalAccountReport.rpt")
rd.ApplyNewServer("serverName or DSN", "databaseUsername", "databasePassword")
rd.ApplyParameters("AccountNumber=038PQRX922;", True)
rd.ExportToDisk(ExportFormatType.PortableDocFormat, "c:\temp\test.pdf")
rd.Close()
End Using
System.Diagnostics.Process.Start("c:\temp\test.pdf")
You could use the Crystal Viewer at this point to deliver the reports and store the report in a database or on the file system (with a db meta data table) and have some predefined connections the user could select from that would be applied when it is run.
You also have the option to write your own front end. In this scenario a user would select a report from your meta data (you could put whatever security on it you wanted, I use AD). Then you can read the report parameters in and lay them out on the web form. When the user fills them in, you then sanatize them and pass them to the report via these extensions and you can output Excel, PDF, Word Doc, RTF, etc. A little more overhead and not the nice preview view, but can work well (I've done something like this in the past). Hope this helps.
About "...let them write their own queries" part of your question.
The solution can be to use some query builder component with friendly user interface which hides from users the complexity of your database and avoid any possible SQL injections.
There are few such products on the market. One of them is called EasyQuery, another one is build by Aspose if I'm not wrong. Try to search in Google for "query bulider for asp.net" or ".net query builder component".

How to change data source in reportviewer control

I have a reportviewer (Microsoft.ReportViewer.WebForms) control on my page. All my reports use one data source. I want to be able to let my reports run on a different database when started from my UAT enviroment. So the location of the reports is the same, but the data comes from a different db. I cannot seem to find how this is done, is it even possible?
EDIT: They are server reports on SQL Server . I know you can set the dataset programmaticaly but I just want the reports to point to a different db and leave the rest of the report intact.
2005
TIA,
John
Did you want to pass a full connection string to the report as a parameter? You can do it but sometimes SSRS gets funny and make sure you delete the report off the server before you deploy a new copy when doing this...
1. Make a parameter - let's call ours connectionStr. Make it not null, not blank, single select and text as the data type. Eventually, you will want to hide this parameter but for testing please leave it visible.
2. So the value you will be using as the connection string... (for testing I set this as the default for the parameter, with nothing put under the available values section) Data Source=MySQLServerName;Initial Catalog=MyDatabaseName;Persist Security Info=True;User ID=MyUserNameForTheServer;Password=MyPasswordForTheServer;MultipleActiveResultSets=True
3. You need an unattended execution account on your report server or you get this: unattended execution account is not specified. (rsInvalidDataSourceCredentialSetting). http://msdn.microsoft.com/en-us/library/ms156302.aspx I can't provide more details because my boss had to do this part for me.
4. Under your datasource properties in SSRS... check Embedded Connection, select the type (mine is just a normal MS SQL Server), for the connection string, open the expression box and put: =Parameters!connectionStr.value and then click credentials and make sure the last option for no credentials is selected.
5. Your datasets for that datasource will no longer be happy when you try to edit them in design view but you can switch the datasource connection properties back to how they were, not using the parameter based connection string, for editing them.
My reports are on different servers, with different instances of the Report Server, too. On some servers, they need to get their data from various databases depending on whatever, stuff. This way, with the connection string as a parameter, I can use the same reports everywhere and just deploy them to the different servers. If you are having to pass this connection string around your app or to a report viewer, I suggest using encryption.
Like I said... SSRS get's funny when you start doing this, though. Your reports should always work in preview mode after doing this, if they don't even when provided with the correct connection strings, then you have an issue that won't be solved by just deploying to the server. Trouble shooting problems with this once they are on the server but not working include checking permissions, making sure the report receives the correct connection string and making all your stored procedures and functions within the SQL database are all the same.
If you want to just pass the database name and everything else is the same (server name, username, password) then just set the connection string parameter equal to your database name and for the datasource expression value use
="Data Source=MySQLServerName;Initial Catalog=" + Parameters!connectionString.value + ";Persist Security Info=True;User ID=MyUserNameForTheServer;Password=MyPasswordForTheServer;MultipleActiveResultSets=True"
I needed to pass the whole thing in, and you can play around with the credential settings - you might be able to save the server username/password info in there for each report so that the unattended execution account is not needed.

BizTalk Generate Metadata Fails with Stored Procedure

I am trying to set up the SQL Adapter in BizTalk 2009 to use a Stored Procedure in our SQL 2008 DB. For some reason, when I click "generate" in the wizard, and then click "next," I receive the following message:
Failed to execute SQL Statement. Please ensure that the supplied syntax is correct. New Transaction cannot enlist in the specified transaction coordinator.
I know the stored proc works, because we call it from C# code as well and I don't have a problem with it. I'm pretty sure I'm missing something basic, but I can't find what it is, and the only information I can find on using Stored Procs through the BizTalk SQL Adapter only show Select statements, so I don't know if there's some other setting I"m missing.
Here's the Stored Proc:
SET NOCOUNT ON;
Declare #Client int
set #Client = (Select Client_Id from Clients where Client_Name = #clientName)
Insert Into [FTP_Data].[dbo].[FileLog](Client_Id, Client_Name, FileType, Received)
Values(#Client, #clientName, #fileType, GETDATE());
Edit/Update: When I move the solution off my developer box and onto the actual server box, it works fine. As far as I can tell, the only difference is that the actual BizTalk Server components (instead of just the developer stuff) are on the same machine as the SQL Server to which I'm connecting. I would think that should be an issue, though...
"New Transaction cannot enlist in the specified transaction coordinator". Seems to indicate a possible DTC error. Is this the SQL server that the BizTalk databases are installed on?

Resources