在Crystal Reports (Visual Studio 2012)中动态更改数据库类型、源等
本文关键字:动态 数据库 类型 源等 Reports Crystal Visual 2012 Studio | 更新日期: 2023-09-27 18:01:40
我在StackOverflow上研究了许多动态更改数据源连接的不同方法。我使用并验证了几乎所有我能找到的c#和vb.net示例,但不知何故,事情不会像他们应该的那样工作。
我们项目的想法是将使用xBase dll的旧报表的数据源连接更改为使用vfpoledb提供程序的crdb_ado.dll,以连接到Visual Foxpro DBF文件(每个文件代表一个表)。
我在这里下载了最新的Crystal Developer Version for Visual Studio (2012): http://scn.sap.com/docs/DOC-35074为了能够在VS中直接使用这些组件,而不需要引用Program Files - Business Object目录中的dll(如在其他示例中所见)。
我尝试通过VS调试器验证旧的"更新"报告的内容(见下面的代码)以及Crystal Reports中新创建的报告(最新版本,它使用正确的路径和设置连接到dbf),因此它们匹配。
然而,我遇到了这些问题:
- 代码可以更改所有参数和属性,但是保存会丢弃所有更改(文件不是只读的)
- 改变表。location抛出com异常(翻译为:无法加载数据库数据,报告错误)
- 当将报告文档对象"附加"到水晶报表查看器时也会发生同样的情况。
- reportDocument.VerifyDatabase()明显失败
- 就我所见,所有设置在两个文件中都是一样的
开发环境是windows 7/64bit/VS 2012 Pro。我们所有的报告都是用Crystal reports的版本9和11创建的。
下面是一个改编的代码示例,其中确实改变了的所有属性或参数(除了表位置)。我还使用了使用Propertybag对象的示例,但这些都不起作用。reportDocument1.Load("path to document");
// also tried adding these two lines as a test
reportDocument1.DataSourceConnections.Clear();
reportDocument1.DataSourceConnections[0].SetConnection(@"c:'testreports","",false);
//changing of table data connections
foreach (Table table in reportDocument1.Database.Tables)
{
ChangeTableLogonInfo(table);
}
// ---
private void ChangeTableLogonInfo(Table table)
{
// server = place containing *.DBF files
table.LogOnInfo.ConnectionInfo.ServerName = @"c:'testreports'";
// set to empty string (looking at generated report in CrRep)
table.LogOnInfo.ConnectionInfo.DatabaseName = "";
table.LogOnInfo.ConnectionInfo.UserID = "";
table.LogOnInfo.ConnectionInfo.Password = "";
// create logon properties
var connectionAttributes = new DbConnectionAttributes();
connectionAttributes.Collection.Set("Collating Sequence","Machine");
connectionAttributes.Collection.Set("Data Source", @"c:'testreports");
connectionAttributes.Collection.Set("Locale Identifier", 1033);
connectionAttributes.Collection.Set("OLE DB Services", -5);
connectionAttributes.Collection.Set("Provider", "VFPOLEDB"); //eg: SQLOLEDB
connectionAttributes.Collection.Set("Use DSN Default Properties",false);
// CLEAR and SET NEW attributes for the given table
table.LogOnInfo.ConnectionInfo.Attributes.Collection.Clear();
table.LogOnInfo.ConnectionInfo.Attributes.Collection.Add(new NameValuePair2 { Name = "Database DLL", Value = "crdb_ado.dll" });
table.LogOnInfo.ConnectionInfo.Attributes.Collection.Add(new NameValuePair2 { Name = "QE_DatabaseName", Value = "" });
table.LogOnInfo.ConnectionInfo.Attributes.Collection.Add(new NameValuePair2 { Name = "QE_DatabaseType", Value = "OLE DB (ADO)" });
table.LogOnInfo.ConnectionInfo.Attributes.Collection.Add(new NameValuePair2 { Name = "QE_LogonProperties", Value = connectionAttributes });
table.LogOnInfo.ConnectionInfo.Attributes.Collection.Add(new NameValuePair2 { Name = "QE_ServerDescription", Value = @"c:'testreports" });
table.LogOnInfo.ConnectionInfo.Attributes.Collection.Add(new NameValuePair2 { Name = "QE_SQLDB", Value = true });
table.LogOnInfo.ConnectionInfo.Attributes.Collection.Add(new NameValuePair2 { Name = "SSO Enabled", Value = false });
// gives a COM error
try
{
table.Location = "some-table-name";
}
catch (Exception e)
{
// handling
}
}
我们确实注意到Crystal Reports本身有些"烦人"的地方。我想知道这是否与它有关:
当我们想通过CR本身更改xbase连接时,我们不能在不丢失报告中所有字段的情况下将其从"xbase连接dbf"指向"vfpoledb连接dbf"(如果您理解我的意思)。
我尝试安装32位和64位版本的VS CR开发人员版,但它似乎没有改变任何东西。
同样,下一段代码在Visual FoxPro中工作(并且我能够在Crystal Reports Document查看器中读取这些转换后的文件)。这是很烦人的,它不能很容易地在c#中完成(似乎:))
lotest = CREATEOBJECT("crystalruntime.application.9")
lorap = lotest.openreport("c:'factuur.rpt")
loData = loRap.Database
LOCAL lnI
lnI = 1
FOR EACH loTable IN lodata.tables
loconn = loTable.connectionproperties
loTable.dllname = "crdb_odbc.dll"
loConn.DeleteAll
IF lnI = 1
loCOnn.Add("Database", "Hoofding")
ELSE
loCOnn.Add("Database", "Detail")
ENDIF
loConn.Add("Database Type","ODBC")
loConn.Add("DSN","DBFACTw")
lnI = lnI + 1
ENDFOR
loRap.Saveas("c:'Factuur2.rpt",2048)
有什么想法或建议吗?由于
我也遇到过同样的问题,只过了一个星期我就解决了。
CrystalDecisions.Shared.Table
的ApplyLogOnInfo
用报告文件中的原始属性覆盖ConnectionInfo
,只更新用户名和密码。
你必须用PropertyBag
代替DbConnectionAttributes
和CrystalDecisions.ReportAppServer.DataDefModel.Table
代替CrystalDecisions.Shared.Table
。
PropertyBag connectionAttributes = new PropertyBag();
connectionAttributes.Add("Auto Translate", "-1");
connectionAttributes.Add("Connect Timeout", "15");
connectionAttributes.Add("Data Source", Server);
connectionAttributes.Add("General Timeout", "0");
connectionAttributes.Add("Initial Catalog", Database);
connectionAttributes.Add("Integrated Security", false);
connectionAttributes.Add("Locale Identifier", "1040");
connectionAttributes.Add("OLE DB Services", "-5");
connectionAttributes.Add("Provider", "SQLOLEDB");
connectionAttributes.Add("Tag with column collation when possible", "0");
connectionAttributes.Add("Use DSN Default Properties", false);
connectionAttributes.Add("Use Encryption for Data", "0");
PropertyBag attributes = new PropertyBag();
attributes.Add("Database DLL", "crdb_ado.dll");
attributes.Add("QE_DatabaseName", Database);
attributes.Add("QE_DatabaseType", "OLE DB (ADO)");
attributes.Add("QE_LogonProperties", connectionAttributes);
attributes.Add("QE_ServerDescription", Server);
attributes.Add("QESQLDB", true);
attributes.Add("SSO Enabled", false);
CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfo ci = new CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfo();
ci.Attributes = attributes;
ci.Kind = CrConnectionInfoKindEnum.crConnectionInfoKindCRQE;
ci.UserName = Username;
ci.Password = Password;
foreach (CrystalDecisions.ReportAppServer.DataDefModel.Table table in Report.ReportClientDocument.DatabaseController.Database.Tables)
{
CrystalDecisions.ReportAppServer.DataDefModel.Procedure newTable = new CrystalDecisions.ReportAppServer.DataDefModel.Procedure();
newTable.ConnectionInfo = ci;
newTable.Name = table.Name;
newTable.Alias = table.Alias;
newTable.QualifiedName = Database + ".dbo." + table.Name;
Report.ReportClientDocument.DatabaseController.SetTableLocation(table, newTable);
}
foreach (ReportDocument subreport in Report.Subreports)
{
foreach (CrystalDecisions.ReportAppServer.DataDefModel.Table table in Report.ReportClientDocument.SubreportController.GetSubreportDatabase(subreport.Name).Tables)
{
CrystalDecisions.ReportAppServer.DataDefModel.Procedure newTable = new CrystalDecisions.ReportAppServer.DataDefModel.Procedure();
newTable.ConnectionInfo = ci;
newTable.Name = table.Name;
newTable.Alias = table.Alias;
newTable.QualifiedName = Database + ".dbo." + table.Name;
Report.ReportClientDocument.SubreportController.SetTableLocation(subreport.Name, table, newTable);
}
}
我希望这对其他开发人员也有帮助,因为在SAP文档或支持论坛中几乎不可能找到这些信息。
Neon的回答对我帮助很大。除非服务器是相同的,否则我无法让大多数人谈论的表循环ApplyLogOnInfo方法工作。它只会用报告文件中的原始值替换所有内容,就像他观察到的那样。
在我的情况下,我只是试图使用web.config中的连接字符串在dev/test/prod数据库之间切换。使用databascontroller。ReplaceConnection最终对我起了作用。我不得不克隆(深度复制)现有的ConnectionInfo,并对几个相关属性进行更改,而不是尝试从头开始构建一个或直接修改现有的。
public static void SetReportLogOnInfo(ReportDocument report)
{
if (!report.IsLoaded)
report.Refresh(); //workaround for report.FileName empty error
foreach (CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfo oldInfo in report.ReportClientDocument.DatabaseController.GetConnectionInfos())
{
var newInfo = oldInfo.Clone(true);
newInfo.UserName = [ConnectionString.UserID];
newInfo.Password = [ConnectionString.Password];
newInfo.Attributes["QE_LogonProperties"]["Server"] = [ConnectionString.DataSource];
report.ReportClientDocument.DatabaseController.ReplaceConnection(oldInfo, newInfo, null, CrDBOptionsEnum.crDBOptionDoNotVerifyDB);
}
}