General Description: Single SQL query of multiple SQL databases (through joins) returns all but one item from all but one database. Item not being returned is joined via an item that can, and sometimes is, null.
Specific Description:
I am continuing development of an internal ticketing system for work. I just started with C#, SQL and web development about a month ago, so I am still wrapping my head around all of the interconnections and syntax of Jquery, SQL, C#, and MVC.
Currently I am trying to display an SQL query in a table to show brief information for our tickets to support members. I have everything being displayed except "CircuitDescription" which is a pretty important element in order for support to differentiate between circuits. It is coming through on the table as "undefined" which I have gathered is the JQuery response for an initialized variable. All other values are coming through on the web page.
Running the SQL query through Microsoft SQL Server Management Studio displays the column with the circuit description.
Disclaimer:
While searching for this I have seen posts discouraging multiple database queries, but this is how the program was written before so I want to keep my code as similar to what has been done so far as possible.
So if we could jump past the part where it is implied I am an idiot (as is my only experience asking questions on stackoverflow) that would be lovely.
If you help me solve this, and help me learn why what you suggest works, then you can feel free to imply or directly call me anything you like.
Code:
C# / SQL Query:
-The join statements are combining the lists by a numeric value from DB "Tickets" with a numeric value from the other databases. Those databases are holding 2 columns, the numeric value and a corresponding string description.
-The full outer join is combining the list by a numeric circuitID from the tickets to a numeric circuitID in the circuits database.
-The circuit database holds the circuit description I am struggling with.
-Some circuitID values are null, which I suspect may be why this is not working. The other join statements that I am receiving data in connection to are all not null.
public static async Task<List<Ticket>> GetAllTicketsForCustomerAsync(DBAccess db, int customerID)
{
var cmd = "select TicketID, Tickets.DateCreated, Tickets.DateResolved, Tickets.CustomerCircuitID, CircuitDescription, TicketTypeDesc, BillingStatusDescription, TicketStatusDescription " +
"from Tickets " +
"join TicketTypes on Tickets.TicketTypeID = TicketTypes.TicketTypeID " +
"join TicketStatusTypes on Tickets.TicketStatus = TicketStatusTypes.TicketStatus " +
"join TicketBillingStatusTypes on Tickets.BillingStatus = TicketBillingStatusTypes.BillingStatus " +
"full outer join CustomerCircuits on Tickets.CustomerCircuitID = CustomerCircuits.CustomerCircuitID " +
"where Tickets.CustomerID = " + customerID +
"order by Tickets.TicketID DESC";
var table = await db.ReadTableAsync(cmd);
return (from DataRow row in table.Rows select db.AssignFromRow<Ticket>(row)).ToList();
}
JQuery:
-Ternary operator for circDesc is to list any tickets without a circuitID as "NonSpecific" for their circuit description. Otherwise they should display the circuit description that is currently coming through as "Undefined"
function buildPartialCustomerTicketsTable(tickets) {
var table = "";
var maxSize = (tickets.length < 5) ? tickets.length : 5;
for (var i = 0; i < maxSize; i++) {
var t = tickets[i];
var circDesc = (t.CustomerCircuitID == null) ? "Nonspecific" : t.CircuitDescription;
var rowClass = ((i % 2) == 0) ? "listRowNormal" : "listRowAlternate";
table += "<tr class='" + rowClass + "'>"
+ "<td class='listElement'><a href='#' onclick='viewTicket(" + t.TicketID + ",true)'>update</a></td>"
+ "<td class='listElement'>" + t.TicketStatusDescription + "</td>"
+ "<td class='listElement'>" + formatDate(t.DateCreated) + "</td>"
+ "<td class='listElement'>" + formatDate(t.DateResolved) + "</td>"
+ "<td class='listElement'>" + circDesc + "</td>"
+ "<td class='listElement'>" + t.TicketTypeDescription + "</td>"
+ "<td class='listElement'>" + t.BillingStatusDescription + "</td>"
+ "<td class='listElement'>" + t.TicketID + "</td>"
+ "</tr>";
}
return table;
}
Requested Code:
public T AssignFromRow<T>(DataRow row) where T : new()
{
var rec = new T();
this.AssignFromRow(row, rec);
return rec;
}
public void AssignFromRow(DataRow row, object rec)
{
if (row == null || rec == null)
{
return;
}
// Look at all of the properties in the record
PropertyInfo[] recprops = rec.GetType().GetProperties();
foreach (PropertyInfo pi in recprops)
{
// default the sql column name to the property name
string columnName = pi.Name;
// skip any read only parameters
if (!pi.CanWrite)
{
continue;
}
// Check for a mapping attribute. This attribute can change the name of the table column name from the default.
var customAttrs = pi.GetCustomAttributes(typeof(MappingAttribute), false);
if (customAttrs.Length > 0)
{
var mapping = (MappingAttribute)customAttrs[0];
if (!string.IsNullOrEmpty(mapping.ColumnName))
{
columnName = mapping.ColumnName;
}
}
// If the row does not have this element name then skip it
if (!row.Table.Columns.Contains(columnName))
{
continue;
}
// If the DataRow has a value with the same name, and it is not null, then assign it
object dbval = row[columnName];
if (dbval != null && !(dbval is DBNull))
{
pi.SetValue(rec, dbval, null);
}
}
}
Related
sb.AppendLine("Employee Id,First Name,Last Name,Email,Username,Password,Role,Group Name,Country Code, Supervisor Id, Hire Date, Birth Date");
for (int i = 0; i < dt.Rows.Count; i++)
{
String[] empid = dt.Rows[i]["EmpId"].ToString().Split(new Char[] { '-' });
sb.AppendLine(Convert.ToInt32(empid[0]).ToString("000000") + "," + dt.Rows[i]["FirstName"] + "," + dt.Rows[i]["LastName"].ToString().Replace(",", " ") +
",," + dt.Rows[i]["Email"] + ",reward," + dt.Rows[i]["Role"] + ",CCCC," + ",," + ",," + dt.Rows[i]["EmployeeHireDate"] + "," + dt.Rows[i]["EmployeeBirthDate"]);
}
email field needs to be empty,
username needs to be the email,
country code needs to be empty,
supervisor id needs to be empty,
Replace this segement:
+ ",CCCC," + ",," + ",," +
with this:
+ ",CCCC,,," +
But you'll really do MUCH better with a dedicated csv library, of which there are several good options on NuGet.
Additionally, if you're using StringBuilder because you heard it's faster, remember that faster is relative. StringBuilder is faster than string concatenation for this kind of work, but if you're ultimately going to writing to a network stream, file, or web response you'll do even better using a StreamWriter
My database has a table with an userId, an userName, a number field that saves an ID for each datetime and a datetime field that records every time the user logs-in and logs-out.
The thing is that logs comes from a fingerprint catcher, and the software that controls the logs is not programmed for avoiding duplicate logs. So, an user can put his finger and save a log-in/log-out hour each time.
My mission is to create a program that would split the datetime field's data in two new datetime fields in another database, one for the log-in hours and another for the log-outs. To achieve that I have to use the ID field that stores a 0 value for the log-ins and 1 to 5 for the log-outs, but I'm out of ideas on how to avoid the duplicates. And if you can give me a good hint on how to achieve the split in a smooth way, it would be lovely.
I have to use ms acces 2007, visual studio 2012 and c# language. Thanks in advance for your attention.
PD: I'm from colombia, thats why my english is a little messy and old.
EDIT: here is an example of my table.
|IdUser||||||||RecordTime||||||||||||||||||||||||||||||||| RecordType |
|399---||||||||28/04/2016 06:55:36 a.m.|||||----- 0----------|
|399---||||||||28/04/2016 06:57:32 a.m.|||||------0----------|
|399---||||||||28/04/2016 05:07:15 p.m.|||||------1----------|
|399---||||||||28/04/2016 05:16:33 p.m.|||||------1----------|
|399---||||||||02/05/2016 07:04:02 a.m.|||||------0----------|
|399---||||||||02/05/2016 05:15:53 p.m.|||||------1----------|
Well, I don't know how big this database of yours is and wether it would be possible to do a cron job every week/day to port the data into the new Database, but i'm going to try explain the concept of my solutions. It's up to you to implement it.
I would use the following method:
Read all the logs of one User into an array with the following fields (If there is too much data, select ranges of datetime with every pass)
Sort that array
-> This should give you changing rows in the RecordType (eg. 0, 2, 0, 1, 0, 5..)
Then write that array (Chronologicaly!) into the new database which has a Unique ID key.
Then you remove this data from the old database in order to not port the same data twice.
Run this every day/week with a cronjob.
It's up to you to implement it. But that is the strategy i would use.
Then you should end up with the following database:
ID|||||IdUser||||||||RecordTime||||||||||||||||||||||||||||||||| RecordType |
1|||||399---||||||||28/04/2016 06:55:36 a.m.|||||----- 0----------|
2|||||399---||||||||28/04/2016 06:57:32 a.m.|||||------0----------|
3|||||399---||||||||28/04/2016 05:07:15 p.m.|||||------1----------|
4|||||399---||||||||28/04/2016 05:16:33 p.m.|||||------1----------|
5|||||399---||||||||02/05/2016 07:04:02 a.m.|||||------0----------|
6|||||399---||||||||02/05/2016 05:15:53 p.m.|||||------1----------|
I hope this helps.
First, the idea of taking two different types of data from a table, without the proper identification, is wrong. Different types of data should always be in different tables.
My boss (i do not know how), resulted to be very illustrated in the ways of programing, and he, after clarifying his needs, gave me a way to make this solution to work:
First, you take the table, and use it to fill an array of any kind, in my case, a DataGridView:
try
{
huella.Open();
OleDbCommand comando = new OleDbCommand();
comando.Connection = huella;
string consulta = "select [User].IdUser,[User].IdentificationNumber,[User].name,[Record].RecordTime," +
"[Record].RecordType from [User] inner join [Record] on [User].IdUser = [Record].IdUser " +
"where " + "[Record].IdUser=#01 and [Record].RecordTime between #time1 and #time2 order by [User].IdUser asc,[Record].RecordTime asc";
comando.CommandText = consulta;
comando.Parameters.AddWithValue("#01", IDcm.Text.ToString());
comando.Parameters.AddWithValue("#time1", dateTimePicker1.Value.Date);
comando.Parameters.AddWithValue("#time2", dateTimePicker2.Value.Date);
OleDbDataAdapter datos = new OleDbDataAdapter(comando);
// using( OleDbDataReader lector = comando.ExecuteReader())
tabla = new DataTable();
datos.Fill(tabla);
//MessageBox.Show(""+tabla);
clu.DataSource = tabla;
}
catch (Exception ex)
{
MessageBox.Show("ERROR " + ex);
}
finally
{
huella.Close();
}
Then, you loop throughout the array's data, assigning the first value of the array to a variable. Inside this loop, you start another loop, to assign the second value of the array to another variable, and finally, you compare this two variables, to assign them to their respective places (following your own criteria):
StreamWriter archivo = new StreamWriter("D:\\MAXIMUM PC.csv", true);
{
archivo.WriteLine('"' + "usuario" + '"' + ";" + '"' + "Hora de entrada" + '"' + ";" + '"' + "Hora de salida" + '"' + ";" + '"' + "Tiempo" + '"' + ";" + '"' + "Pago" + '"');
for (i = 0; i < rows; i++)
{
//assign the first variable, in my case, DateTime data types
fechaHora = Convert.ToDateTime(clu.Rows[i].Cells[0].Value.ToString());
for (j = i + 1; j < rows; j++)
{
//assign the second variable
fechaActual = Convert.ToDateTime(clu.Rows[j].Cells[0].Value.ToString());
if (fechaHora.Date == fechaActual.Date)
{
//here i start the compare process
if (fechaHora.TimeOfDay != fechaActual.TimeOfDay)
{
tiempo = fechaActual.Subtract(fechaHora);
// if the dates are the same, but their time is different..
if (tiempo.TotalHours > 7 && fechaHora.TimeOfDay > fechaActual.TimeOfDay)
{
//if the timespan between the times is over 7 hours and the first date is over the second....
entrada = fechaHora;
salida = fechaActual;
pay = Convert.ToDouble(tiempo.TotalHours * hourPay);
archivo.WriteLine(usuario + ";" + entrada + ";" + salida + ";" + tiempo.TotalHours + ";" + pay);
}
//if the timespan between the times is over 7 hours and the second date is over the fist....
else if (tiempo.TotalHours > 7 && fechaHora.TimeOfDay < fechaActual.TimeOfDay)
{
entrada = fechaHora;
salida = fechaActual;
pay = Convert.ToDouble(tiempo.TotalHours * hourPay);
archivo.WriteLine(usuario + ";" + entrada + ";" + salida + ";" + tiempo.TotalHours + ";" + pay);
}
//if the timespan between the times is under 2 hours and the first date is under or equal the second....
else if (tiempo.TotalHours < 2 && fechaHora.TimeOfDay <= fechaActual.TimeOfDay)
{
error = fechaActual;
}
}
}
}
}
}
Thanks for all your help, i hope this will be useful.
I fill combo-box with a data from database Access but my question is how can I put a specific row in database filled as a Default item in combo-box with C#?
gerant remplirlistgerant = new gerant();
foreach (gerant ligne in remplirlistgerant.getinfogerant())
{
cmbgerant.Items.Add(ligne.CIN_GERANT + " - " + ligne.NOM_GERANT + " - " + ligne.PRENOM_GERANT);
}
Hope I know yours,
Example if ligne.CIN_GERANT = "aaa" the current row will be selected.
gerant remplirlistgerant = new gerant();
foreach (gerant ligne in remplirlistgerant.getinfogerant())
{
cmbgerant.Items.Add(ligne.CIN_GERANT + " - " + ligne.NOM_GERANT + " - " + ligne.PRENOM_GERANT);
// Example if ligne.CIN_GERANT = "aaa" then select this row.
if (ligne.CIN_GERANT == "aaa" )
{
cmbgerant.SelectedIndex = cmbgerant.Items.Count - 1;// Item just added
}
}
I read data from a text file which is 27 MB file and contains 10001 rows, I need to handle large data. I perform some kind of processing in each row of data and then write it back to a text file. This is the code I have am using
StreamReader streamReader = System.IO.File.OpenText("D:\\input.txt");
string lineContent = streamReader.ReadLine();
int count = 0;
using (StreamWriter writer = new StreamWriter("D:\\ft1.txt"))
{
do
{
if (lineContent != null)
{
string a = JsonConvert.DeserializeObject(lineContent).ToString();
string b = "[" + a + "]";
List<TweetModel> deserializedUsers = JsonConvert.DeserializeObject<List<TweetModel>>(b);
var CreatedAt = deserializedUsers.Select(user => user.created_at).ToArray();
var Text = deserializedUsers.Where(m => m.text != null).Select(user => new
{
a = Regex.Replace(user.text, #"[^\u0000-\u007F]", string.Empty)
.Replace(#"\/", "/")
.Replace("\\", #"\")
.Replace("\'", "'")
.Replace("\''", "''")
.Replace("\n", " ")
.Replace("\t", " ")
}).ToArray();
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
writer.WriteLine(TextWithTimeStamp);
}
lineContent = streamReader.ReadLine();
}
while (streamReader.Peek() != -1);
streamReader.Close();
This code helps does well up to 54 iterations as I get 54 lines in the output file. After that it gives error "Index was outside the bounds of the array." at line
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
I am not very clear about the issue if the maximum capacity of array has been violated, if so how can I increase it or If I can write the individual line encountered in loop through
writer.WriteLine(TextWithTimeStamp);
And clean the storage or something that can solve this issue. I tried using list insead of array , still issue is the same.Please help.
Change this line
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
to
var TextWithTimeStamp = (Text.Any() ? Text.First().a : string.Empty) +
" (timestamp:" + (CreatedAt.Any() ? CreatedAt.First() : string.Empty) + ")";
As you are creating Text and CreatedAt collection objects, they might be empty (0 total item) based on some scenarios and conditions.
Those cases, Text[0] and CreatedAt[0] will fail. So, before using the first element, check if there are any items in the collection. Linq method Any() is used for that purpose.
Update
If you want to skip the lines that do not contain text, change this lines
var TextWithTimeStamp = Text[0].a + " (timestamp:" + CreatedAt[0] + ")";
writer.WriteLine(TextWithTimeStamp);
to
if (Text.Any())
{
var TextWithTimeStamp = Text.First().a + " (timestamp:" + CreatedAt.First() + ")";
writer.WriteLine(TextWithTimeStamp);
}
Update 2
To include all the stringss from CreatedAt rather than only the first one, you can add all the values in comma separated strings. A general example
var strings = new List<string> { "a", "b", "c" };
var allStrings = string.Join(",", strings); //"a,b,c"
My ultimate goal is to get the parent of one work item at a time recursively, until there are no more parents in the hierarchy. At the moment, there is nothing recursive yet, I am still at the point of optimizing the way I obtain the parent work item. I have thought of a way of doing this involving a query:
public WorkItem GetParentWorkItem(int id)
{
StringBuilder queryString = new StringBuilder("SELECT [System.Id]" +
" FROM WorkItemLinks " +
" WHERE [Source].[System.WorkItemType] = '" + TFS_TIMESHEET_WORK_ITEM_TYPE + "'" +
" AND [Source].[System.TeamProject] = '" + TFS_TIMESHEET_PROJECT_KEY + "'" +
" AND [Source].[System.Id] = " + id
);
Query wiQuery = new Query(GetWorkItemStore, queryString.ToString());
WorkItemLinkInfo[] wiTrees = wiQuery.RunLinkQuery();
WorkItem wi = GetWorkItemStore.GetWorkItem(wiTrees[1].TargetId);
return wi;
}
The problem with this method is that it gets all the linked work items, including predecessor, successor, child and parents. I knew that wiTrees[1] was the parent work item so I hard coded the index.
I found out a way to get the "parent" WorkItemTypeEnd object from the work item store:
WorkItemLinkTypeEnd linkTypEnd = GetWorkItemStore.WorkItemLinkTypes.LinkTypeEnds["Parent"];
Where do I go from here?
This works on TFS 2013:
var parent_link = work_item.WorkItemLinks.Cast<WorkItemLink> ().FirstOrDefault (x => x.LinkTypeEnd.Name == "Parent");
WorkItem parent_work_item = null;
if (parent_link != null)
parent_work_item = work_item_store.GetWorkItem (parent_link.TargetId);
Found a solution, which returns the parent WorkItem if there is a parent, if not returns null.
public WorkItem GetParentWorkItem(int id)
{
StringBuilder queryString = new StringBuilder("SELECT [System.Id]" +
" FROM WorkItemLinks " +
" WHERE [Source].[System.WorkItemType] = '" + TFS_TIMESHEET_WORK_ITEM_TYPE + "'" +
" AND [Source].[System.TeamProject] = '" + TFS_TIMESHEET_PROJECT_KEY + "'" +
" AND [Source].[System.Id] = " + id
);
Query wiQuery = new Query(GetWorkItemStore, queryString.ToString());
WorkItemLinkInfo[] wiTrees = wiQuery.RunLinkQuery();
int parentLinkId = GetWorkItemStore.WorkItemLinkTypes.LinkTypeEnds["Parent"].Id;
foreach (WorkItemLinkInfo linkInfo in wiTrees)
{
// -2 is the LinkTypeId for parent
if (linkInfo.LinkTypeId == parentLinkId)
{
workItem = GetWorkItemStore.GetWorkItem(linkInfo.TargetId);
break;
}
else
{
workItem = null;
}
}
return workItem;
}