为什么我在尝试转换时间戳时收到超出范围的消息
本文关键字:范围 消息 时间戳 转换 为什么 | 更新日期: 2023-09-27 18:35:18
所以我有以下代码:
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; }
}
上面的代码中有很多问题。基本上,您正在尝试按照表的确切模型对类进行建模,这并不总是最佳路径。
例如,我会以这种方式设计您的类
(为了避免新类坐标和字符串坐标之间的命名混淆,我将后者重命名为位置)
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
上循环,并使用属于特定用户的List<Coordinate>
计算距离,使用时间戳对他们进行排序
foreach(User usr in listUser)
{
...
foreach(Coordinate cc in usr.Coordinates.OrderBy(x => x.TimeStamp)
{
......
}
}
话虽如此,我真的建议您花一些时间尝试学习现代ORM工具(实体框架,Dapper)的用法,该工具将删除所有代码以从数据库加载数据,并让您专注于任务所需的逻辑。