Changing a Crystal Report datasource (access) at runtime in C# - c#

I'm getting the error 'Log on failed' when trying to change my datasource at runtime. I've trawled SO and the SAP site and from that put together the code below, but I'm still getting the error. My access db is 2013 and isn't password protected and I'm using the default "Admin" user. The report has an OLE DB connection. My code loops through all tables, including subreports and changes the login, and I also change the database login. Any help much appreciated:
public void RunTestReport()
{
DataSet testDataSet = new DL.NonConformance().GetNonConformances(1, "NonConformance");
var rpt = new ReportDocument();
rpt.Load(#"C:\Reports\rptTest.rpt");
SetCrystalLogin(rpt);
rpt.SetDataSource(testDataSet);
crystalReportViewer1.ReportSource = rpt;
crystalReportViewer1.Refresh();
}
public void SetCrystalLogin(ReportDocument oRpt)
{
ConnectionInfo oConnectInfo = new ConnectionInfo();
string dbpath = string.Concat(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), #"\db\dbacc.db");
oConnectInfo.ServerName = dbpath;
oConnectInfo.DatabaseName = string.Empty;
oConnectInfo.UserID = "Admin";
oConnectInfo.Password = string.Empty;
// Set the logon credentials for all tables
SetCrystalTablesLogin(oConnectInfo, oRpt.Database.Tables);
// Check for subreports
foreach (CrystalDecisions.CrystalReports.Engine.Section oSection in oRpt.ReportDefinition.Sections)
{
foreach (CrystalDecisions.CrystalReports.Engine.ReportObject oRptObj in oSection.ReportObjects)
{
if (oRptObj.Kind == CrystalDecisions.Shared.ReportObjectKind.SubreportObject)
{
// This is a subreport so set the logon credentials for this report's tables
CrystalDecisions.CrystalReports.Engine.SubreportObject oSubRptObj = oRptObj as CrystalDecisions.CrystalReports.Engine.SubreportObject;
// Open the subreport
CrystalDecisions.CrystalReports.Engine.ReportDocument oSubRpt = oSubRptObj.OpenSubreport(oSubRptObj.SubreportName);
SetCrystalTablesLogin(oConnectInfo, oSubRpt.Database.Tables);
}
}
}
oRpt.Refresh();
oRpt.SetDatabaseLogon("Admin", string.Empty, dbpath, string.Empty, true);
oRpt.VerifyDatabase();
oRpt.Refresh();
}
private void SetCrystalTablesLogin(CrystalDecisions.Shared.ConnectionInfo oConnectInfo, Tables oTables)
{
foreach (CrystalDecisions.CrystalReports.Engine.Table oTable in oTables)
{
CrystalDecisions.Shared.TableLogOnInfo oLogonInfo = oTable.LogOnInfo;
oLogonInfo.ConnectionInfo = oConnectInfo;
oTable.ApplyLogOnInfo(oLogonInfo);
}
}

Turns out you can't configure a .accdb datasource at runtime, I'll need to create an ODBC connection.

Related

Create crystal report at runtime using a remote sql server as data source

I had my database and applications (web, api & service bus that all create crystal reports at runtime) on the same server. All worked perfect creating reports at runtime.
Moved the database to a different server, which only allow remote connections from the app server.
For my database connections, all I had to do in my projects was change the server in the connection string from (local) to the ip address of the database server, and it all works fine.
However it seems that to just change the server from (local) to the ip address for the crystal reports does not work (give a "Database logon failed" error)
I'm not sure if this is the problem, but for creating the report locally (before uploading the .rpt to the server) I had to create a connection where the server is set to "local" ( in the data source location). Since I cannot access the new database remotely from my local machine, I cannot change that)
The code I use look as follow:
string server = ConfigurationManager.AppSettings["Server"];
string database = ConfigurationManager.AppSettings["Database"];
string user = ConfigurationManager.AppSettings["DatabaseUser"];
string password = ConfigurationManager.AppSettings["DatabasePassword"];
var report = new ReportClass {FileName = reportPath};
report.Load();
report.SetDatabaseLogon(user, password, server, database);
var parameterValue = new ParameterDiscreteValue {Value = item.Id};
var parameter = new ParameterValues {parameterValue};
report.DataDefinition.ParameterFields["#id"].ApplyCurrentValues(parameter);
report.ExportToDisk(ExportFormatType.PortableDocFormat, path);
Thank you, I ended up with this that worked (using sql)
Essentially the idea is setting the logon info not just for the report, but also for all subreports. Would have been nice if one could just do a "foreach subreport in report.." ,but seems not to work that way
public static void SetConnections(ReportDocument report)
{
Database database = report.Database;
Tables tables = database.Tables;
var crConnInfo = new ConnectionInfo
{
ServerName = ConfigurationManager.AppSettings["Server"],
DatabaseName = ConfigurationManager.AppSettings["Database"],
UserID = ConfigurationManager.AppSettings["DatabaseUser"],
Password = ConfigurationManager.AppSettings["DatabasePassword"]
};
foreach (Table table in tables)
{
TableLogOnInfo crLogOnInfo = table.LogOnInfo;
crLogOnInfo.ConnectionInfo = crConnInfo;
table.ApplyLogOnInfo(crLogOnInfo);
}
Sections crSections = report.ReportDefinition.Sections;
foreach (Section crSection in crSections)
{
ReportObjects crReportObjects = crSection.ReportObjects;
foreach (ReportObject crReportObject in crReportObjects)
{
if (crReportObject.Kind == ReportObjectKind.SubreportObject)
{
var crSubreportObject = (SubreportObject)crReportObject;
ReportDocument subRepDoc = crSubreportObject.OpenSubreport(crSubreportObject.SubreportName);
Database crDatabase = subRepDoc.Database;
Tables crTables = crDatabase.Tables;
foreach (Table crTable in crTables)
{
TableLogOnInfo crLogOnInfo = crTable.LogOnInfo;
crLogOnInfo.ConnectionInfo = crConnInfo;
crTable.ApplyLogOnInfo(crLogOnInfo);
}
}
}
}
}
I use Crystal report on oracle database, in my application i use this code:
CrystalDecisions.CrystalReports.Engine.ReportDocument rpt = new CrystalDecisions.CrystalReports.Engine.ReportDocument();
//reset connection
DBController.LogonEx(DBAlias, "", DBUsername, DBPassword);
//Create the QE (query engine) propertybag with the provider details and logon property bag
CrystalDecisions.ReportAppServer.DataDefModel.PropertyBag QE_Details = new CrystalDecisions.ReportAppServer.DataDefModel.PropertyBag();
ConnectionInfo ci = DBController.GetConnectionInfos(null)[0];
//copy from existing attributes except for server name
for (int idx = 0; idx < ci.Attributes.PropertyIDs.Count; idx++)
{
switch (ci.Attributes.PropertyIDs[idx])
{
case "QE_ServerDescription":
QE_Details.Add(ci.Attributes.PropertyIDs[idx], DBAlias);
break;
case "QE_LogonProperties":
//this is itself a property bag
CrystalDecisions.ReportAppServer.DataDefModel.PropertyBag logonDetails = new CrystalDecisions.ReportAppServer.DataDefModel.PropertyBag();
PropertyBag OLDLogon = (PropertyBag)ci.Attributes[ci.Attributes.PropertyIDs[idx]];
for (int idx2 = 0; idx2 < OLDLogon.PropertyIDs.Count; idx2++)
{
switch (OLDLogon.PropertyIDs[idx2])
{
case "Server":
case "Data Source":
logonDetails.Add(OLDLogon.PropertyIDs[idx2], DBAlias);
break;
default:
logonDetails.Add(OLDLogon.PropertyIDs[idx2], OLDLogon[OLDLogon.PropertyIDs[idx2]]);
break;
}
}
QE_Details.Add(ci.Attributes.PropertyIDs[idx], logonDetails);
break;
default:
QE_Details.Add(ci.Attributes.PropertyIDs[idx], ci.Attributes[ci.Attributes.PropertyIDs[idx]]);
break;
}
}
//now replace all existing connections with new one
CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfo newConnInfo = new CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfo();
newConnInfo.Attributes = QE_Details;
newConnInfo.Kind = CrystalDecisions.ReportAppServer.DataDefModel.CrConnectionInfoKindEnum.crConnectionInfoKindCRQE;
newConnInfo.UserName = DBUsername;
newConnInfo.Password = DBPassword;
foreach (CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfo oldConnInfo in DBController.GetConnectionInfos(null))
{
DBController.ReplaceConnection(oldConnInfo, newConnInfo.Clone(true), null, CrystalDecisions.ReportAppServer.DataDefModel.CrDBOptionsEnum.crDBOptionDoNotVerifyDB);
}
i hope this help

How to dynamically change crystal report database connection

I am new with crystal reports. I tried to to implement the crystal report in my win form c# application using report wizard visual studio 2012, so don't know what happen's in backhand for this. Everything works good on my computer but when i tried install this on another computer connection string changes and gives error.
I tried many links like Dynamic Connection string Change but as i am using report wizard for setup so don't know where to use this.
I also tried all options in report wizard for connection string but didn't find anything that change connection string at run time.
Is there any options by which i can attach connection String from app config at run time.
Try something like this:
strServer= ConfigurationManager.AppSettings["ServerName"].ToString();
strDatabase= ConfigurationManager.AppSettings["DataBaseName"].ToString();
strUserID= ConfigurationManager.AppSettings["UserId"].ToString();
strPwd= ConfigurationManager.AppSettings["Password"].ToString();
report.DataSourceConnections[0].SetConnection(strServer, strDatabase, strUserID, strPwd);
strServer= ConfigurationManager.AppSettings["ServerName"].ToString();
strDatabase= ConfigurationManager.AppSettings["DataBaseName"].ToString();
strUserID= ConfigurationManager.AppSettings["UserId"].ToString();
strPwd= ConfigurationManager.AppSettings["Password"].ToString();
//may be you need to set the integrated security to false, first.
report.DataSourceConnections[o].IntegratedSecurity = False;
report.DataSourceConnections[0].SetConnection(strServer, strDatabase, strUserID, strPwd);
Here's an example of changing all the main report tables as well as all subreports tables, to a newly specified TargetServer and TargetDatabase with Integrated Authentication:
using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;
// using CrystalDecisions.ReportAppServer.CommLayer; // not used directly, but this is needed in Project References.
//
// be sure to set "copy local" = true in the Project:
// see https://stackoverflow.com/questions/38025601/could-not-load-file-or-assembly-crystaldecisions-reportappserver-commlayer-ver
static ReportDocument crReportDocument;
static ConnectionInfo crConnectionInfo = new ConnectionInfo();
static public string TargetServer { get; set; }
static public string TargetDatabase { get; set; }
static void crAssignConnectionInfo()
{
crConnectionInfo.UserID = "";
crConnectionInfo.Password = "";
crConnectionInfo.DatabaseName = TargetDatabase;
crConnectionInfo.ServerName = TargetServer;
crConnectionInfo.IntegratedSecurity = true; // in case the report was saved with SQL authentication, switch to Integrated
}
static void SetSubreportLoginInfo(CrystalDecisions.CrystalReports.Engine.Sections objSections)
{
foreach (Section section in objSections)
{
foreach (ReportObject reportObject in section.ReportObjects)
{
SubreportObject crSubreportObject;
switch (reportObject.Kind)
{
case ReportObjectKind.SubreportObject:
crSubreportObject = (SubreportObject)reportObject;
ReportDocument subRepDoc = crSubreportObject.OpenSubreport(crSubreportObject.SubreportName);
if (subRepDoc.ReportDefinition.Sections.Count > 0) {
SetSubreportLoginInfo(subRepDoc.ReportDefinition.Sections);
}
Tables crTables = subRepDoc.Database.Tables;
foreach (Table table in crTables)
{
TableLogOnInfo tableLogOnInfo = new TableLogOnInfo();
tableLogOnInfo.ConnectionInfo.UserID = crConnectionInfo.UserID;
tableLogOnInfo.ConnectionInfo.Password = crConnectionInfo.Password;
tableLogOnInfo.ConnectionInfo.DatabaseName = crConnectionInfo.DatabaseName;
tableLogOnInfo.ConnectionInfo.ServerName = crConnectionInfo.ServerName;
tableLogOnInfo.ConnectionInfo.IntegratedSecurity = crConnectionInfo.IntegratedSecurity;
table.ApplyLogOnInfo(tableLogOnInfo);
}
break;
case ReportObjectKind.FieldObject:
case ReportObjectKind.TextObject:
case ReportObjectKind.LineObject:
case ReportObjectKind.BoxObject:
case ReportObjectKind.PictureObject:
case ReportObjectKind.ChartObject:
case ReportObjectKind.CrossTabObject:
case ReportObjectKind.BlobFieldObject:
case ReportObjectKind.MapObject:
case ReportObjectKind.OlapGridObject:
case ReportObjectKind.FieldHeadingObject:
case ReportObjectKind.FlashObject:
default:
// none of the other objects need to have login assigned
break;
}
}
}
}
static void SetCrystalDocumentLogon()
{
crAssignConnectionInfo();
TableLogOnInfo crTableLogonInfo = new TableLogOnInfo();
foreach (Table crTable in crReportDocument.Database.Tables)
{
try
{
crConnectionInfo.Type = crTable.LogOnInfo.ConnectionInfo.Type;
crTableLogonInfo.ConnectionInfo = crConnectionInfo;
crTableLogonInfo.ReportName = crTable.LogOnInfo.ReportName;
crTableLogonInfo.TableName = crTable.LogOnInfo.TableName;
crTable.ApplyLogOnInfo(crTableLogonInfo);
}
catch (Exception ex)
{
Console.WriteLine("Error during SetCrystalDocumentLogon " + ex.Message);
throw;
}
SetSubreportLoginInfo(crReportDocument.ReportDefinition.Sections);
}
}

Crystal Reports changing logon info at runtime doesn't work C#

I know there is a problem with this, as I found many threads about this, but nothing worked for me and I'm getting crazy !
I have to change logon informations when I generate my report.
But the ApplyLogOnInfo method always brings back the bad data.
I try to connect to an ODBC MySQL database.
Here is my code :
ReportDocument myReport = new ReportDocument();
myReport.Load("myReportPath");
ParameterField myParamFieldId = myReport.ParameterFields["param1"];
ParameterDiscreteValue param = new ParameterDiscreteValue();
param.Value = "param1";
myParamFieldId.CurrentValues.Add(param);
ParameterField myParamFieldNumRevision = myReport.ParameterFields["param2"];
ParameterDiscreteValue paramNumRevision = new ParameterDiscreteValue();
param.Value = "param2";
myParamFieldNumRevision.CurrentValues.Add(param);
ApplyLogOnInfo(myReport);
try
{
myReport.ExportToHttpResponse(ExportFormatType.PortableDocFormat, Response, true, "Rapport");
myReport.Close();
}
catch (Exception ex)
{
throw ex;
}
finally
{
myReport.Dispose();
}
private void ApplyLogOnInfo(ReportDocument rpt)
{
ConnectionInfo connInfo = new ConnectionInfo();
connInfo.ServerName = "test";
connInfo.DatabaseName = "test";
connInfo.UserID = "test";
connInfo.Password = "test";
connInfo.Type = ConnectionInfoType.CRQE;
connInfo.IntegratedSecurity = false;
foreach (CrystalDecisions.CrystalReports.Engine.Table table in rpt.Database.Tables)
{
TableLogOnInfo logonInfo = table.LogOnInfo;
logonInfo.ConnectionInfo = connInfo;
table.ApplyLogOnInfo(logonInfo); // Here, old values are brought back
}
// Idem on Sub-reports
foreach (ReportDocument sousRpt in rpt.Subreports)
{
ReportDocument rptSub = rpt.OpenSubreport(sousRpt.Name);
foreach (CrystalDecisions.CrystalReports.Engine.Table table in rptSub.Database.Tables)
{
TableLogOnInfo logonInfo = table.LogOnInfo;
logonInfo.ConnectionInfo = connInfo;
table.ApplyLogOnInfo(logonInfo);
}
}
}
I saw that this could be due to sub-reports and parameters. No way to get this to work.
I tried also:
rpt.DataSourceConnections[0].SetConnection("test", "test", "test", "test");
// But DataSourceConnections[0].Attributes and DataSourceConnections[0].LogonProperties still store values from the old connection, and doesn't work. I tried to clear it does'nt apply.
// I tried to pass parameters like this
myReport.SetParameterValue("param1", "test");
myReport.SetParameterValue("param1", "test");
// I tried with dataset and fill method
Well now I'm stuck, and really need your help !! Thanks
I realize this is months later and you may have solved it. In upgrading our Crystal Reports from 2008 to 2012, I noticed there were issues with how we were passing parameters and login credentials. Overall, everything was not working as I had hoped.
I decided it was best to write a new function that was compatible in both 2008 and 2012, and was overall more flexible. It takes the viewer, source, report path, and parameters to load it all up.
I just started using this in our Production environment for 2008 for a couple reports with great results. I would suggest you modify it a bit further if it does not work exactly as you may have hoped for sub-reports that may or may not come from a different ODBC login credentials.
public void LoadCrystalReportViewer(CrystalReportViewer crystalReportViewer, CrystalReportSource crystalReportSource, string reportFilePath, Dictionary<string, object> parameters)
{
crystalReportSource.ReportDocument.FileName = reportFilePath;
crystalReportSource.ReportDocument.Load(reportFilePath);
crystalReportSource.ReportDocument.Database.Tables[0].LogOnInfo.ConnectionInfo.Password = "PASSWORD";
crystalReportSource.ReportDocument.Database.Tables[0].ApplyLogOnInfo(crystalReportSource.ReportDocument.Database.Tables[0].LogOnInfo);
foreach (KeyValuePair<string, object> parameter in parameters)
{
if (crystalReportSource.ReportDocument.ParameterFields[parameter.Key] != null)
{
crystalReportSource.ReportDocument.SetParameterValue(parameter.Key, parameter.Value);
}
}
crystalReportSource.EnableCaching = true;
crystalReportSource.CacheDuration = 300;
crystalReportViewer.ReportSource = crystalReportSource;
crystalReportViewer.ReuseParameterValuesOnRefresh = true;
crystalReportViewer.EnableParameterPrompt = false;
crystalReportViewer.LogOnInfo.Add(crystalReportSource.ReportDocument.Database.Tables[0].LogOnInfo);
foreach (ParameterField parameterField in crystalReportViewer.ParameterFieldInfo)
{
parameterField.DefaultValues = crystalReportSource.ReportDocument.ParameterFields[parameterField.Name].CurrentValues;
}
crystalReportViewer.Visible = true;
}

Crystal Report Connection string from properties.settings winform c#

Is it possible to set the connection string of crystal report from the properties.settings of a winform in c# like the following?
this is just my assumptions
rpt.connectionString = Properties.Settings.Default.ConnectionString
This is my code for managing logins/connectionstrings (dbLogin is just a simple class for storing information, you can replace that with string values).
//Somewhere in my code:
foreach (CrystalDecisions.CrystalReports.Engine.Table tbCurrent in rdCurrent.Database.Tables)
SetTableLogin(tbCurrent);
//My set login method
private void SetTableLogin(CrystalDecisions.CrystalReports.Engine.Table table)
{
CrystalDecisions.Shared.TableLogOnInfo tliCurrent = table.LogOnInfo;
tliCurrent.ConnectionInfo.UserID = dbLogin.Username;
tliCurrent.ConnectionInfo.Password = dbLogin.Password;
if(dbLogin.Database != null)
tliCurrent.ConnectionInfo.DatabaseName = dbLogin.Database; //Database is not needed for Oracle & MS Access
if(dbLogin.Server != null)
tliCurrent.ConnectionInfo.ServerName = dbLogin.Server;
table.ApplyLogOnInfo(tliCurrent);
}
In case you already have a successfull connectiont to SQL Server, you may use the following static method to set your report connection based on your SqlConnection:
using System.Text;
using CrystalDecisions.Shared;
using System.Data.SqlClient;
namespace StackOverflow
{
public class MyCrystalReports
{
// This method will allow you may easily set report datasource based on your current SqlServerConnetion
public static void SetSqlConnection(CrystalDecisions.CrystalReports.Engine.ReportClass MyReport, SqlConnection MySqlConnection)
{
// You may even test SqlConnection before using it.
SqlConnectionStringBuilder SqlConnectionStringBuilder = new SqlConnectionStringBuilder(MySqlConnection.ConnectionString);
string ServerName = SqlConnectionStringBuilder.DataSource;
string DatabaseName = SqlConnectionStringBuilder.InitialCatalog;
Boolean IntegratedSecurity = SqlConnectionStringBuilder.IntegratedSecurity;
string UserID = SqlConnectionStringBuilder.UserID;
string Password = SqlConnectionStringBuilder.Password;
// Of course, you may add extra settings here :D
// On Crystal Reports, connection must be set individually for each table defined on the report document
foreach (CrystalDecisions.CrystalReports.Engine.Table Table in MyReport.Database.Tables)
{
CrystalDecisions.Shared.TableLogOnInfo TableLogOnInfo = Table.LogOnInfo;
TableLogOnInfo.ConnectionInfo.ServerName = ServerName;
TableLogOnInfo.ConnectionInfo.DatabaseName = DatabaseName;
TableLogOnInfo.ConnectionInfo.IntegratedSecurity = IntegratedSecurity;
if (IntegratedSecurity != true)
{
TableLogOnInfo.ConnectionInfo.UserID = UserID;
TableLogOnInfo.ConnectionInfo.Password = Password;
}
Table.ApplyLogOnInfo(TableLogOnInfo);
}
}
}
}

Crystal report datsource Set by ConnectionInfo is not getting set- Does not connect to right database

I have 8 reports that use the exact same code to set the reporting datasource out of which 5 reports work and are able to point to Production environment. The rest of the 3 reports, I have ran and rerun database verify and database update yet when I run these reports on production, they bring data back from DEV environment.
Here is how my datasource is getting set.
I call stored procedure in all 8 crystal reports .
I have done very detailed debugging and verified that the datasource data is getting the correct information so what is missing.
string database = ConfigurationManager.AppSettings[env + "Database"].ToString();
string server = ConfigurationManager.AppSettings[env + "Server"].ToString();
CrystalReportViewer1.ParameterFieldInfo = fields;
rptDoc.Load(Server.MapPath(report));
ConnectionInfo connectionInfo = Reports.GetConnectionInfo(server, database, "userID", "password");
//connectionInfo.Attributes = attributes;
connectionInfo.Type = ConnectionInfoType.SQL;
SetDBLogonForReport(connectionInfo, env);
CrystalReportViewer1.ReportSource = rptDoc;
private void SetDBLogonForReport(ConnectionInfo oConnectionInfo, string env)
{
try
{
TableLogOnInfos oTableLogOnInfos = CrystalReportViewer1.LogOnInfo;
string[] sparams = new string[]{
};
foreach (CrystalDecisions.CrystalReports.Engine.Table oTable in rptDoc.Database.Tables)
{
if (oTable.LogOnInfo.ConnectionInfo.ServerName == oConnectionInfo.ServerName)
{
TableLogOnInfo oTableLogOnInfo = oTable.LogOnInfo;
oTableLogOnInfo.ConnectionInfo = oConnectionInfo;
oTable.ApplyLogOnInfo(oTableLogOnInfo);
// oTable.Location = String.Format( "{0}.dbo.{1}", oConnectionInfo.DatabaseName, oTable.Name );
bool b = oTable.TestConnectivity();
if (!b)
{
invokeErrorLogger(sparams, env);
}
}
}
}
catch
{
throw;
}
}
The reports that work are already pointing to your prod server. You are only applying the logon credentials if the server stored in the report matches the server name you are trying to set. Change to the following:
// Clear existing connection info first
rptDoc.DataSourceConnections.Clear();
foreach (CrystalDecisions.CrystalReports.Engine.Table oTable in rptDoc.Database.Tables)
{
var oTableLogonInfo = oTable.LogonInfo;
oTableLogonInfo.ConnectionInfo = oConnectionInfo;
oTable.ApplyLogOnInfo(oTableLogonInfo);
bool b = oTable.TestConnectivity();
if (!b)
{
invokeErrorLogger(sparams, env);
}
}

Categories