Qlik Desktop - Custom Connector - Error On ExtractQuery and other questions - c#

I'm trying to develop a simple custom connector to Qlik, where I Get the data from a source specific, load the data in the Qlik and extract the data according own query. However, I found some problems and question, that I describe bellow:
I pass the field, tableName and data for the connection like in the bellow codes, but when I click on "Load Data", the error "Object reference not defined as an instance of an object" is shown, how if the tables in the "QvAditiConnection.Init()" had not been defined. Bellow, the codes:
public QvAditiConectorConnection(QvxConnection connection)
{
try
{
_logApp = new LogApp();
_util = new ClsUtil();
_nomeTabela = " ";
_tipoConexao = " ";
_connectionString = " ";
_parameters = " ";
if (connection != null && connection.MParameters != null && connection.MParameters.Count > 0)
{
this.MParameters = connection.MParameters;
}
GetParametersFromConnection();
if (string.IsNullOrWhiteSpace(_parameters) == false)
{
_connectionStringParameters = _util.RecuperaParametrosConnectionString(_parameters);
_tipoConexao = _connectionStringParameters[0];
string connectionString = GetConnectionString();
if (TestarConexao(connectionString))
{
_connectionString = connectionString;
StartConnection();
}
else
{
_logApp.CriarLog("ERRO na conexão com o banco de dados.");
}
}
else
{
QvxLog.Log(QvxLogFacility.Audit, QvxLogSeverity.Error, "Init() Erro de conexão. Verifique os dados ou o servidor.");
}
Init();
}
catch (Exception ex)
{
_logApp = new LogApp();
if (string.IsNullOrWhiteSpace(ex.Message) == false)
{
QvxLog.Log(QvxLogFacility.Audit, QvxLogSeverity.Error, "Init() Erro: " + ex.Message);
_logApp.CriarLog("ERRO Constructor: " + ex.Message);
}
else
{
QvxLog.Log(QvxLogFacility.Application, QvxLogSeverity.Notice, "Init() Erro Desconhecido");
_logApp.CriarLog("ERRO não identificado");
}
}
}
public override void Init()
{
try
{
if (string.IsNullOrWhiteSpace(_parameters) == false)
{
QvxLog.SetLogLevels(true, true);
QvxLog.Log(QvxLogFacility.Application, QvxLogSeverity.Notice, "Init()");
List<QvxTable> tabelas = new List<QvxTable>();
DataTable schemaTables = _connectionPostGreSqlStaging.GetSchema("Tables");
foreach (DataRow row in schemaTables.Rows)
{
_nomeTabela = (string)row[2];
_dataTableStagingArea = new DataTable(_nomeTabela);
GetDataReader();
QvxTable dadosTable = new QvxTable();
dadosTable.TableName = _nomeTabela;
dadosTable.Fields = _qvxFields;
dadosTable.GetRows = GetDataRowsConnector;
tabelas.Add(dadosTable);
}
this.MTables = tabelas;
}
}
How can you see below, the connection is correct in this case, because the application gets and shows the tables and fields in the correct form:
Select Dialog
But, after I click in "Insert Script" and "Load Data", The error below is shown, how if the tables in the application has not been defined:
Error on click in load data button
The "ExtractQuery" is exact how bellow, and when I debug the code and use the "Add Watch" in parameter "qvxTables", I see that he's null and not contains Tables:
public IEnumerable<QvxDataRow> GetDataRowsConnector()
{
DataTable dadosTabela = _dataTableStagingArea;
foreach (var item in dadosTabela.Rows)
{
yield return MakeEntry(item as DataRow, FindTable(_nomeTabela, MTables));
}
}
public QvxDataRow MakeEntry(DataRow item, QvxTable table)
{
try
{
var row = new QvxDataRow();
for (int i = 0; i < _dataTableStagingArea.Columns.Count; i++)
{
var field = table.Fields.Where(a => a.FieldName == _dataTableStagingArea.Columns[i].ColumnName)
.Select(b => b).FirstOrDefault();
row[field] = item[field.FieldName].ToString();
}
return row;
}
catch (Exception ex)
{
throw new Exception(ex + "MakeEntry()");
}
}
In this case, when the Method "ExtractQuery" is called, the tables some times came, but the data don't came in the communication, similar to error 1. And in this case 2, when I debug step by step and arrive in the line "
dadosTable.GetRows = GetDataRowsConnector;" the debug don't enter in the"GetDataRowsConnector" or in the "MakeEntry", even when I insert breakpoints in the methods or press "F11" to use "Step Into".
How I show the preview data in select dialog(I use the QvxSdk in this case)? Even using the getPreview, I don't get the data and don't see example in the documentation. If do you have suggestions or examples for me, I appreciate if you share. And How I edit and select the fields to show in this window(How show or not Metadada, Selection Summary etc)? And how I insert a logo in the connector, like the connectors like "Oracle", "PostGreSql" etc?
select dialog with highlighted data area preview
When I click in the "Insert Script", I want to send a blank script to the editor, ao invés de send the script "Load [FIELD] SQL SELECT * FROM ...", and ao mesmo tempo get this script that the Qlik genereta automactilly in my backend, It's possible do this?
Wrong Script Editor Area
Correct Script Editor Area
Remember that I use the QvxSdk for this solution, based on the "Simple Example" provided for Qlik, but I'm open for suggestion for others APIs if is the case for answer my questions. I use too the .Net Framework 4.5 to develop the connector
Thank you in advance for your attention and help.
Thanks!

Related

SQLite showing "Insufficient Parameters supplied to the command" despite having enough parameters

I have the following SQLite table.
CREATE TABLE "Ingredient_Detailed" (
"DETAILED_INGREDIENT_ID" TEXT,
"INGREDIENT_CODE" INTEGER NOT NULL,
"BRAND" TEXT NOT NULL,
"INGREDIENT_SOURCE" TEXT NOT NULL,
"UNIT_PRICE" REAL NOT NULL DEFAULT 0,
"AMOUNT_IN_UNIT" REAL NOT NULL DEFAULT 0,
"MINIMUM_PRICE_PER_UNIT" REAL NOT NULL DEFAULT 0,
"QUALITY" INTEGER NOT NULL DEFAULT 1,
"UNITS_AVAILABLE" INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY("INGREDIENT_CODE") REFERENCES "Ingredient"("CODE"),
PRIMARY KEY("DETAILED_INGREDIENT_ID")
)
I have a C# application where I am trying to insert records into this table with the following method:
public int SaveDetailedIngredient(DetailedIngredient pDetailedIngredient)
{
try
{
using (var conn = new SQLiteConnection(GetConnectionString()))
{
var saveDetailedIngredientCommand = new SQLiteCommand("INSERT INTO INGREDIENT_DETAILED (DETAILED_INGREDIENT_ID, " +
"INGREDIENT_CODE, BRAND, INGREDIENT_SOURCE, UNIT_PRICE, AMOUNT_IN_UNIT, " +
"MINIMUM_PRICE_PER_UNIT, QUALITY, UNITS_AVAILABLE) " +
"VALUES ($pDetailedIngredientId, $pCode, $pBrand, $pSource, $pUnitPrice, $pAmountInUnit, $pPricePerUnit, $pQuality, $pUnitsAvailable)", conn);
pDetailedIngredient.DetailedIngredientCode = pDetailedIngredient.Code + "-" + pDetailedIngredient.Brand + "-" + pDetailedIngredient.IngredientSource;
saveDetailedIngredientCommand.Parameters.AddWithValue("pDetailedIngredientId", pDetailedIngredient.DetailedIngredientCode);
saveDetailedIngredientCommand.Parameters.AddWithValue("pCode", pDetailedIngredient.Code);
saveDetailedIngredientCommand.Parameters.AddWithValue("pBrand", pDetailedIngredient.Brand.Trim().ToUpper());
saveDetailedIngredientCommand.Parameters.AddWithValue("pSource", pDetailedIngredient.IngredientSource.Trim().ToUpper());
saveDetailedIngredientCommand.Parameters.AddWithValue("pUnitPrice,", pDetailedIngredient.UnitPrice);
saveDetailedIngredientCommand.Parameters.AddWithValue("pAmountInUnit", pDetailedIngredient.AmountInUnit);
saveDetailedIngredientCommand.Parameters.AddWithValue("pPricePerUnit", pDetailedIngredient.MinimumUnitPrice);
saveDetailedIngredientCommand.Parameters.AddWithValue("pQuality", pDetailedIngredient.Quality);
saveDetailedIngredientCommand.Parameters.AddWithValue("pUnitsAvailable", pDetailedIngredient.UnitsAvailable);
conn.Open();
return saveDetailedIngredientCommand.ExecuteNonQuery();
}
}
catch (SQLiteException sqlEx)
{
Console.WriteLine(sqlEx.Message);
Console.WriteLine(sqlEx.ErrorCode);
throw sqlEx;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw ex;
}
}
Despite indicating 9 fields and supplying 9 parameters, SQLite throws an exception saying "Unknown Error: Insufficient Parameters Supplied to Command".
I copy and pasted the name of the parameters to make sure there's no typo in them, yet it still throws the error.
I debugged the application during the method executing and the supplied pDetailedIngredient has all the necessary attribute values assigned, and I can see that each of the parameters in the command is being assigned correctly.
I have several other methods for inserting data into other tables, all follow the same structure, so I doubt that this is related to the way my method is written.
Am I missing something here? It doesn't feel like this is the right error.
I honestly wish I had a good answer as to “WHY” this is happening. I can only assume that the “AddWithValue” is doing something I am not aware of. However, after a some back and forth I would ask you to try this and see if it works for you as it did for me.
Is what I changed is by adding the parameters as shown below. This consistently worked with some testing and only failed as expected when the id was duplicated. Please let me know if this helps as I am with you in a sense that on the surface, your code looks good to me.
saveDetailedIngredientCommand.Parameters.Add("pDetailedIngredientId", DbType.String).Value = pDetailedIngredient.DetailedIngredientCode;
saveDetailedIngredientCommand.Parameters.Add("pCode", DbType.Int32).Value = pDetailedIngredient.Code;
saveDetailedIngredientCommand.Parameters.Add("pBrand", DbType.String).Value = pDetailedIngredient.Brand.Trim().ToUpper();
saveDetailedIngredientCommand.Parameters.Add("pSource", DbType.String).Value = pDetailedIngredient.IngredientSource.Trim().ToUpper();
saveDetailedIngredientCommand.Parameters.Add("pUnitPrice", DbType.Decimal).Value = pDetailedIngredient.UnitPrice;
saveDetailedIngredientCommand.Parameters.Add("pAmountInUnit", DbType.Decimal).Value = pDetailedIngredient.AmountInUnit;
saveDetailedIngredientCommand.Parameters.Add("pPricePerUnit", DbType.Decimal).Value = pDetailedIngredient.MinimumUnitPrice;
saveDetailedIngredientCommand.Parameters.Add("pQuality", DbType.Int32).Value = pDetailedIngredient.Quality;
saveDetailedIngredientCommand.Parameters.Add("pUnitsAvailable", DbType.Int32).Value = pDetailedIngredient.UnitsAvailable;
Let me know if this does not work for you and I will remove it.
Another potential cause of "Insufficient Parameters Supplied to Command" error
If you use an approach similar to the following for entering data into a SQLite DB programmatically:
create a class w/needed properties
instantiate class and populate the instance
save instance to DB
the class property name and DB column name must match (excluding case-sensitivity).
Example (I use NuGet Dapper so exact syntax may differ):
public class StartState
{
public string Phase { get; set; }
public string ArgText { get; set; }
public string startTime { get; set; }
}
// in winforms form.cs
private static void SaveStartState()
{
StartState model = new StartState();
{
model.Phase = Terms.Status;
model.ArgText = _tsarg;
model.startTime = starttTime4DB;
DBconnects.SaveState(model);
}
}
// in DBconnects.cs
public static void SaveState(StartState model)
{
using (var dbc = new SQLiteConnection(Connections.SQLiteDB()))
{
dbc.Execute($"insert into {DBtbl.State}(" +
"phase,argText,startTime)" +
" values(" +
"#phase,#argText,#startTime)"
, model);
}
}
// DB Definition
CREATE TABLE 'StartState' (
phase TEXT NOT NULL PRIMARY KEY,
argText TEXT,
startTime TEXT
);

Revit External application running outside of API context is not allowed

I'm trying to start a transaction inside my code but, as you can see in the title, it generate an error. I have reading many thread on External Application but don't correctly know how it work.
This is the code I use to start my transaction :
inside my form :
private void Loading_FA_only()//Lance l'import en boucle suivant les items dans la file d'attente
{
foreach(FileInfo fi in selected_rfas_donnees)
{
Loading_Family.famille = fi;
Loading_Family lf = new Loading_Family();
lf.Execute(Command.uiapplication);
Listing_succes(Loading_Family.succes, fi);
}
selected_rfas.Clear();
selected_rfas_donnees.Clear();
Update_Compteur();
}
and that what I try to execute :
public class Loading_Family : IExternalEventHandler
{
public static FileInfo famille;
public static bool succes;
public void Execute(UIApplication uiapp)
{
UIDocument uidoc = uiapp.ActiveUIDocument;
Application app = uiapp.Application;
Document doc = uidoc.Document;
// Access current selection
Selection sel = uidoc.Selection;
// Retrieve elements from database
FilteredElementCollector col
= new FilteredElementCollector(doc)
.WhereElementIsNotElementType()
.OfCategory(BuiltInCategory.INVALID)
.OfClass(typeof(Wall));
// Filtered element collector is iterable
foreach (Element e in col)
{
Debug.Print(e.Name);
}
// Modify document within a transaction
succes = false;
string nom_famille = famille.Name.Remove(famille.Name.Length - 4, 4);
FilteredElementCollector familles_doc = new FilteredElementCollector(doc).OfClass(typeof(Family));
foreach (Element elmt in familles_doc)
{
if (elmt.Name == nom_famille)
{
var result = TaskDialog.Show("CPF - Importation", "Il semblerait que " + nom_famille + " soit déjà présent dans votre projet.\nVoullez-vous le remplacer ?", TaskDialogCommonButtons.Yes | TaskDialogCommonButtons.No);
if (result == TaskDialogResult.Yes)
{
using (Transaction tr = new Transaction(doc, "Importer la famille"))
{
tr.Start();
doc.LoadFamily(famille.FullName);
tr.Commit();
tr.Dispose();
}
}
}
else
{
using (Transaction tr = new Transaction(doc, "Importer la famille"))
{
tr.Start();
doc.LoadFamily(famille.FullName);
tr.Commit();
tr.Dispose();
}
}
}
familles_doc = new FilteredElementCollector(doc);
foreach (Element elmt in familles_doc)
{
if (elmt.Name == nom_famille) { succes = true; }
else { succes = false; }
}
using (Transaction tx = new Transaction(doc))
{
tx.Start("Transaction Name");
tx.Commit();
}
}
public string GetName()
{
return "my event";
}
}
I'm desperate with this. I absolutely don't know how theirs "ExternalEventHandler" or "ExternalApplication" work.
Thank for help :)
It is very simple. Use external events and make sure the external event is declared inside the constructor of MyForm. Personally I've never used ShowDialog because it blocks the user access to the rest of the UI.
Please work through the Revit API getting started material. That explains all the required fundamentals of the Revit API and its architecture, including how external applications and commands are implemented and used.
Please note that the Revit API cannot be used at all outside of a valid Revit API context, and such a context is provided exclusively by the callbacks issues by Revit.exe while running and loading a Revit add-in.
Therefore, the statement you make in your description is expected and desired: a Revit external application can never run outside of a valid Revit API context and is therefore indeed not allowed.
Please spell check your texts before submitting them, especially the title, since typos such as the one in your description make it harder to find an issue.

Invalid data type in criteria expression for no reason

I have this unbelievably annoying issue with an Access db sitting on our server. It was working fine when yesterday it suddenly starting squatting out these
Invalid data type in criteria expression
So I tested on my machine - working fine
Uploaded that working code to the server - same error
Copied the db down to my machine - working fine
The only difference I can see between the data is that if I log the SQL query values to my error message I can see that a value for Document Date has a different format on my pc and on the server:
server
#documentDate : 2018-10-09 12:00:00 AM
local machine
#documentDate : 09/10/2018 12:00:00
All other values look exactly the same.
So is this an issue relating to locality/DateTime formats? If so, why can't Access just accept a DateTime value and store it appropriately, since that column is of data type Date/Time.
Would really appreciate some help on this it is driving my crazy.
Here is the code used to insert a record:
public int InsertAod(Aod aod)
{
if (CheckAod(aod) > 0)
{
Log.Message("This entry already exists");
return 1;
}
var cmd = new OleDbCommand(Constants.InsertAod);
cmd.Parameters.AddWithValue("#company", aod.ParentCollection.Company);
cmd.Parameters.AddWithValue("#businessUnit", aod.ParentCollection.BusinessUnit);
cmd.Parameters.AddWithValue("#sellerGln", aod.ParentCollection.SellerGln);
cmd.Parameters.AddWithValue("#messageId", aod.ParentCollection.MessageId);
cmd.Parameters.AddWithValue("#documentNo", aod.ParentCollection.DocumentNumber);
cmd.Parameters.AddWithValue("#documentDate", aod.ParentCollection.DocumentDate); // Data type of DocumentDate is DateTime
cmd.Parameters.AddWithValue("#region", aod.ParentCollection.Region);
cmd.Parameters.AddWithValue("#storeGln", aod.ParentCollection.StoreGln);
cmd.Parameters.AddWithValue("#storeCode", aod.ParentCollection.StoreCode);
cmd.Parameters.AddWithValue("#storeDescription", aod.ParentCollection.StoreDescription);
cmd.Parameters.AddWithValue("#lineItem", aod.LineItem);
cmd.Parameters.AddWithValue("#movementType", aod.MovementType);
cmd.Parameters.AddWithValue("#orderNo", aod.OrderNumber);
cmd.Parameters.AddWithValue("#reference", aod.ParentCollection.Reference);
cmd.Parameters.AddWithValue("#barcode", aod.Barcode);
cmd.Parameters.AddWithValue("#articleNo", aod.PnPArticleNumber);
cmd.Parameters.AddWithValue("#vendorCode", aod.VendorProductCode);
cmd.Parameters.AddWithValue("#articleDescription", aod.PnPArticleDescription);
cmd.Parameters.AddWithValue("#qty", aod.PnPQuantity);
try
{
//throw new Exception("how is this possible");
return ExecuteCommand(cmd, Execute.Insert);
}
catch (Exception e)
{
var data = cmd.Parameters.Count.ToString();
foreach (OleDbParameter parameter in cmd.Parameters)
{
data += $" {Environment.NewLine} {parameter.ParameterName} : {parameter.Value} {Environment.NewLine}";
}
Log.Error(e, $"Failed to insert AOD for {aod.OrderNumber} - {aod.LineItem}", data);
return 0;
}
}
Here is the code for CheckAod:
public int CheckAod(Aod aod)
{
var cmd = new OleDbCommand(Constants.CountAod);
cmd.Parameters.AddWithValue("#orderNo", aod.OrderNumber);
cmd.Parameters.AddWithValue("#messageId", aod.ParentCollection.MessageId);
cmd.Parameters.AddWithValue("#lineItem", aod.LineItem);
try
{
return ExecuteCommand(cmd, Execute.Count);
}
catch (Exception e)
{
Log.Error(e, "Failed to call CheckAod");
throw;
}
}
And the ExecuteCommand mentioned above:
private int ExecuteCommand(OleDbCommand cmd, Execute command)
{
var output = 0;
if (!(persistantConnection.State == ConnectionState.Open))
persistantConnection.Open();
cmd.Connection = persistantConnection;
using (cmd)
{
try
{
switch (command)
{
case Execute.Insert:
output = cmd.ExecuteNonQuery();
Log.Message("success");
break;
case Execute.Count:
output = (int)cmd.ExecuteScalar();
break;
}
}
catch (Exception e)
{
var data = cmd.Parameters.Count.ToString();
foreach (OleDbParameter parameter in cmd.Parameters)
{
data += $" {Environment.NewLine} {parameter.ParameterName} : {parameter.Value} {Environment.NewLine}";
}
Log.Error(e, "Failed to execute command", data);
throw;
}
return output;
}
}
This is what Constants.InsertAod looks like:
internal const string InsertAod =
#"INSERT INTO TAOD ([COMPANY], [BUSINESS UNIT], [SELLER GLN], [MESSAGE ID], [DOCUMENT NO],
[DOCUMENT DATE], [REGION], [STORE GLN], [STORE CODE], [STORE DESCRIPTION], [LINE ITEM],
[MOVEMENT TYPE], [ORDER NO], [REFERENCE], [BARCODE], [PNP ARTICLE NO],
[VENDOR PRODUCT CODE], [PNP ARTICLE DESCRIPTION], [PNP QTY])
VALUES (#company, #businessUnit, #sellerGln, #messageId, #documentNo, #documentDate, #region,
#storeGln, #storeCode, #storeDescription, #lineItem, #movementType, #orderNo, #reference,
#barcode, #articleNo, #vendorCode, #articleDescription, #qty)";
And I can confirm that the above values are in the same order as they appear in the db itself.
After a bunch of messing around I realised that the server was failing to convert the value PnPQuantity from string to number (the Access data type).
When I tried to do this explicitly I was getting this error
System.FormatException: Input string was not in a correct format.
Even though the string was in the form "18.000"
I changed my code to include a CultureInfo parameter:
decimal.Parse(aod.PnPQuantity, CultureInfo.InvariantCulture)
And it now works fine.
What I don't understand is why the machine suddenly can't handle converting a 18.000 to a number, when the localisation settings are configured to use . as a decimal seperator:
Also, since this was working fine a couple of days ago this means something must have changed on the server, but what could it possibly be?

C# simple form index was outside the bounds of the array

I'm a student of programming and decided to make a simple program to practice.
It's a simple form, with name, date of birth, address etc, and it's being saved in a text file (I know there are easier ways, but I want to learn all of them and started with this one =) )
I have a button to search, by name, if the person is already saved and, if yes, it's supposed to fill the form with the data.
Here's an example of how it's saved:
38b7aa1f-0afb-4fe5-a8f6-40fe953eb1ca;Cindy;22/07/2005;111.111.111-11;22.222.222-2;33333-333;Testes;2112;05;Testando;Testadora;SP;cindy#gmail.com;(44)44444-4444;(55)55555-5555;True;True;Rose;26/05/1950;666.666.666-66;77.777.777-7
So, the name (Cindy) would be in and index[1] of an array.
The problem is this error: index was outside the bounds of the array
At this line: if (linha[1] == txtboxNome.Text)
I've searched on internet and kinda understood the problem, but still don't know how to fix it.
Can anybody help me, please?
How can I load my form properly?
Here's an print to help you "see" the program. Don't worry abou the layout, a few things get opacity 0 when running =)
http://i.imgur.com/jze16Pz.jpg
Thanks in advance =)
private void pesquisarNovoBtn_Click(object sender, RoutedEventArgs e)
{
var filePath = #"E:\Programação\WPF ConsultorioDentista\WPF ConsultorioDentista\bin\Debug\Pacientes.txt";
string[] resultado = null;
using (var abrirPacientes = System.IO.File.OpenText(filePath))
{
string lerPacientes = abrirPacientes.ReadLine();
while (lerPacientes != null)
{
var linha = lerPacientes.Split(';');
if (linha[1] == txtboxNome.Text)
{
resultado = linha;
break;
}
lerPacientes = abrirPacientes.ReadLine();
}
if (resultado == null)
{
MessageBox.Show("Paciente não encontrado.");
}
else
{
txtboxNome.Text = resultado[1];
txtboxData.Text = resultado[2];
txtboxCPF.Text = resultado[3];
txtboxRG.Text = resultado[4];
txtboxCEP.Text = resultado[5];
txtboxEndereco.Text = resultado[6];
txtboxNumero.Text = resultado[7];
txtboxCompl.Text = resultado[8];
txtboxBairro.Text = resultado[9];
txtboxCidade.Text = resultado[10];
txtboxUF.Text = resultado[11];
txtboxEmail.Text = resultado[12];
txtboxCel.Text = resultado[13];
txtboxTelRes.Text = resultado[14];
//checkBoxClinico.IsChecked = resultado[15];
//checkBoxOrto.IsChecked = resultado[16];
txtboxNomeResp.Text = resultado[17];
txtboxNascResp.Text = resultado[18];
txtboxCPFResp.Text = resultado[19];
txtboxRGResp.Text = resultado[20];
}
abrirPacientes.Close();
}
This is where you need to "Step Through" the application. Set a Breakpoint (F9) on the If STatement :
if (linha[1] == txtboxNome.Text)
{
resultado = linha;
break;
}
And mouse over to look at the values contained in the linha array.
Most likely you have a header in the first row of your file and it's not splitting.

C# Inserting a new Requirement in HP Quality Center - AccessViolationException

Trying to create a prototype application that will post a new Requirement to HPQC 11.
I've managed to get a solid connection but when I attempt to add the blank requirement I get an AccessViolationException.
TDConnectionClass td = HPQC_Connect(); //Open a connection
ReqFactory myReqFactory = (ReqFactory)td.ReqFactory; //Start up the Requirments Factory.
Req myReq = (Req)myReqFactory.AddItem(DBNull.Value); //Create a new blank requirement (AccessViolationException)
myReq.Name = "New Requirement"; //Populate Name
myReq.TypeId = "1"; // Populate Type: 0=Business, 1=Folder, 2=Functional, 3=Group, 4=Testing
myReq.ParentId = 0; // Populate Parent ID
myReq.Post(); // Submit
Any ideas? I'm fairly new to C# and coding in general, so it's probably best to assume I know nothing.
After some significant working through the isse the following code works correctly:
private void HPQC_Req_Create_Click()
{
TDConnection td = null;
try
{
td = new TDConnection();
td.InitConnectionEx("server");
td.Login(HPQCUIDTextbox.Text.ToString(), HPQCPassTextbox.Text.ToString());
Console.WriteLine(HPQCPassTextbox.Text.ToString());
td.Connect("DEFAULT", "Test_Automation_Playground");
bool check = td.LoggedIn;
if (check == true)
{
Console.WriteLine("Connected.");
HPQCStatus.Text = "Connected.";
}
ReqFactory myReqFactory = (ReqFactory)td.ReqFactory;
Req myReq = (Req)myReqFactory.AddItem(-1); //Error Here
myReq.Name = "New Requirement 1";
myReq.TypeId = "1"; // 0=Business, 1=Folder, 2=Functional, 3=group, 4=testing
myReq.ParentId = 0;
myReq.Post();
Console.WriteLine("Requirement Created.");
HPQCStatus.Text = "Requirement Created.";
try
{
td.Logout();
td.Disconnect();
td = null;
}
catch
{ }
}
catch (Exception ex)
{
Console.WriteLine("[Error] " + ex);
try
{
td.Logout();
td.Disconnect();
td = null;
}
catch
{ }
}
This code requires that the Server be patched to QC 11 Patch 9 (Build 11.0.0.7274) in order to work. Previous versions cause errors, most notably the error in the question.
Requirements in ALM are hierarchical, when creating requirement you need to create it under some existing requirement.
What you want to do is get a hold of the root requirement, it's Id should be either 0 or 1, you can check it in ALM UI.
And then get an instance of ReqFactory from a property on that Root requirement.
And then add your requirement to that factory.
Also, make sure you are working on STA and not MTA thread.

Categories