I can get a value from a .net data table that has columns named "Col1" and "Col2" like this:
DataTable dt = new DataTable()
// some more code that fills it
Console.Writeline("{0}, {1}", dt.Rows[0]["Col1"], dt.Rows[0]["Col2"]);
I could also use a variable if my datatable has a lot of columns
string x = // something that will be one of the columns in the table
dt.Rows[i][x] = "Some new value"
Is anything like this possible in NAV with a Record variable?
Well "like" this but not exactly. You can use RecordRef type to get reference to a field. But to interact with the certain field you will still need to adress it by its field number. You can iterate through all fields in the table and check their names to find the one you need. Not performant though.
Related
I originally had my combobox cbSortOrder defined in the form as a fixed Value List. I am now attempting to set this in VBA code, using an array. I have tried with a variant and string array but do not think my issue is connected with that but is something to do with the combobox definition.
When attempting to set these values in a With statement, I get the Object Doesn't Support Method error.
I have attached screenprints of the actual error and properties of my combobox.
Unfortunately, you cannot use collections as row source in Access. A Row Source Type of Value List refers to a comma (or semicolon) separated text (depends on the list separator setting in Windows).
If you don't want to use such a value list, use a Row Source Type of Table/Query and specify a table or query name. You can also insert a SELECT statement directly into this field.
This also means, that you must store the entries in a table for this Row Source Type. You can also use a local table instead of a table in the back-end.
Also, the ComboBox has no List property. Use the RowSource property instead. In the code behind of the form you can omit the Forms("formName") part.
With cbSortOrder
.RowSourceType = "Table/Query"
.RowSource = "SELECT Id, Description FROM tlkpSortOrder ORDER BY Description"
End With
or
With cbSortOrder
.ColumnCount = 2 ' If you have and id and a text
.ColumnWidths = "0" ' Hides the Id column
.RowSourceType = "Value List"
.RowSource = "1,Ascending,2,Descending,3,Undefined"
End With
See also: ComboBox.RowSourceType property (Access)
I have taken some search but ether the examples only use one parameter / columnname or they just add Strings together.
I have a table describing projets. There are unchangable columns like an id, projectnumber and such, and several 'dynamic' columns, which a user / admin can add through an interface in the application.
After that a user should see a List of all 'dynamic' Colums, and can decide to display them through checkboxes.
So what I need now, is a query as this
SELECT id, projectname, <LIST_OF_COLUM_NAMES> FROM project
I would like this to be safe from malicious Queries, like someone very clever naming a column
; DELETE TABLE projets --
and then displaying it.
I found several solutions where the querystring is just concatenated ether on the programm side or inside a stored procedure.
I found several examples for stored procedures which get one colum name and create a query statement from it.
I found this article
How to pass an array into a SQL Server stored procedure
on which I must admit I am not sure if it applies to my problem.
Is there a way to achive this without creating a security risk throug SQL-Injection?
There are several easy way to resolve this without risk of sql injection.
Write SELECT * FROM query and limit number of columns that are seen on application, this way all columns are returned and it is up to application to decide which ones to display.
Instead of passing string of columns to stored procedure, have user pass list of column indexes and based on integer value, you can have dynamic sql that generates SELECT statement only with columns that user wants back.
If you just want to display list of columns that exists in a table to the user you should select list of columns from Information Schema Views, this way you are sure which column exists in database.
In SQL-Server you can assign parameter datatype of sysname which has is how all system objects names are stored as, this could give you extra data validation.
No matter what you decide to do, you should never concatenate strings in application or stored procedures.
I tried to work with the Information Schema Views but I was not able to avoid conatenating Strings alltogether. This is what I came up with:
When the user wants to add a Column he can enter a display name, and select from a range of Datatypes. Then I create an internal unique internal column name (like DATETIME_67).
Then I create my query like this:
String querystring = "ALTER TABLE projects ADD " + internalname + " " + typestring;
Note that the String internalname and typestring are both generated inside my code, with no input from the user (so this should be safe against injection, I guess).
Then I write the internal name and the display name to a lookup table:
String querystring = "INSERT INTO lookup (tablename, displayname) " +
"VALUES (#tablename, #displayname)";
using (SQLCommand command = new SQLCommand(querystring, con)) { //con is a SQLConnection object
command.Parameters.AddWithValue("#tablename", internalname);
command.Parameters.AddWithValue("#displayname", displayname);
}
Here the actual Input from the user is inserted, but it is parameterized, so it should also be safe.
When I retrieve the columns I want to display I do also use the (hopefully) safe internal names:
List<String> selectedColumns; //the list of internal col names
String query = "SELECT id, projectnumber, projectname {0} FROM projects"; //projectnumer and name a mandatory fields
if (selectedColumns.Count > 0)
{
fieldstring = String.Join(",", selectedColumns);
fieldstring = ", " + fieldstring;
}
query = String.Format(query, fieldstring);
Please comment on this. Is is working exactly as I need it. The user can add (and remove) custom field to the datatabe and provide display names for them. He can also define, which of the present custom field are to be displayed, and he can enter data for the created fields. I am just not sure, if this save against malicious injections.
I have a csv file read into a TextFieldParser.
Before I place the data rows into a DataTable, I want to add a couple of extra fields that are not in the csv.
This line writes all the csv data into the table ok -
tempTable.Rows.Add(parser.ReadFields())
If I do something like this -
tempTable.Rows.Add(parser.ReadFields(), stationID, sMaxSpeedDecimal, sqlFormattedDate)
the Row.Add seems to treat all the Parser data as one field and then appends the new columns. Basically the parser data is lost to the database.
How can I add additional columns so the tempTable.Rows.Add includes all the parser data plus new column data in one write?
You must either pass one array containing all the field values or else pass all the field values individually. Because you are passing multiple arguments, it is assumed to be the latter and the array is treated as one field value. You must either break up the array and pass each field individually, e.g.
Dim fields = parser.ReadFields()
tempTable.Rows.Add(fields(0), fields(1), stationID, sMaxSpeedDecimal, sqlFormattedDate)
or else combine the extra field values with the original array to create a new array, e.g.
Dim fields = parser.ReadFields().Concat({stationID, sMaxSpeedDecimal, sqlFormattedDate})
tempTable.Rows.Add(fields)
I want to get two field values from a database table and combine them together and use that as the display text in the label. Also I want to make another field value in the table as the label id.
The AgeCat table stores the different age categories. The table definition is
age_cat_id ---> primary key
range_start
range_end
e.g:
age_cat_id=>1
range_start=>18
range_end=>24
What I want is to have the label text set as '"range_start" to "range_end"'
e.g: '18 to 24'
and the label id to be the age_cat_id.
What is the proper way to format the label text to display as above? I have retrieved all the records of the AgeCat table, and stored them in a string array ($agecats) using find(all). I'm having trouble retrieving values from that array and then format it according to above way and set as the label text. Please suggest if there is a better way to do this.
The purpose of this is to display each record in the AgeCat table following the format of 'range_start to range_end' but having their value set as the corresponding age_cat_id.
E.g: 18 to 24 is displayed using some form element but the value should be 1. It's like having a drop down list with custom strings, but each linked to a unique value. So once an option is selected, the value is passed, not the string.
If the label is not good enough, what form element is suitable for this?
Virtual fields:cakephp documentation.
public $virtualFields = array(
'cat_date' => 'CONCAT(AgeCat.range_start, " to ", AgeCat.range_end)'
);
P.S.: according to Cakephp conventions the primary key should be named id.
Edit: take a look there: form helper
echo $this->Form->select('id', $cat_date)
In the controller something like this:
$cat_date=$this->Model->find('list',array('fields'=>array('id','cat_name')));
with $cat_date an array containing the desired values in the select.
I am copying a record from one table to another in Access 2007. I iterate through each field in the current record and copy that value to the new table. It works fine until I get to my lookup column field that allows multiple values. The name of the lookup column is "Favorite Sports" and the user can select multiple values from a dropdown list.
I believe the values of a multivalued field are stored in an array but I cannot access the values in VBA code! I've tried myRecordset.Fields("myFieldName").Value(index) but it didn't work. I don't understand how Access stores multiple values in one field.
I saw something about ItemsSelected on another forum but I don't know what Object is associated with that method.
Thanks for any help!
I would recommend against using multivalue fields for precisely the reason you're running into, because it's extremely complex to refer to the data stored in this simple-to-use UI element (and it's for UI that it's made available, even though it's created in the table design).
From your mention of "ItemsSelected," you seem to be assuming that you access the data in a multivalue field the same way you would in a multiselect listbox on a form. This is not correct. Instead, you have to work with it via a DAO recordset. The documentation for working with multivalue fiels explains how to do it in code, something like this:
Dim rsMyField As DAO.Recordset
Set rsMyField = Me.Recordset("MyField").Value
rsChild.MoveFirst
Do Until rsChild.EOF
Debug.Print rsChild!Value.Value
rsChild.MoveNext
Loop
rsChild.Close
Set rsChild = Nothing
Now, given that you can usually access the properties of a recordset object through its default collections, you'd expect that Me.Recordset("MyField").Value would be returning a recordset object that is navigable through the default collection of a recordset, which is the fields collection. You'd think you could do this:
Me.Recordset("MyField").Value!Value.Value
This should work because the recordset returned is a one-column recordset with the column name "Value" and you'd be asking for the value of that column.
There are two problems with this:
it doesn't actually work. This means that Me.Recordset("MyField").Value is not reallly a full-fledged recordset object the way, say, CurrentDB.OpenRecordset("MyTable") would be. This is demonstrable by trying to return the Recordcount of this recordset:
Me.Recordset("MyField").Value.Recordcount
That causes an error, so that means that what's being returned is not really a standard recordset object.
even if it did work, you'd have no way to navigate the collection of records -- all you'd ever be able to get would be the data from the first selected value in your multivalued field. This is because there is no way in this shortcut one-line form to navigate to a particular record in any recordset that you're referring to in that fashion. A recordset is not like a listbox where you can access both rows and columns, with .ItemData(0).Column(1), which would return the 2nd column of the first row of the listbox.
So, the only way to do this is via navigating the child DAO recordset, as in the code sample above (modelled on that in the cited MSDN article).
Now, you could easily write a wrapper function to deal with this. Something like this seems to work:
Public Function ReturnMVByIndex(ctl As Control, intIndex As Integer) As Variant
Dim rsValues As DAO.Recordset
Dim lngCount As Long
Dim intRecord As Integer
Set rsValues = ctl.Parent.Recordset(ctl.ControlSource).Value
rsValues.MoveLast
lngCount = rsValues.RecordCount
If intIndex > lngCount - 1 Then
MsgBox "The requested index exceeds the number of selected values."
GoTo exitRoutine
End If
rsValues.MoveFirst
Do Until rsValues.EOF
If intRecord = intIndex Then
ReturnMVByIndex = rsValues(0).Value
Exit Do
End If
intRecord = intRecord + 1
rsValues.MoveNext
Loop
exitRoutine:
rsValues.Close
Set rsValues = Nothing
Exit Function
End Function
Using that model, you could also write code to concatenate the values into a list, or return the count of values (so you could call that first in order to avoid the error message when your index exceeded the number of values).
As cool as all of this is, and as nice as the UI that's presented happens to be (it would be really nice if they'd added selection checkboxes as a type for a multiselect listbox), I'd still recommend against using it precisely because it's so much trouble to work with. This just takes the problem of the standard lookup field (see The Evils of Lookup Fields in Tables) and makes things even worse. Requiring DAO code to get values out of these fields is a pretty severe hurdle to overcome with a UI element that is supposed to make things easier for power users, seems to me.
For a quick and dirty way of getting the values out of a multivalued ('complex data') column, you can use an ADO Connection with the Jet OLEDB:Support Complex Data connection property set to False e.g. the connection string should look something like this:
Provider=Microsoft.ACE.OLEDB.12.0;
Data Source=C:\dbs\TestANSI92.accdb;
Jet OLEDB:Engine Type=6;
Jet OLEDB:Support Complex Data=False
The multivaled type column will now be of type MEMO (adLongVarWChar) with each value separated by a semicolon ; character.
But that's only half the problem. How to get data into a multivalued column?
The Access Team seem to have neglected to enhance the Access Database Engine SQL syntax to accommodate multivalued types. The 'semicolon delimiter' trick doesn't work in reverse e.g.
INSERT INTO TestComplexData (ID, weekday_names_multivalued)
VALUES (5, 'Tue;Thu;Sat');
fails with the error, "Cannot perform this operation", ditto when trying to update via ADO recordset :(