I have a list of Teachers and I would like to be able to display all data from the list of a specific teacher. I would like to search by name. However, either my list stays empty or I could not get the data from it. I do not know where the problem is.
Here is how i load the data from my sql server database to my list:
public void connectDbRead(List<Teacher> Teachers)
{
var teacherData = TeacherData.GetInstance();
//Get all students in the singleton
var teachers = teacherData.Teachers;
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = "Server=DESKTOP;Database=Test;Trusted_Connection=true";
conn.Open();
SqlCommand command = new SqlCommand("SELECT * FROM TEACHERS", conn);
command.Parameters.Add(new SqlParameter("0", 1));
teachers.Clear();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
teachers.Add(new Teacher
{
Name = (string)reader["NAME"],
FamilyName = (string)reader["FAMILY_NAME"],
Age = (int)reader["AGE"]
});
Console.WriteLine(String.Format("{0} | {1}| {2}",
reader[1], reader[2], reader[3]));
}
}
}
}
Here is how I search the data from the list:
public void searchTeacher(List<Teacher> Teachers)
{
string teacherName = "";
Console.WriteLine("Who do you want to find. Write his name: ");
teacherName = Console.ReadLine();
List<Teacher> teachers = Teachers.FindAll(x => x.Name == teacherName);
Console.WriteLine("List Of ELements!");
foreach (Teacher t in teachers)
{
Console.WriteLine(t.ToString());
}
}
I call it in the main function like that, but in switch cases:
var teacherData = TeacherData.GetInstance();
var teachers = teacherData.Teachers;
teacherData.searchTeacher(teachers);
teacherData.connectDbRead(teachers);
One line solution(don't even need to use LINQ) :
foreach(teachers t in Teacher)
if (t.Name == "TeacherName or any searcj query")
MessageBox.Show(t.name & t.age & .......)
Or if you still want to use LINQ :
forech(Teachers t in Teacher)
var result = Teachers.Where(Teacher => t.Name == "Name Here")
/// use the result as u want )
I changed things around a bit; deleted some things that I didn't understand but I tested it this way and it worked.
class Program
{
static List<Teacher> teachers = new List<Teacher>();
static void Main(string[] args)
{
connectDbRead();
searchTeacher();
Console.ReadLine();
}
public static void connectDbRead()
{
using (SqlConnection conn = new SqlConnection("Server=DESKTOP;Database=Test;Trusted_Connection=true"))
{
conn.Open();
SqlCommand command = new SqlCommand("SELECT * FROM TEACHERS", conn);
teachers.Clear();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
teachers.Add(new Teacher
{
Name = (string)reader["NAME"],
FamilyName = (string)reader["FAMILY_NAME"],
Age = (int)reader["AGE"]
});
}
}
}
}
public static void searchTeacher()
{
string teacherName = "";
Console.WriteLine("Who do you want to find. Write his name: ");
teacherName = Console.ReadLine();
var foundTeachers = teachers.FindAll(x => x.Name == teacherName);
Console.WriteLine("List Of ELements!");
foreach (Teacher t in foundTeachers)
{
Console.WriteLine(String.Format("{0} | {1} | {2}", t.Name, t.FamilyName, t.Age));
}
}
}
public class Teacher
{
public int Age { get; set; }
public string Name { get; set; }
public string FamilyName { get; set; }
}
Related
I'm kind a new on c#. I have a problem with to store the className to list since i need to display all the class that teacher taught. On result, it turns out just the last class teacher taught. I did use join table between teacher and classes.
Model
public class Teacher
{
public int teacherId { get; set; }
public string teacherfName { get; set; }
public string teacherlName { get; set; }
public string className { get; set; }
public int classId { get; set; }
}
Controller
public Teacher FindTeacher(int id)
{
Teacher newTeacher = new Teacher();
MySqlConnection Conn = school.AccessDatabase();
Conn.Open();
MySqlCommand cmd = Conn.CreateCommand();
//SQL QUERY
cmd.CommandText = "Select * from teachers left join classes on teachers.teacherid=classes.teacherid where teachers.teacherid = " + id;
//Gather Result Set of Query into a variable
MySqlDataReader ResultSet = cmd.ExecuteReader();
while (ResultSet.Read())
{
int teacherId = (int)ResultSet["teacherId"];
string teacherfName=ResultSet["teacherfname"].ToString();
string teacherlName=ResultSet["teacherlname"].ToString();
newTeacher.teacherId = teacherId;
newTeacher.teacherFName = teacherFName;
newTeacher.teacherLName = teacherLName;
newTeacher.className = className;
newTeacher.classId = (int)ResultSet["classid"];
}
return newTeacher;
}
Your only returning one teacher if you want all the teachers your code should be:
public IEnumerable<Teacher> FindTeacher(int id)
{
//Lise here
List<Teacher> teachers = new List<Teacher>();
//note the using
using MySqlConnection Conn = school.AccessDatabase();
Conn.Open();
//note the using
using MySqlCommand cmd = Conn.CreateCommand();
//SQL QUERY
cmd.CommandText = "Select * from teachers left join classes on teachers.teacherid=classes.teacherid where teachers.teacherid = " + id;
//Gather Result Set of Query into a variable
MySqlDataReader ResultSet = cmd.ExecuteReader();
while (ResultSet.Read())
{
//new teacher in the loop
Teacher newTeacher = new Teacher();
int teacherId = (int)ResultSet["teacherId"];
string teacherfName=ResultSet["teacherfname"].ToString();
string teacherlName=ResultSet["teacherlname"].ToString();
newTeacher.teacherId = teacherId;
newTeacher.teacherFName = teacherFName;
newTeacher.teacherLName = teacherLName;
newTeacher.className = className;
newTeacher.classId = (int)ResultSet["classid"];
//add to the collection
teachers.Add(newTeacher);
}
//return the collection
return teachers;
}
If also added using statements. These are important to prevent memory leaks
Modify Teacher Class to be able to carry List of TeacherClass that correspond to one teacher:
Define New Class TeacherClass to Carry a TeacherClass Data
public class TeacherClass
{
public string Name { get; set; }
public int Id { get; set; }
}
Modify Teacher Class To have a List Of TeacherClass
public class Teacher
{
public int teacherId { get; set; }
public string teacherfName { get; set; }
public string teacherlName { get; set; }
public List<TeacherClass> classes { get; set; } = new List<TeacherClass>();
}
Then get your function to set this TeacherClass List in a loop:
public Teacher FindTeacher(int id)
{
Teacher newTeacher = new Teacher();
//note the using
using MySqlConnection Conn = school.AccessDatabase();
Conn.Open();
//note the using
using MySqlCommand cmd = Conn.CreateCommand();
//SQL QUERY
cmd.CommandText = "Select * from teachers left join classes on teachers.teacherid=classes.teacherid where teachers.teacherid = " + id;
//Gather Result Set of Query into a variable
MySqlDataReader ResultSet = cmd.ExecuteReader();
// Check if any rows retrieved
if (reader.HasRows)
{
// Iterate Over Rows
while (ResultSet.Read())
{
// Set Teacher Data Just Once
if(newTeacher.teacherId == 0){
newTeacher.teacherId = (int)ResultSet["teacherId"];;
newTeacher.teacherFName = ResultSet["teacherfname"].ToString();
newTeacher.teacherLName = ResultSet["teacherlname"].ToString();
}
// Add new TeacherClass data for this teacher
newTeacher.classes.Add(
new TeacherClass(){
Name = className, // className Check this variable as it is not declared
Id = (int)ResultSet["classid"]
});
}
}
return newTeacher;
}
we have a stored procedure , which results data as below.
testCol1 testCol2 testCol3 testCol4 testCol5
124 1234 4543 4532 5564
123 1235 4546 4537 5565
it has 190,000 records.
I am trying to fetch data in List<TestData> type and then pass it to third party.
below is the code:
public class TestData
{
public int testCol1 { get; set; }
public int testCol2 { get; set; }
public string testCol3 { get; set; }
public double? testCol4 { get; set; }
public int testCol5 { get; set; }
}
var inputs = new List<TestData>();
using (SqlConnection con = new SqlConnection(fitchConnectionString))
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = con;
cmd.CommandText = "dbo.ReadAll_ForTest";
cmd.CommandTimeout = 0;
con.Open();
using (SqlDataReader dr = new SqlDataReader(cmd.ExecuteReader()))
{
while (dr.Read())
{
inputs.Add(new TestData()
{
testCol1 = (int)dr["testCol1"];
testCol2 = (int)dr["testCol2"];
testCol3 =(string)dr["testCol3"];
testCol4 = (double)dr["testCol4"];
testCol5 = (int)dr["testCol5"];
});
}
}
}
//pass to third party
var output = thirdparty.Convert(inputs).ToArray();
its working fine , however is taking lots of time to fetch the data.
is there is a way we can fetch data in faster manner?
One way is to specify types explicitly, so that the framework doesn't have to figure out what you mean. Get the ordinals (indices) in advance, and extract the exact type from the column:
using (var dr = cmd.ExecuteReader())
{
var testCol1Idx = dr.GetOrdinal("testCol1");
var testCol2Idx = dr.GetOrdinal("testCol2");
var testCol3Idx = dr.GetOrdinal("testCol3");
var testCol4Idx = dr.GetOrdinal("testCol4");
var testCol5Idx = dr.GetOrdinal("testCol5");
while (dr.Read())
{
inputs.Add(new TestData()
{
testCol1 = dr.GetInt32(testCol1Idx);
testCol2 = dr.GetInt32(testCol2Idx);
testCol3 = dr.GetString(testCol3Idx);
testCol4 = dr.GetDouble(testCol4Idx);
testCol5 = dr.GetInt32(testCol5Idx);
});
}
}
Other than that, 100K+ are a lot of records. Do you really need all of them? Try to work with a subset of the data, or aggregate data before using them.
I have an string array (query.Tags) for filter a list of values and each time, procces just take the first value of string array during the query execution.
I tried several combinations but nothing changed. Of course, I tested all of these SQL statements in SQL SERVER View.
Can you tell me what I doing wrong?
public IEnumerable<ActorDto> SearchMembersInLists(ListMembersQuery query)
{
IEnumerable<ActorDto> result = null;
var sql = #"select DISTINCT t.ActorId,
a.Id, a.TypeId, a.Name, a.Identifier
FROM [ActorTag] t
INNER JOIN [Actor] a ON t.ActorId = a.Id
where t.Name IN #tags
";
using (var cnx = DbConnectionFactory.GetDefault().GetConnection())
{
cnx.Open();
var query_result = cnx.QueryMultiple(sql, new { query.Tags});
result = query_result.Read<ActorDto>();
}
return result;
}
the original code is this, i just tried to simplify as I could
public IEnumerable<ActorDto> SearchMembersInLists(ListMembersQuery query)
{
IEnumerable<ActorDto> result = null;
var sql = #"
SELECT DISTINCT a.Id, a.TypeId, a.Name, a.Identifier,a.Description, a.Email, a.PictureUrl, a.DisplayName --Actor
FROM [RoleMember] lm
INNER JOIN [Actor] a ON lm.ActorId = a.Id
WHERE {tag_filter} {lists_filter}
ORDER BY a.DisplayName DESC OFFSET #pageIndex ROWS FETCH NEXT #pageSize ROWS ONLY
";
bool has_tags = true;
bool has_lists = true;
if (query.Tags != null && query.Tags.Any())
{
sql = sql.Replace("{tag_filter}", "a.Id IN (SELECT t.ActorId FROM [ActorTag] t WHERE t.Name IN #tags)");
has_tags = true;
}
else
{
sql = sql.Replace("{tag_filter}", "");
has_tags = false;
}
if (query.Lists != null && query.Lists.Any())
{
if (has_tags)
{
sql = sql.Replace("{lists_filter}", "AND lm.RoleId IN #lists");
}
else
{
sql = sql.Replace("{lists_filter}", "lm.RoleId IN #lists");
}
has_lists = true;
}
else
{
sql = sql.Replace("{lists_filter}", "");
has_lists = false;
}
if (!has_tags && !has_lists){
sql = sql.Replace("WHERE", "");
}
var values = new
{
lists = query.Lists,
tags = query.Tags,
pageIndex = query.PageIndex * query.PageSizeOrDefault,
pageSize = query.PageSizeOrDefault
};
using (var cnx = DbConnectionFactory.GetDefault().GetConnection())
{
cnx.Open();
result = cnx.Query<ActorDto>(sql, values);
}
return result;
}
There is nothing wrong in the code shown, assuming you're using the latest version of dapper. A similar example is shown below (that can be run in a console exe etc). Please check your data is what you expect.
Note; the query code can actually be significantly simplified, but I wanted to keep it as similar to your example as possible. The simple alternative is here:
public static IEnumerable<ActorDto> SearchMembersInLists(ListMembersQuery query)
{
using (var cnx = GetConnection())
{
return cnx.Query<ActorDto>(
#"select Id, Name from FooActors where Name IN #Tags", new { query.Tags });
}
}
The full program with the more complex query layout is shown below. The output is:
2: Barney
4: Betty
using Dapper;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
class Program
{
static void Main()
{
// reset and populate
using (var conn = GetConnection())
{
conn.Open();
try { conn.Execute(#"drop table FooActors;"); } catch { }
conn.Execute(#"create table FooActors (
Id int not null primary key identity(1,1),
Name nvarchar(50) not null);");
conn.Execute(#"insert FooActors(Name) values(#Name);", new[]
{
new { Name = "Fred" },
new { Name = "Barney" },
new { Name = "Wilma" },
new { Name = "Betty" },
});
}
// run a demo query
var tags = new[] { "Barney", "Betty" };
var query = new ListMembersQuery { Tags = tags };
var actors = SearchMembersInLists(query);
foreach(var actor in actors)
{
Console.WriteLine("{0}: {1}", actor.Id, actor.Name);
}
}
public static IDbConnection GetConnection()
{
return new SqlConnection(
#"Initial Catalog=master;Data Source=.;Integrated Security=SSPI;");
}
public class ActorDto
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ListMembersQuery
{
public string[] Tags { get; set; }
}
public static IEnumerable<ActorDto> SearchMembersInLists(ListMembersQuery query)
{
IEnumerable<ActorDto> result = null;
const string sql = #"select Id, Name from FooActors where Name IN #Tags";
using (var cnx = GetConnection())
{
cnx.Open();
var query_result = cnx.QueryMultiple(sql, new { query.Tags });
result = query_result.Read<ActorDto>();
}
return result;
}
}
I'm working on this for more than a week and quit stressed currently,
Hope you guys can put me out of my misery.
I welcome If you can suggest overall different approach too. Okay here we go,
I'm on a learning curve and creating a small chat app using SignalR, MVC, JSON, jquery.
I have Chatter class which contain list of ChatMsg class (Msgs). As GetData() method shows below, I'm getting my classes populated from database to a list. As you can see list of Chatter contain some variables including list of ChatMsg. This will get any changes to Table ( new chat messages).
Up to here, this is working fine. [Add part]
[Serializable]
public class Chatter
{
public string Name { get; set; }
public bool Open { get; set; }
public DateTime LastMsg { get; set; }
public IEnumerable<ChatMsg> Msgs { get; set; }
}
[Serializable]
public class ChatMsg
{
public DateTime MsgCreated { get; set; }
public string MsgType { get; set; }
public string MsgBody { get; set; }
}
public List<Chatter> GetData()
{
Dictionary<string, List<ChatMsg>> dcm = new Dictionary<string, List<ChatMsg>>();
List<Chatter> lcm = new List<Chatter>();
using (var connection = new SqlConnection(_connString))
{
connection.Open();
using (var command = new SqlCommand(#"SELECT [Sender], [Receiver], [Body], [MessageCreated] FROM [dbo].[Chat] WHERE [Receiver] = #Name AND [Seen] = #Seen", connection))
{
command.Parameters.Add(new SqlParameter("#Name", "Fan"));//Test val
command.Parameters.Add(new SqlParameter("#Seen", "0"));//Test val
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
List<ChatMsg> cm = new List<ChatMsg>();
cm.Add(item: new ChatMsg { MsgCreated = Convert.ToDateTime(reader["MessageCreated"]), MsgType = "from", MsgBody = (string)reader["Body"] });
if (dcm.ContainsKey((string)reader["Sender"]))
{ dcm[(string)reader["Sender"]].Add(item: new ChatMsg { MsgCreated = Convert.ToDateTime(reader["MessageCreated"]), MsgType = "from", MsgBody = (string)reader["Body"] }); }
else { dcm.Add((string)reader["Sender"], cm); }
}
}
}
foreach (KeyValuePair<string, List<ChatMsg>> pair in dcm)
{
lcm.Add(item: new Chatter { Name = pair.Key, Open = true, LastMsg = DateTime.UtcNow, Msgs = pair.Value });
}
// Updateting [Seen] = 1 here
return lcm;
}
Now if this is a new instance I'm putting this list of Chatters to Session.
Each time when getData() gets new data I'd like to check my Session["ChatHistory"] and if Parent.Name exist I'd like to update Parent and Addrange to Msgs, if not ad new parent from getData() session list.
I'm strugling on following code.
public string receiveMessages()
{
if (Session["ChatHistory"] == null) Session["ChatHistory"] = new List<Chatter>();
List<Chatter> lc = (List<Chatter>)Session["ChatHistory"];
ChatRepository chatRepository = new ChatRepository();
List<Chatter> c = (List<Chatter>)chatRepository.getData();
//havent tested below
foreach (Chatter e in c)
{
var temp_lc = lc.Find(n => n.Name == e.Name);// Can we avoid linq?
if (temp_lc == null)
{
lc.Add(e);
}
else
{
// How to Addrange to Msgs?
}
}
var serializer = new JavaScriptSerializer();
var t = serializer.Serialize(lc);
return t;
}
How to Update list of class in list of class?
How to remove an item from list of class?
Thank you so much!
Consider using variable names like chatters and chatterHistory instead of c and lc. It makes it much easier to read.
Try rewriting your foreach in receiveMessages() like so:
foreach (Chatter e in c)
{
var temp_lc = lc.Where(x => x.Name == e.Name).SingleOrDefault();
if (temp_lc == null)
{
lc.Add(e);
}
else
{
temp_lc.Msgs = temp_lc.Msgs.Concat(e.Msgs).ToList();
}
}
If temp_lc exists, temp_lc.Msgs.Concat(e.Msgs).ToList() will concatenate the Msgs property with e.Msgs. ToList() converts it into a List<ChatMsg>, and then we can assign the whole thing back to temp_lc.Msgs.
That last step is important because Concat() does not mutate (change) the object it is called on - instead, it returns a new object that we then can assign back to temp_lc.Msgs.
So I have the following code :
listOfUserLogs.Add(new Log
{
TimeStamp = Convert.ToDateTime(myReader["TimeStamp"]),
CheckpointId = Convert.ToInt32(myReader["CheckpointId"])
});
And when I run the program I get the System.IndexOutOfRangeException {"TimeStamp"} . I don't understand why is this so and how to fix it .
Note: I edited the post so you can see the entire code and let me know what am I missing.
You can see my program here :
namespace Distance
{
class Program
{
static void Main(string[] args)
{
string connectionString = GetConnectionString();
using (SqlConnection sourceConnection =
new SqlConnection(connectionString))
{
sourceConnection.Open();
SqlDataReader myReader = null;
SqlCommand myCommand = new SqlCommand("SELECT User.Id , [Log].[TimeStamp] ,[Checkpoints].[Id] ,[Checkpoints].[Coordinates] FROM dbo.[Users] INNER JOIN dbo.[Log] ON [Log].[UserId] =[Users].[Id] INNER JOIN dbo.[Checkpoints] ON [Checkpoints].[Id] = [Log].[CheckpointId] ", sourceConnection);
//SqlCommand myCommand = new SqlCommand("SELECT User.Id ,User.Name ,Checkpoint.Id ,Checkpoint.Coordinates , Log.TimeStamp FROM dbo.Users, INNER JOIN dbo.Log ON Log.UserId = User.Id, INNER JOIN dbo.Checkpoints ON Checkpoint.Id = Log.CheckpointId ;", sourceConnection);
myReader = myCommand.ExecuteReader();
var listOfUsers = new List<User>(); //get users from db
//long countStart = System.Convert.ToInt32(myCommand.ExecuteScalar());
var listOfCheckpoints = new List<Checkpoint>(); //get checkpoints from db
var listOfUserLogs = new List<Log>();
while (myReader.Read())
{
listOfUsers.Add(new User
{
Id = Convert.ToInt32(myReader["Id"]),
Name = myReader["Name"].ToString(),
Coordinates = myReader["Coordinates"].ToString()
});
listOfCheckpoints.Add(new Checkpoint
{
Id = Convert.ToInt32(myReader["Id"]),
Coordinates = myReader["Coordinates"].ToString()
});
listOfUserLogs.Add(new Log
{
TimeStamp = Convert.ToDateTime(myReader["TimeStamp"]),
CheckpointId = Convert.ToInt32(myReader["CheckpointId"]),
UserId =Convert.ToInt32(myReader["UserId"])
});
}
StringBuilder sb = new StringBuilder();
foreach (var user in listOfUsers)
{
string address = user.Coordinates;
DateTime currentDate = new DateTime(2014, 8, 1);
var dictionary = new Dictionary<string, double>();
while (currentDate <= DateTime.Now)
{
double dayUserDistance = 0.00;
// var listOfUserLogs = new List<Log>(); //Get logs where day == currentDate from db
var previousCoordinate = address;
foreach (var log in listOfUserLogs)
{
Checkpoint checkpoint = listOfCheckpoints.FirstOrDefault(x => x.Id == log.CheckpointId);
dayUserDistance += DistanceCalculator.GetDistance(previousCoordinate, checkpoint.Coordinates);
previousCoordinate = checkpoint.Coordinates;
}
dayUserDistance += DistanceCalculator.GetDistance(previousCoordinate, address);
dictionary.Add(currentDate.ToString("yyyy-MM-dd"), dayUserDistance);
currentDate = currentDate.AddDays(1);
}
sb.Append(user.Name + ";");
foreach (KeyValuePair<string, double> keyValuePair in dictionary)
{
sb.Append(keyValuePair.Value + ";");
}
sb.AppendLine();
}
Console.WriteLine();
Console.ReadLine();
}
}
private static string GetConnectionString()
// To avoid storing the sourceConnection string in your code,
// you can retrieve it from a configuration file.
{
return "Data Source=BESA-PC;" +
" Integrated Security = true;" +
"Initial Catalog=CykelScore2;";
}
}
}
internal class DistanceCalculator
{
public static double GetDistance(string previousCoordinate, string coordinates)
{
string[] PairSequence = previousCoordinate.Split(',');
float sLatitude = float.Parse(PairSequence[0]);
float sLongitude = float.Parse(PairSequence[1]);
string[] PairSequence2 = coordinates.Split(',');
float eLatitude = float.Parse(PairSequence2[0]);
float eLongitude = float.Parse(PairSequence2[1]);
var sCoord = new GeoCoordinate(sLatitude, sLongitude);
var eCoord = new GeoCoordinate(eLatitude, eLongitude);
return sCoord.GetDistanceTo(eCoord);
}
}
internal class Checkpoint
{
public int Id { get; set; }
public string Coordinates { get; set; }
}
internal class Log
{
public DateTime TimeStamp { get; set; }
public int CheckpointId { get; set; }
public int UserId { get; set; }
}
internal class User
{
public int Id { get; set; }
public string Coordinates { get; set; }
public string Name { get; set; }
}
There are a lot of problems in your code above. Basically you are trying to model your classes following the exact model of your tables and this is not always the best path.
For example, I would design your classes in this way
(To avoid naming confusion between the new class Coordinate and the string coordinates I have renamed the latter to Location)
internal class Coordinate
{
public int coordID { get; set; } // This is your CheckpointID
public string Location { get; set; } // This is the string coordinate loaded
public DateTime TimeStamp { get; set; } // This is the TimeStamp of the coordinate
}
internal class User
{
public int Id { get; set; }
public string Name { get; set; }
public List<Coordinate> Coordinates {get;set;}
}
With these changes in place I would modify your query in this way
#"SELECT User.Id AS UserID,
[Log].[TimeStamp],
[Checkpoints].[Id] as CheckPointID,
[Checkpoints].[Coordinates] as Location
FROM dbo.[Users] INNER JOIN dbo.[Log]
ON [Log].[UserId] = [Users].[Id]
INNER JOIN dbo.[Checkpoints]
ON [Checkpoints].[Id] = [Log].[CheckpointId]
ORDER BY User.ID, [Log].[TimeStamp]" <--- This order by is very important
At this point your loop should change with
User usr = null;
int curUserID = -1;
while (myReader.Read())
{
int id = Convert.ToInt32(myReader["UserId"]);
if(curUserID != id)
{
// Enter this block only if the user changes from the previous one
// They are ordered so you are sure to get them in line
usr = new User()
{
Id = id,
Name = reader["Name"].ToString(),
Coordinates = new List<Coordinate>()
};
curUserID = id;
listOfUsers.Add(usr);
}
// Add all the coordinates that belong to the same user
Coordinate cc = new Coordinate()
{
cc.coordID = Convert.ToInt32(reader["CheckPointID"]);
cc.TimeStamp = Convert.ToDateTime(reader["TimeStamp"]);
cc.Location = reader["Location"].ToString();
};
usr.Coordinates.Add(cc);
}
At the end of this loop you could loop on the listOfUser and calculate the distance using the List<Coordinate> that belongs to a specific user after ordering them using the TimeStamp
foreach(User usr in listUser)
{
...
foreach(Coordinate cc in usr.Coordinates.OrderBy(x => x.TimeStamp)
{
......
}
}
Said that, I really recommend you to spend a bit of your time trying to learn the usage of a modern ORM tool (Entity Framework, Dapper) that would remove all the code to load data from DB and let you concentrate your efforts on the logic required by your task.