C# - Checking a part of string in Hashtable Key - c#

I have two .csv files -
File1 -
EmployeeId Username BranchId NewJoinee EmpType AccessDetails
5543101 abc.username 40000 0 64 60060||60060
5543102 someone.username 40000 0 64 52474||52474
5543103 ABC.someone 40000 0 NULL NULL
5543104 XZ.someone 40000 0 64 pppA082||
The Access Details column contains details as ``CardNumber|SecretPin|TokenNumber```
Same is the File2
EmployeeId Username BranchId NewJoinee EmpType AccessDetails
5000101 abc.username 40500 0 64 5555||60060
5000102 someone.username 40500 0 64 52474||1234
5000103 ABC.someone 40500 0 NULL NULL
5000104 XZ.someone 40500 0 64 10A082||5644
I have to compare these two files, my conditions are -
If the card number matches but token Id doesn't, do nothing
If the card number does not match but token Id matches, do nothing
If the access details are null, then print this employee details to another file
If both card number and token Id do not match, then print this employee details to another file
I am currently doing this but using this approach I am only able to implement the 1st condition.I am using hashtables to store the content of files. I am making cardnumber as key and rest of the details as value.
This is how I am storing values of File1
if (!_File1.ContainsKey(cardnumber))
_File1.Add(cardnumber, username + "," + branchId + "," + employeeID + "," + cardnumber + "," + tokennumber + "," + empType + "," + newJoinee);
In the simiar fashion,I am storing the contents of file2 in a different hashtable. Then I am doing the following to read file2 data and compare
//readingFile2 data
row = SplitCSV(line);
employeeID = row[employeeIndex].Trim().ToString();
username = row[UsernameIndex].Trim().ToString();
branchID = row[branchIdIndex].Trim().ToString();
newJoinee = row[njIndex].Trim().ToString();
emptype = row[emptypeIdIndex].Trim().ToString();
accessdetails = row[ValueIndex].Trim().ToString();
if (!accessdetails.Equals("NULL"))
{
string[] values = value.Split('|');
cardnumberfile2 = values[0];
tokenumberFile2 = values[2];
}
else
{
cardnumberfile2 = employeeID;
tokennumberfile2 = "";
}
if(_file1.ContainsKey(cardnumberfile2))
{
//reading values from File1
// As cardnumber in file2 is present in the file1, then do nothing
}
I was wondering of perhaps I should store key as
key = cardnumber+tokennumber
and then perhaps do something that could search if a token Id or a cardnumber is present in this key or not.
because in the above approach, I can test if the cardnumber is present or not because it is the key in both Files, but this approach will fail if we have different cardnumbers and same token number(row 1 in the example table)
Secondly, I dont know how to deal with the null value column as there are no unique linking values for these records.
P.S - I don't own the data. I can't change it.

There may be 2 maps. Data structures can look like this:
List<ClassMappedToFileItem> fileData; //data loaded from the file
Dictionary<string, int> cardNumbers; // value is an index pointing to an item in the fileData list
Dictionary<string, int> tokenIds; // value is an index pointing to an item in the fileData list

Related

EPPlus - join string with named cell

I need to create an Excel file in which the user can later adjust a specific information. I'm using C# and EPPlus v6.0.4. For example, if the input is a list of products, I want it to be joined with ' taxes included' string (which can be later changed by the user at Excel):
descriptionA -> descriptionA taxes included
descriptionB -> descriptionB taxes included
descriptionC -> descriptionC taxes included
I'm assuming two worksheets: ws1 (parameter) and ws2 (output list). As shown below, cell B1 is where the user will be able to change the "taxes included" string.
ws1.Cells["A1"].Value = "Additional information:";
excel.Workbook.Names.Add("auxData", ws1.Cells["B1"]);
ws1.Cells["B1"].Value = " taxes included 12%";
At the second worksheet (ws2) I will have the data being populated.
int excelLine = 1;
foreach (var product in productList)
{
string productDescription = product.Description;
ws2.Cells["A" + excelLine].Formula = .....; //need ideas on how to solve this
excelLine ++;
}
At the above .Formula I was trying CONCAT or similar function but its not working (excel file is generated with errors or the formula is not accepted).
The expected output is a cell value ="product full description variable string" & auxData, therefore suitable to B1 text changes by the user (auxData is an Excel name to =ws1!$B$1).
My own solution was:
int excelLine = 1;
foreach (var product in productList)
{
string cellDescription = "=\"" + product.Description + " -- additional information \" & auxData & \"%\"";
ws2.Cells["A" + excelLine].Formula = cellDescription;
excelLine ++;
}

Update a table column by matching the column name to a string

I have a table with many columns and I need to update the column that matches a set of parameters. Is it possible to concatenate a string and then use the string result to update a matching named column of a database using Telerik's OpenAccess? If so, I'm thinking reflection is required here? I would like to be able to do something like shown below:
A simplified example table:
Sku QtyOnHand Whse1Aug2017 Whse2Aug2017 Whse3Aug2017
==================================================================
ABC-123 87 2 4 8
XYZ-789 43 0 5 4
string warehouseId = "1"
string month = "Aug"
string year = "2017"
string sku = "ABC-123"
int qtySold = 3;
string columnName = "Whse" + warehouseId + month + year;
var query = (from s in model.Sales
where s.SKU == sku
select s).FirstOrDefault();
query.columnName = query.columnName + qtySold;
query.SaveChanges();
You can use Reflection for this. Eg
void UpdateProperty(object targetObject, string propertyName, object value)
{
var pi = targetObject.GetType().GetProperty(propertyName);
pi.SetValue(targetObject, value);
}
The Reflection would do it but is a bit expensive.
Telerik Data Access does this out of the box. Check the API for setting and getting values from artificial types/fields. See the section "Setting and Getting Artificial Properties/Fields":
http://docs.telerik.com/data-access/feature-reference/api/context-api/feature-ref-api-context-api-artificial-data-api
here is an example how it works:
category.SetFieldValue( "CategoryName", "MyCategory" );
category.SetFieldValue( "Description", "New Description" );

Transfer value from Windows Registry into DataGridView table

I have an application which has a DataGridViewon the main form. One of the features of the application is reading and writing to the registry from the DataGridView. I'm able to write to the registry and currently trying to read from the registry but having issues getting the final values into the table.
The application has three columns - Name, Age, School. The registry has a subkey named "Students" which has a few student names in it which have different values for Age and School within them. I want to get the subkey names and transfer them to the Name column in the DataGridView, then get the Age and School values and transfer these to the corresponding column in the table.
foreach (var x in Test.GetSubKeyNames())
{
RegistryKey product = Test.OpenSubKey(x);
//string SubKey = Convert.ToString(product);
Console.WriteLine("+\tKey: " + product);
//Testing
foreach (var value in product.GetValueNames())
{
Console.WriteLine("+\tValue: " + value);
string keyValue = Convert.ToString(product.GetValue(value));
//Having trouble here
StudentGridView.CurrentRow.Cells["Age"].Value = keyValue;
}
}
Console.ReadLine();

Retrieving the value from the database and assigning it to different textboxes having a delimeter in it

d.field1 = txt1.Text.Trim() + "$" + txt2.Text.Trim() +
"$" + txt3.Text.Trim() + "$" + txt4.Text.Trim();
This above code suggest that the values of the textbox are been stored in the database combining all the textbox using a delimeter $.
Field1 column in the database is some what have the value as a$b$c$d.
Now I need to retrieve the same value from the database and assign it to particular textbox as.
Txt5.text = a;
Txt6.text=b;
Txt7.text = c;
Txt8.Text = d;
How can I do this ?? I am planning to write a stored procedure along with data access object and value object. First of all I have inserted the values into the database now I need to retrieve.
Any suggestions are appreciated.
you can do something like this:
string t = "a$b$c$d";
string[] temp = t.Split('$');
txt1.Text = temp[0] !=null ? temp[0] : "" ;
just made a Fiddle

Received an invalid column length from the bcp client for colid 6

I want to bulk upload csv file data to sql server 2005 from c# code but I am encountering the below error -
Received an invalid column length from the bcp client for colid 6.
when bulk copy write to database server
I know this post is old but I ran into this same issue and finally figured out a solution to determine which column was causing the problem and report it back as needed. I determined that colid returned in the SqlException is not zero based so you need to subtract 1 from it to get the value. After that it is used as the index of the _sortedColumnMappings ArrayList of the SqlBulkCopy instance not the index of the column mappings that were added to the SqlBulkCopy instance. One thing to note is that SqlBulkCopy will stop on the first error received so this may not be the only issue but at least helps to figure it out.
try
{
bulkCopy.WriteToServer(importTable);
sqlTran.Commit();
}
catch (SqlException ex)
{
if (ex.Message.Contains("Received an invalid column length from the bcp client for colid"))
{
string pattern = #"\d+";
Match match = Regex.Match(ex.Message.ToString(), pattern);
var index = Convert.ToInt32(match.Value) -1;
FieldInfo fi = typeof(SqlBulkCopy).GetField("_sortedColumnMappings", BindingFlags.NonPublic | BindingFlags.Instance);
var sortedColumns = fi.GetValue(bulkCopy);
var items = (Object[])sortedColumns.GetType().GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(sortedColumns);
FieldInfo itemdata = items[index].GetType().GetField("_metadata", BindingFlags.NonPublic | BindingFlags.Instance);
var metadata = itemdata.GetValue(items[index]);
var column = metadata.GetType().GetField("column", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(metadata);
var length = metadata.GetType().GetField("length", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(metadata);
throw new DataFormatException(String.Format("Column: {0} contains data with a length greater than: {1}", column, length));
}
throw;
}
One of the data columns in the excel (Column Id 6) has one or more cell data that exceed the datacolumn datatype length in the database.
Verify the data in excel. Also verify the data in the excel for its format to be in compliance with the database table schema.
To avoid this, try exceeding the data-length of the string datatype in the database table.
Hope this helps.
I faced a similar kind of issue while passing a string to Database table using SQL BulkCopy option. The string i was passing was of 3 characters whereas the destination column length was varchar(20). I tried trimming the string before inserting into DB using Trim() function to check if the issue was due to any space (leading and trailing) in the string. After trimming the string, it worked fine.
You can try text.Trim()
Check the size of the columns in the table you are doing bulk insert/copy. the varchar or other string columns might needs to be extended or the value your are inserting needs to be trim. Column order also should be same as in table.
e.g, Increase size of varchar column 30 to 50 =>
ALTER TABLE [dbo].[TableName]
ALTER COLUMN [ColumnName] Varchar(50)
I have been using SqlBulkCopy to transfer an Access database to SQL. Simply put, I create each table in SQL, load the corresponding Access table into a DataTable, then SqlBulkCopy the DataTable to the corresponding SQL table. Because I used a standardized function to do this, I did not setup any ColumnMappings. When there are no ColumnMappings, SqlBulkCopy seems to simply map column 0 to column 0, column 1 to column 1, etc.
In my case, I had to make sure that the column order in my Access table matched the column order in my new SQL table EXACTLY. Once I did that, it ran as expected.
Great piece of code, thanks for sharing!
I ended up using reflection to get the actual DataMemberName to throw back to a client on an error (I'm using bulk save in a WCF service). Hopefully someone else will find how I did it useful.
static string GetDataMemberName(string colName, object t) {
foreach(PropertyInfo propertyInfo in t.GetType().GetProperties()) {
if (propertyInfo.CanRead) {
if (propertyInfo.Name == colName) {
var attributes = propertyInfo.GetCustomAttributes(typeof(DataMemberAttribute), false).FirstOrDefault() as DataMemberAttribute;
if (attributes != null && !string.IsNullOrEmpty(attributes.Name))
return attributes.Name;
return colName;
}
}
}
return colName;
}
I got this error message with a much more recent ssis version (vs 2015 enterprise, i think it's ssis 2016). I will comment here because this is the first reference that comes up when you google this error message. I think it happens mostly with character columns when the source character size is larger than the target character size. I got this message when I was using an ado.net input to ms sql from a teradata database. Funny because the prior oledb writes to ms sql handled all the character conversion perfectly with no coding overrides. The colid number and the a corresponding Destination Input column # you sometimes get with the colid message are worthless. It's not the column when you count down from the top of the mapping or anything like that. If I were microsoft, I'd be embarrased to give an error message that looks like it's pointing at the problem column when it isn't. I found the problem colid by making an educated guess and then changing the input to the mapping to "Ignore" and then rerun and see if the message went away. In my case and in my environment I fixed it by substr( 'ing the Teradata input to the character size of the ms sql declaration for the output column. Check and make sure your input substr propagates through all you data conversions and mappings. In my case it didn't and I had to delete all my Data Conversion's and Mappings and start over again. Again funny that OLEDB just handled it and ADO.net threw the error and had to have all this intervention to make it work. In general you should use OLEDB when your target is MS Sql.
I just stumbled upon this and using #b_stil's snippet, I was able to figure the culprit column. And on futher investigation, I figured i needed to trim the column just like #Liji Chandran suggested but I was using IExcelDataReader and I couldn't figure out an easy way to validate and trim each of my 160 columns.
Then I stumbled upon this class, (ValidatingDataReader) class from CSVReader.
Interesting thing about this class is that it gives you the source and destination columns data length, the culprit row and even the column value that's causing the error.
All I did was just trim all (nvarchar, varchar, char and nchar) columns.
I just changed my GetValue method to this:
object IDataRecord.GetValue(int i)
{
object columnValue = reader.GetValue(i);
if (i > -1 && i < lookup.Length)
{
DataRow columnDef = lookup[i];
if
(
(
(string)columnDef["DataTypeName"] == "varchar" ||
(string)columnDef["DataTypeName"] == "nvarchar" ||
(string)columnDef["DataTypeName"] == "char" ||
(string)columnDef["DataTypeName"] == "nchar"
) &&
(
columnValue != null &&
columnValue != DBNull.Value
)
)
{
string stringValue = columnValue.ToString().Trim();
columnValue = stringValue;
if (stringValue.Length > (int)columnDef["ColumnSize"])
{
string message =
"Column value \"" + stringValue.Replace("\"", "\\\"") + "\"" +
" with length " + stringValue.Length.ToString("###,##0") +
" from source column " + (this as IDataRecord).GetName(i) +
" in record " + currentRecord.ToString("###,##0") +
" does not fit in destination column " + columnDef["ColumnName"] +
" with length " + ((int)columnDef["ColumnSize"]).ToString("###,##0") +
" in table " + tableName +
" in database " + databaseName +
" on server " + serverName + ".";
if (ColumnException == null)
{
throw new Exception(message);
}
else
{
ColumnExceptionEventArgs args = new ColumnExceptionEventArgs();
args.DataTypeName = (string)columnDef["DataTypeName"];
args.DataType = Type.GetType((string)columnDef["DataType"]);
args.Value = columnValue;
args.SourceIndex = i;
args.SourceColumn = reader.GetName(i);
args.DestIndex = (int)columnDef["ColumnOrdinal"];
args.DestColumn = (string)columnDef["ColumnName"];
args.ColumnSize = (int)columnDef["ColumnSize"];
args.RecordIndex = currentRecord;
args.TableName = tableName;
args.DatabaseName = databaseName;
args.ServerName = serverName;
args.Message = message;
ColumnException(args);
columnValue = args.Value;
}
}
}
}
return columnValue;
}
Hope this helps someone

Categories

Resources