1

我在 StackOverflow 上研究了许多不同的方法来动态更改数据源连接。我使用并验证了我能找到的几乎所有 c# 和 vb.net 示例,但不知何故,事情并没有按应有的方式进行。

我们项目的想法是将数据源连接从旧报告(使用 xBase dll)更改为 crdb_ado.dll,并使用 VFPOLEDB 提供程序连接到 Visual Foxpro DBF 文件(每个文件代表一个表)。

我在这里下载了 Visual Studio (2012) 的最新 Crystal Developer 版本:http: //scn.sap.com/docs/DOC-35074 为了能够直接在 VS 中使用这些组件,而无需在程序文件中引用 dll -业务对象目录(如在其他示例中所见)。

我尝试通过较旧的“更新”报告(参见下面的代码)的 VS 调试器以及 Crystal Reports 中新创建的报告(最新版本,它使用正确的路径和设置连接到 dbf)来验证内容,因此它们匹配向上。

但是,我遇到了这些问题:

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

开发环境为 windows 7/64bit/VS 2012 Pro。Crystal Reports(XI 或 2011)安装在这台机器上。我们所有的报表都是使用 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 文档查看器中读取这些转换后的文件)。这很烦人,它不能在 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)

有什么想法或建议吗?谢谢

4

2 回答 2

4

我遇到了同样的问题,一周后我设法解决了它。

ApplyLogOnInfoof用报告文件中的原始属性CrystalDecisions.Shared.Table覆盖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 文档或支持论坛中几乎不可能找到此信息。

于 2013-06-19T10:58:15.193 回答
3

霓虹的回答对我帮助很大。除非服务器相同,否则我无法让大多数人谈论的表循环 ApplyLogOnInfo 方法起作用。就像他观察到的那样,它只会用报告文件中的原始值替换所有内容。

就我而言,我只是试图使用 web.config 中的连接字符串在 dev/test/prod 数据库之间切换。使用 DatabaseController.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);
        }
    }
于 2013-07-22T21:13:03.390 回答