My company has a Windows Forms application that leverages ClickOnce and .NET Remoting (soon to be WCF) to a back end IIS web application that handles all data access operations. For reporting, we currently use SSRS and have the distributed clients connect directly to the report server.
We would like to potentially remove, or augment, our dependency on SSRS reports with the ability to support local reporting via RDLC files. One hurdle that I have to overcome is that it is unlikely that the individual client machines will have direct access to the database and therefore would require the report data to be fetched from the web application over our Remoting or WCF transport layer.
"Discovering" the parameters of the report for purposes of dynamically building a UI of report parameter prompts I don't think is terribly difficult, but actually telling the back-end system what class/method to invoke in order to return the correct data for the report is less simple.
Has anyone experimented with somehow embedding information into the RDLC file (either through a comment in the report or otherwise) that can be used as a "hint" to the server application layer for determining what method to execute? It's likely that the actual RDLC will be stored in the database versus being distributed with our application.
Any insight or guidance would be appreciated.
-MrB
Check out www.gotreportviewer.com and the RDL viewer example (last sample on right side). It already has the code to load up the RDLC and parse the XML file to get the parameters out as well as the connection information and query information. Armed with that you should be able to use the backend to get everything and populate/load the report and run it.
As far as adding a hint or comment in the RDLC somewhere to specify what call to make I would suggest perhaps instead just using the report name as the hint. We've done this in the past for our reports to know what to call to load the data.
We've done something like this before :
VB Version:
Select Case GetReportName()
Case "SiteEval"
Using adp As New DataSetsTableAdapters.SiteEvalTableAdapter, _
objDT As New DataSets.SiteEvalDataTable
adp.Fill(objDT)
objLR.DataSources.Add(New ReportDataSource("DataSets_SiteEval", objDT))
End Using
Case ....
C# version:
switch (GetReportName()) {
case "SiteEval":
using (DataSetsTableAdapters.SiteEvalTableAdapter adp = new DataSetsTableAdapters.SiteEvalTableAdapter()) {
using (DataSets.SiteEvalDataTable objDT = new DataSets.SiteEvalDataTable()) {
adp.Fill(objDT);
objLR.DataSources.Add(new ReportDataSource("DataSets_SiteEval", objDT));
}
}
break;
Leveraging our XSD's and datasets to get our reports loading. In this case our local reports have access to the DB. In your case you could take this idea and fill the dataset with your own returned datatable from a call into your service. It's not the most elegant or easiest to maintain if you have a large set of reports.
In our case, we are now taking the RDL Viewer example and modifying it to our needs so that the above code is not needed. We will just pass in the path to the RDLC file and the code will load what is needed by reading the RDLC's xml. However, in this case the local report has access to the DB. It would not be to difficult I think to modify it to get it's data from an outside source either using the above code idea or modifying the RDL Viewer example from www.gotreportviewer.com as we are now doing.
With the RDL Viewer example modified we have a little something like this going at the moment (still working it out...) Code is in VB.
Dim r As New Report(Server.MapPath("App_Reports/" & GetReportName() & ".rdlc"), GetReportName())
Dim p As ReportParameterInfoCollection = r.GetParameters() 'read only....
If p.Count > 0 Then
Dim rptParams(p.Count - 1) As ReportParameter
Dim i As Integer = 0
For Each param In p
rptParams(i) = New ReportParameter(param.Name)
rptParams(i).Values.Add("99999999")
i += 1
Next
r.SetParameters(rptParams)
End If
r.LoadReport()
Goodbye ugly and long switch statement. Goodbye needing to know how to load the report....
Now if we can just figure out how to handle parameters a little nicer.... This code is a current work in progress but the RDL Viewer sample got us started quickly....
I also am playing with doing the same thing with Reflection. I found this article that makes the reflection work a breeze.
http://www.slimee.com/2009/09/net-using-reflection-to-execute.html
Now all you would do is pass in the string of your data table and it will produce the datatable driving the report. Since with datasets you will always know how it creates the names you can easily work with this.
The way I see it is both approaches are solutions that work at eliminating the ugly switch statements and will make the code easier to maintain. With reflection the code is much smaller but may be a bit slower.
Both have a bit of the magic string issue in them. Somewhere along the line you have to pass in a string of what you want to run. A team could easily create a convention that to easily solve though....
Related
I'm looking to break my access applications out into their component pieces so that I can use some version of source control with it. I currently have separated the front end and back end database. However I would like to go one step further and separate out the front end logic in the forms into their component files. If someone could point me in the right direction it would be much appreciated.
Access for about 20 years has supported the standard source code control interface.
The fact that the project is stored in one file is MOOT since Access can “logically” view each individual item (forms, reports, query, code etc.) as a separate object. So don’t confuse a logical view of the application vs that of physical.
There are quite a few posts on SO that outline this, and this post here gives some more information:
Version control for VBA file
When you use SCC with Access, then you see this:
if using Visual Source safe, you see this:
If using Team foundation server, you see this:
And the UI inside of Access shows the status of such objects. eg this:
There also add ins for git hub etc.
Keep in mind that Access 2010 was/is the last version to support SCC. If using 2013, then you have to use the noted "save-as-text" to send out each part and code as a standard text file which then of course can be used by any standard source code provider/control system.
i want to write a loop which reads a row from sql data table, take the attributes and save it to variables. the variables are parameters for a webservice. i have 103 columns but most time not all are used, i only want to get out the attributes which are used. the other variables shall stay at there initialized value.
Anybody knows a fast way to do it? a code example would be cool, but a hint were also good enough for the beginning.
You haven't exactly demonstrated what you've already tried so I'm not inclined to give you a completely functioning solution however the following snippets and links might prove useful:
Traversing SQL Results
There are obviously many ways of doing this I tend to use a SqlDataReader
Using connection As New SqlConnection(ConfigurationManager.ConnectionStrings("ConnectionStringName").ConnectionString)
Using command As New SqlCommand("storedProcedureName", connection)
command.CommandType = Data.CommandType.StoredProcedure
'** Add Parameters if required **'
command.Parameters.AddWithValue("parameterName", parameterValue)
connection.Open()
Using dataReader = command.ExecuteReader()
While dataReader.Read
'** Put code here for interacting with the web service **'
End While
End Using
connection.Close()
End Using
End Using
Getting the data from the columns is actually quite easy, you can access them explicitly by column name or by index and getting ordinal to see what it's value is. When a column returns DBNull, determined using a call such as IsDBNull(columnValue), then the value has not been populated in the database. Given the number of columns you are dealing with I would recommend using the SqlDataReader.Item Property to get the value and SqlDataReader.GetOrdinal Method for getting the name of the column.
Interacting with a Web Service
A quick google (other search engines are available) will give you plenty of examples on how to consume a web service in VB.NET. Here's a few of the examples I think you should consider looking at for this part:
Code Project: Using a Web Service in VB.NET
Microsoft: How to access a Web service in a Windows-based application by using Visual Basic 2005 or Visual Basic .NET
Stack Overflow: Consuming Web Services in VB.NET
The above examples will be much more eloquent in their examples and will give you a better understanding than I can in a small answer box so I am not going to write code snippets for this part.
I want to automate CR report printing from VB/VBA.
I have Crystal Reports XI Developer installed and .rpt files (some requiring parameters) to run and send to a printer, without requiring any user intervention.
Now, I stuck at the very first step, adding the correct libraries. There are probably about 50 libraries or so that start with Crystal Reports or Business Objects, and I have no idea which ones I need.
Any pointers to get me started would be appreciated.
Thanks
Martin
Ok, I solved my problem.
CRAXDRT.dll aka Crystal Reports ActiveX Designer Run Time Library is the correct library.
Here is some sample code:
Dim CR As New CRAXDRT.Application
Dim rep As CRAXDRT.Report
Set rep = CR.OpenReport(Range("CRpath"))
rep.ParameterFields(1).AddCurrentValue "Boston"
rep.ParameterFields(2).AddCurrentValue "Cars"
rep.Database.Tables(1).ConnectionProperties("Password").Value = "pw12345"
rep.ReadRecords
rep.PrintOut promptUser:=False, numberOfCopy:=1 ' promptUser:=True doesn't work
It depends on your budget, but I recommend a 3rd party program called Visual Cut (http://www.milletsoftware.com/Visual_CUT.htm). Using either a command line or the built-in GUI, you can process Crystal Report files pretty much any way you want, including PDFing, emailing, and even printing directly to a printer. So you can skip the VBA entirely if you want.
It's been around since 2002 and the developer has been continuously adding custom features to it by customer request, so it suffers a little from 'feature creep'. However, the manual (downloadable for free from the website) does a good job of keeping it all in perspective. Also, if you get the maintenance agreement, the developer is very responsive. In my case, he usually returns my calls within the day.
*(This isn't the developer talking, just a satisfied customer)
We are running Crystal Reports on a Windows Server 2008 with .NET framework 3.5 SP1.
I have seen many causes of the general error "The request could not be submitted for background processing." on other forums, however they tend to be persistent and repeatable affecting just a single report due to a specific formatting issue with a specific report.
We are seeing this error with the below stack trace, intermittently.
It affects multiple different reports we have.
It affects one particular report more frequently than other reports.
Once a report is affected the same error will often appear in multiple reports at approximately the same time eg. for the next 10 minutes.
The same report run with the same parameters may work when run again (soon after) or the application may need restarting before the report can be successfully re run.
These reports all worked previously without issue. No change in server or code seems apparent which would have precipitated this error. All code behind for this is VB.NET
We have had difficulty reproducing it in test environments and upgrading to the latest version of Crystal has not helped at all.
Any help or suggestions that you might be able to make to resolve this issue would be appreciated.
"The request could not be submitted for background processing."
at CrystalDecisions.ReportAppServer.Controllers.DatabaseControllerClass.ReplaceConnection(Object oldConnection, Object newConnection, Object parameterFields, Object crDBOptionUseDefault)
at CrystalDecisions.CrystalReports.Engine.Table.SetDataSource(Object val, Type type)
at CrystalDecisions.CrystalReports.Engine.ReportDocument.SetDataSourceInternal(Object val, Type type)
--- End of inner exception stack trace ---
at CrystalDecisions.ReportAppServer.ConvertDotNetToErom.ThrowDotNetException(Exception e)
at CrystalDecisions.CrystalReports.Engine.ReportDocument.SetDataSourceInternal(Object val, Type type)
at CrystalDecisions.CrystalReports.Engine.ReportDocument.SetDataSource(DataSet dataSet)
at "USER CODE"
After many days, finally I discovered what is the root of problem, in case you are including jpg images in your report.
The thing is that CR for VS2008 or later versions, can’t handle jpg files in CMYK mode. CR only can handle jpg files in RGB mode.
It’s funny that lower versions of CR (the one that came with VS2003) could handle any kind of jpg files. Thanks, Crystal.
For me the issue was with the Temporary Crystal Report that gets generated in the TEMP folder in Windows. There is a limit to the number of Temporary Crystal Reports that can be generated by Crystal report engine while processing it in a loop. Either the space in Temp folder runs out due to low memory in C drive or the limit of reports is reached after which in one single run crystal report cannot export further. It will give the error mentioned in question.
For me this issue was recurring at every 500 reports that were processed (I was generating the reports say, for a year and exporting them to a system folder one by one using my application)
The solution is simple. Always close and dispose the temporary .rpt Crystal Report file after exporting it .
for i as integer=0 to reportcount -1
Dim rpt as New MyCrystalReport
Dim filename as String = "MyReport" & i & ".Pdf"
//Query the DB obtain the dataset then set the datasource to the report
...
//Export the report
rpt.ExportToDiskCrystalDecisions.Shared.ExportFormatType.PortableDocFormat,fileName)
rpt.Close()
rpt.Dispose()
next
Isolate the report generation code.
Our final resolution was to take the code that was generating the report and move it into its own isolated service. Our original service then calls our new Crystal service with the relevant parameters and Crystal RPT file. This is obviously a costly solution as it involves modifying all report generation code to call the Crystal service instead. The Crystal service does not exhibit the error. The code had not changed besides that, so we can only presume the cause of the error was some interaction of the Crystal reports engine and the environment within our application.
Is there a chance the report object is leaked in the server's memory? I ran into a similar case where the report object was being stored into a Session object, so the report didn't need to get reloaded as the user navigated between pages. However, when the user was done with the report, the object remained in the Session, and wasn't cleaned up properly when the Session was destroyed by the server. I had to add a bit of code in the Session_End event in global.asax to find the report object and call the dispose method on it.
The fact that this appears intermittently but then affects all reports for a matter of 10 minutes makes me think it could be session-related. In my situation the server reached a limit on the number of reports that could be created on the server (in memory) because they weren't being released. The symptoms were similar to yours.
Hope this helps!
Try this: If you left any blank space at crystal report(header,footer or any sections) suppress it. that's all. I had this problem and i fixed this way.
I too have come across such issue, where I figured out the Column having Photographs was creating the issue. The way out was to convert the photograph (Datatype Image in SQL Server) from .NET Data set to byte and then save it as Bitmap. After , that this same BMP file can be converted to bytes and replaced to appropriate column of the identified row. By this the space reduced to a great extend and then after exporting the Report document and Datatable was disposed properly.
I would like to be able to loop through all of the defined parameters on my reports and build a display string of the parameter name and value. I'd then display the results on the report so the user knows which parameters were used for that specific execution. The only problem is that I cannot loop through the Parameters collection. There doesn't seem to be an indexer on the Parameters collection, nor does it seem to implement IEnumerable. Has anyone been able to accomplish this? I'm using SSRS 2005 and it must be implemented within the Report Code (i.e., no external assembly). Thanks!
Unfortunately, it looks like there's no simple way to do this.
See http://www.jameskovacs.com/blog/DiggingDeepIntoReportingServices.aspx for more info. If you look at the comments of that post, there are some ways to get around this, but they're not very elegant. The simplest solution will require you to have a list of the report parameters somewhere in your Report Code, which obviously violates the DRY principle, but if you want the simplest solution, you might just have to live with that.
You might want to rethink your constraint of no external assembly, as it looks to me that it would be much easier to do this with an external assembly. Or if your report isn't going to change much, you can create the list of parameter names and values manually.
If I'm understanding your question, just do what I do:
Drop a textbox on the report, then while you are setting up the report, insert the following:
="Parameter1: " + Parameters!Parameter.Label + ", Parameter2: " + Parameters!Parameter2.Label...
Granted, it's not the prettiest thing, but it does work pretty well in our app.
And I'm using Labels instead of Values since we have datetime values, and the user only cares about either the short date or the month and year (depending on circumstance), and I've already done that formatting work in setting up the parameters.
I can think of at least two ways to do this. The first might work, the second will definitely work.
Use the web service. I'm pretty sure I saw API for getting a collection of parameters. Even if there's no direct access you can always create a standard collection and copy the ReportParameter objects from one to the other in a foreach loop - and then access Count, with individual parameter properties available by dereferencing the ReportParameter instances.
Reports are RDL. RDL is XML. Create an XmlDocument and load the RDL file, then use the DOM to do, well, anything you like up to and including setting default values or even rewriting connection strings.
If your app won't have file-system access to the RDL files you can get them via the web service.