2013-03-01 5 views
1

Я исследовал множество различных способов динамического изменения соединения источника данных здесь, в StackOverflow. Я использовал и проверял почти все примеры C# и vb.net, которые я мог найти, но как-то все будет не так, как должно.Динамическое изменение типа базы данных, источника и т. Д. В Crystal Reports (для Visual Studio 2012)

Идея нашего проекта заключается в изменении соединений источника данных из старых отчетов - с использованием dll xBase - с crdb_ado.dll с поставщиком VFPOLEDB для подключения к файлам DBF Visual Foxpro (каждый файл, представляющий одну таблицу) ,

Я скачал последнюю Кристал Developer Версия для Visual Studio (2012) здесь: http://scn.sap.com/docs/DOC-35074 Чтобы иметь возможность использовать эти компоненты непосредственно в VS без ссылок на библиотеки DLL в Program Files - бизнес-объект каталог (как показано в другие примеры).

Я попытался проверить содержимое через отладчик VS более старого «обновленного» отчета (см. Код ниже) вместе с недавно созданным отчетом в Crystal Reports (последняя версия, в которой используется правильный путь и настройки для подключения к dbf), поэтому они совпадают.

Однако я столкнулся с этими проблемами:

  1. код способен изменить все параметры и атрибуты, но сохранить его отменяет все изменения (файл НЕ только для чтения)
  2. Изменение table.location throws a com исключение (перевод: не удалось загрузить данные базы данных, ошибка в отчете)
  3. То же самое происходит, когда «прикреплять» объект документа отчета к программе просмотра отчетов о кристалле.
  4. reportDocument.VerifyDatabase() явно не удается
  5. Для насколько я могу судить, все настройки являются, кажется, в обоих файлах

Дев среда Windows 7/64bit/VS 2012 Pro. На этом компьютере установлены Crystal Reports (XI или 2011). Все наши отчеты были созданы с версиями 9 и 11 Crystal Reports.

Ниже приведен один из адаптированных примеров кода, который изменил все атрибуты или параметры (кроме расположения таблицы). Я также использовал примеры, которые использовали объекты 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. Интересно, имеет ли это какое-то отношение к этому:

Когда мы хотели изменить соединение xbase через сам CR, мы не можем указать его из «xbase connection 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) 

Любые идеи или предложения? Спасибо

ответ

2

У меня была такая же проблема, и только через неделю мне удалось ее решить.

ApplyLogOnInfoCrystalDecisions.Shared.Table перезаписывает 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 документации или форумах поддержки.

+0

У меня такая же проблема, но не с использованием ReportAppServer, так как я просто запускаю отчет локально. Я не могу, чтобы жизнь меня определяла, откуда происходит название базы данных - это, конечно, не похоже, что она публично раскрыта. Проверка с помощью отражателя выглядит так, как будто в моей сборке (9.2.3333.0), даже если она запутана, похоже, передает информацию из ConnectionInfo вниз в неуправляемый код. this._obfuscatedtablename_ (connectionInfo.ServerName, connectionInfo.DatabaseName, connectionInfo.UserID, connectionInfo.Password, true); – agrath

2

Ответ Неона помог мне. Если сервер не был таким же, я не смог бы получить метод цикла ApplyLogOnInfo таблицы, который большинство людей говорит о работе. Он просто заменит все исходными значениями из файла отчета, как он заметил.

В моем случае я просто пытался перевернуть между базами данных dev/test/prod, используя строку подключения в web.config. Использование 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); 
     } 
    }