在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),因此它们匹配。

然而,我遇到了这些问题:

  1. 代码可以更改所有参数和属性,但是保存会丢弃所有更改(文件不是只读的)
  2. 改变表。location抛出com异常(翻译为:无法加载数据库数据,报告错误)
  3. 当将报告文档对象"附加"到水晶报表查看器时也会发生同样的情况。
  4. reportDocument.VerifyDatabase()明显失败
  5. 就我所见,所有设置在两个文件中都是一样的

开发环境是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)

有什么想法或建议吗?由于

在Crystal Reports (Visual Studio 2012)中动态更改数据库类型、源等

我也遇到过同样的问题,只过了一个星期我就解决了。

CrystalDecisions.Shared.TableApplyLogOnInfo用报告文件中的原始属性覆盖ConnectionInfo,只更新用户名和密码。

你必须用PropertyBag代替DbConnectionAttributesCrystalDecisions.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);
        }
    }