所以我有以下代码:
listOfUserLogs.Add(new Log { TimeStamp = Convert.ToDateTime(myReader [" TimeStamp"]), CheckpointId = Convert.ToInt32(myReader [" CheckpointId"]) });
当我运行程序时,我得到了System.IndexOutOfRangeException {"TimeStamp"}
。我不明白为什么会这样以及如何解决它。
注意:我编辑了帖子,这样您就可以看到整个代码,让我知道我错过了什么。
你可以在这里看到我的节目:
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; }
}
答案 0 :(得分:4)
上面的代码中存在很多问题。基本上,您正在尝试按照表格的确切模型对类进行建模,但这并不总是最佳路径 例如,我会以这种方式设计你的课程
(为了避免在新类Coordinate和字符串坐标之间命名混淆,我将后者重命名为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;}
}
有了这些更改,我会以这种方式修改您的查询
@"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
此时你的循环应该随着
改变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);
}
在此循环结束时,您可以循环listOfUser
并使用TimeStamp
List<Coordinate>
计算距离
foreach(User usr in listUser)
{
...
foreach(Coordinate cc in usr.Coordinates.OrderBy(x => x.TimeStamp)
{
......
}
}
说,我真的建议你花一点时间来学习现代ORM工具(实体框架,Dapper)的使用,它会删除所有从DB加载数据的代码,让你集中精力关于你的任务所需的逻辑。