In my last post, I talked about updating the connection information of a Crystal Report at runtime, and some of the quirky behavior that Crystal Reports exhibits (and by quirky I mean, just doesn’t work in certain scenarios). Well, after patting myself on the back at figuring out that I could not use RDO connections and should instead use ADO if I wanted to update them from .Net… I ran into another problem. All of a sudden, when I updated my LoginInfo object in Crystal I would get the following error:
Missing parameter values.
at CrystalDecisions.ReportAppServer.ConvertDotNetToErom.ThrowDotNetException(Exception e)
at CrystalDecisions.ReportSource.EromReportSourceBase.ExportToStream(ExportRequestContext reqContext)
at CrystalDecisions.CrystalReports.Engine.FormatEngine.ExportToStream(ExportRequestContext reqContext)
at CrystalDecisions.CrystalReports.Engine.ReportDocument.ExportToStream(ExportOptions options)
at CrystalDecisions.CrystalReports.Engine.ReportDocument.ExportToHttpResponse(ExportOptions options, HttpResponse response, Boolean asAttachment, String attachmentName)
at Iuf.Reporting.CrystalReport.GenerateCrystalReport(HttpResponse& response)
Missing parameter values.
at CrystalDecisions.ReportAppServer.Controllers.ReportSourceClass.Export(ExportOptions pExportOptions, RequestContext pRequestContext)
at CrystalDecisions.ReportSource.EromReportSourceBase.ExportToStream(ExportRequestContext reqContext)
What I found was, if I did not change the login information, the report worked fine. If I did change the login information, Crystal stopped passing down parameters to sub-reports. The top level parameter would be set, but any linked subreports no longer were populated. I quickly learned, that when setting a new DataSource, you had to do so BEFORE you set your parameter values or Crystal would clear all of them. This was a great find (for most people). My problem was, I WAS setting the new connection before the parameter values there that didn’t fix my issue. After much frustration, I blindly made a change that fixed my problem. When applying new connection information to a Crystal Report from .Net, you MUST set the subreports connection information before the main report. I don’t know why, I can’t explain it and frankly, I don’t care at this point at 5:17 on a Friday. It works. Here is the updated method I use from the CrystalReports class (you should not that it’s references a few properties from the class that you would need to change, but the names should make them self explanitory):
1: ''' <summary>
2: ''' Applies the contents of the ConnectionString property to the report (if it's been set).
3: ''' </summary>
4: ''' <remarks></remarks>
5: Private Sub ApplyNewServer(ByVal report As ReportDocument)
6:
7: If Me.ServerName = "" Then
8: Exit Sub
9: End If
10:
11: For Each subReport As ReportDocument In report.Subreports
12: For Each crTable As Table In subReport.Database.Tables
13: Dim loi As TableLogOnInfo = crTable.LogOnInfo
14: loi.ConnectionInfo.ServerName = Me.ServerName
15:
16: If Me.UseTrustedConnection = True Then
17: loi.ConnectionInfo.IntegratedSecurity = True
18: Else
19: loi.ConnectionInfo.UserID = Me.Username
20: loi.ConnectionInfo.Password = Me.Password
21: End If
22:
23: crTable.ApplyLogOnInfo(loi)
24: Next
25: Next
26:
27: 'Loop through each table in the report and apply the new login information (in our case, a DSN)
28: For Each crTable As Table In report.Database.Tables
29: Dim loi As TableLogOnInfo = crTable.LogOnInfo
30: loi.ConnectionInfo.ServerName = Me.ServerName
31:
32: If Me.UseTrustedConnection = True Then
33: loi.ConnectionInfo.IntegratedSecurity = True
34: Else
35: loi.ConnectionInfo.UserID = Me.Username
36: loi.ConnectionInfo.Password = Me.Password
37: End If
38:
39: crTable.ApplyLogOnInfo(loi)
40: 'If your DatabaseName is changing at runtime, specify the table location.
41: 'crTable.Location = ci.DatabaseName & ".dbo." & crTable.Location.Substring(crTable.Location.LastIndexOf(".") + 1)
42: Next
43:
44: End Sub
Now a small rant, the piece that I find most frustrating is that individuals have been having this problem with Crystal for over a half decade (as well as flat out not being able to change RDO connections real time from code). When you look at snippits of code provided with Visual Studio and from SAP/Business Objects, they do not tell you to swap out your subreports connections first (in fact, their snippits all tell you to do it the other way). Who tests this stuff and do they use real world scenarios? Sure, there are work arounds, you could use DataTables and pass those to reports… but shouldn’t there be a standard way to update connections that consistently works. For everything I like about Crystal (like a decent programming API), they took it like 90% of the way there and then floundered on a few key pieces that have frustrated and continue to frustrate their users.
Hi there, I just had to take some time out of developing to thank you for this article. I’ve been pulling my hair out trying to figure out why in the world I kept getting this “Missing parameter values” error. I pretty much feel the same way about crystal reports. It has been a real love-hate relationship.
I’m glad it helped! It’s been a problem that I’ve wrestled with for years. I may cleanup the Crystal Report wrapper class I use and post it. It’s evolved over the years and finally takes care of most issues that I’ve had.
The only final issue I haven’t been able to resolve is that .Net generated reports sometimes come out with different margins then with the client or viewer. I suspect it could be because of printer settings but I haven’t been able to verify or consistently fix it without addressing the actual layout of the report to force it to work with both (Crystal is highly dependent on the print drivers installed and I suspect that has something to do with it).
Hello,
I’ve tried almost everything to solve that problem on my own – with no results.
Big THANK YOU.
Glad to help. This was a problem that took me a lot of trial and erroring to come up with a solution for. I’m glad it helped! :)
Thank you, Thank you, Thank you, I was facing this issue for a few weeks, now everything works like a charm. Again thanks for posting this wonderful solution.
Again, glad to help. :) It makes me day when I know I’ve saved someone some time.
hello, heroes. just looking for solution after wasting 2 days. i don’t know yet whether this tip will be helpful for me but anyway thank you all very much for such a great feat on the CR battle field. i believe in you. i believe in myself. thanks, blakepell, you’re great man. now i have to go.
Hope it helps! :) I struggled with workarounds for a long time before I figured this out. Best of luck to you. The Crystal Reports posts I’ve made for this fix have been the most popular on my blog that people come in to read. I would move to the newest version of Crystal if they could provide a good streamlined fix to this problem that worked across a wide array of connection types. I like Crystal for it’s all in one solution that doesn’t require a server piece (but can be integrated with a server piece).
The university that I work for it taking a long look at SQL Server Reporting Services again and it likely will be in our mix in the future so I’ll be able to take a look at how they currently stack up against Crystal.
Thanks for your comment, it’s much appreciated.
Hi,
I tried the same code to update the db connection in CR2008 from .net.
The changes are applied to tables in the Report. But if it has Synonyms or SQL query via command, the new db connection is not applied.
Do you have any suggestion on how to accomplish this?
Thanks,
Sree.
That’s a tough one. I use SQL query’s via command often and those do work for me with this code. Do you have Synonymns enabled in the options? File->Options->Database->Synonymns (I’m seeing this through Crystal 10′s IDE but there should be something similiar from the Visual Studio side). I don’t know if that will help or if it’s just for what the development environment shows you by default. I haven’t used Crystal with synonymns myself yet.
I’m having a similar problem, but with subreports that use stored procedures (vs. tables). The subreport link seems to disappear after changing the database/server. Like yours, it works fine if i just change the login info (user/pwd) and it works fine if my subreport contains just tables, but prompts me for the stored procedure parameters (which are linked in the original main/subreport) whenever i change the server/database. Any ideas would be appreciated!
This is huge.
This is something you would (normally) never consider, to swap the subs and main report order.
I don’t know how and what prompted you to try that, but thanks for a great discovery and thanks for sharing!
[...] we’ve got a ConnectionInfo we’ve got to apply it. The correct way seems to be to iterate [...]
[...] we’ve got a ConnectionInfo we have to apply it. The correct way seems to be to iterate [...]
Thanks *SO* much for this. As a little payback, for those who worship at the other church, here is a C# version (I removed the dependency on global variables too):
private void applyRuntimeConnectionInfo( ref ReportDocument report, string serverName, string dbName, bool useTrustedConnection, string userName = "", string password = "" )
{
if ( serverName.Length == 0 )
{
return;
}
foreach ( ReportDocument subReport in report.Subreports )
{
foreach ( Table crTable in subReport.Database.Tables )
{
TableLogOnInfo loi = crTable.LogOnInfo;
loi.ConnectionInfo.ServerName = serverName;
if ( useTrustedConnection )
{
loi.ConnectionInfo.IntegratedSecurity = true;
}
else
{
loi.ConnectionInfo.UserID = userName;
loi.ConnectionInfo.Password = password;
}
crTable.ApplyLogOnInfo( loi );
}
}
//Loop through each table in the report and apply the new login information (in our case, a DSN)
foreach ( Table crTable in report.Database.Tables )
{
TableLogOnInfo loi = crTable.LogOnInfo;
loi.ConnectionInfo.ServerName = serverName;
if ( useTrustedConnection )
{
loi.ConnectionInfo.IntegratedSecurity = true;
}
else
{
loi.ConnectionInfo.UserID = userName;
loi.ConnectionInfo.Password = password;
}
crTable.ApplyLogOnInfo( loi );
// If your DatabaseName is changing at runtime, specify the table location.
string location = "[" + dbName + "].[dbo].[" + crTable.Location.Substring( crTable.Location.LastIndexOf( "." ) + 1 ) + "]";
crTable.Location = location;
}
}
JP
Thanks for the conversion. Also, check out this post I made a few weeks ago. I recently had to make some changes in my Crystal Reports wrapper and I decided to move some of the logic above into extension methods which make it super handy to access via Visual Studio.
http://www.blakepell.com/Blog/?p=487
Whoops, one more change to make it so you don’t have to comment/uncomment code. Right at the end of the method, these two lines:
// If your DatabaseName is changing at runtime, specify the table location.
string location = "[" + dbName + "].[dbo].[" + crTable.Location.Substring( crTable.Location.LastIndexOf( "." ) + 1 ) + "]";
crTable.Location = location;
Wrap them like so:
// If your DatabaseName is changing at runtime, specify the table location.
if ( dbName.Length > 0 )
{
string location = "[" + dbName + "].[dbo].[" + crTable.Location.Substring( crTable.Location.LastIndexOf( "." ) + 1 ) + "]";
crTable.Location = location;
}
My apologies for the lack of formatting; was hoping wrapping this stuff in code tags would pick up my formatting. Not so much. Oh well ….
JP
Thanks for the code posting and I’m glad what I posted helped. :)
Thanks for the info; Got me most of the way to working.
In my case we use Oracle stored procs to obtain the data, and we discovered that we need to also update the Table.Location property to get things working. Hope this helps someone else save a tone of time…
Public Shared Sub CrystalLogin(mainInRD As ReportDocument, dataSource As String, userId As String, pwd As String)
Try
'now update logon info for all sub-reports
If Not mainInRD.IsSubreport AndAlso mainInRD.Subreports IsNot Nothing AndAlso mainInRD.Subreports.Count > 0 Then
For Each rd As ReportDocument In mainInRD.Subreports
CrystalLogin(rd, dataSource, userId, pwd)
Next
End If
Catch
End Try
'do the main reports database
Dim logonInfo As TableLogOnInfo = Nothing
For Each table As CrystalDecisions.CrystalReports.Engine.Table In mainInRD.Database.Tables
logonInfo = table.LogOnInfo
logonInfo.ConnectionInfo.ServerName = dataSource
logonInfo.ConnectionInfo.DatabaseName = ""
logonInfo.ConnectionInfo.UserID = userId
logonInfo.ConnectionInfo.Password = pwd
table.ApplyLogOnInfo(logonInfo)
Dim prop As PropertyInfo = Nothing
prop = table.[GetType]().GetProperty("RasTable", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim rasTable As ISCRTable = Nothing
rasTable = DirectCast(prop.GetValue(table, Nothing), ISCRTable)
table.Location = rasTable.QualifiedName
'Console.Out.WriteLine(table.Name)
'Console.Out.WriteLine(rasTable.QualifiedName)
Next
End Sub
Great feedback. Thanks for sharing Eric.
Si utilizas el metodo de conexion OLEDB (ADO ) para conectarte a SQL Server y crear tus reportes en Crystal Report, debes utilizar el Proveedor de OLE DB Microsoft SQL Server, si quieres funcionen tus reportes en Windows 64bits.
Ponle atencion a este detalle, a mi me costo 4 dias de investigacion. Probe cualquier cantidad de codigo y solo cambiando esto en el reporte me funcionó para Win64, toda mi programacion en Visual Studio 2010 estaba correcta.
Espero les sea de mucha utilidad.
Saludos,