为什么此代码仅在第二次 运行 时有效?
Why does this code only work the second time it is run?
在我的地图应用程序中,当我用除坐标(纬度和经度)之外的所有关于位置的数据填充 table 时 - 我不希望用户 know/provide ),这些值稍后会在数据表示的地图首次加载到应用程序时以编程方式确定。
当我从主窗体加载现有地图时,代码是这样的:
private void loadExistingMapToolStripMenuItem_Click(object sender, EventArgs e)
{
bool pushpinsAdded = false;
RemoveCurrentPushpins();
using (var frmLoadExistingMap = new mdlDlgFrm_LoadExistingMap())
{
if (frmLoadExistingMap.ShowDialog() == DialogResult.OK)
{
foreach (Pushpin pin in frmLoadExistingMap.Pins)
{
this.userControl11.myMap.Children.Add(pin);
pushpinsAdded = true;
}
}
}
if (pushpinsAdded)
{
RightsizeZoomLevelForAllPushpins();
}
lblMapName.Text = currentMap;
lblMapName.Visible = true;
}
“加载现有地图”表单 (frmLoadExistingMap) 中的代码确定地址的坐标。如果坐标已经存在,那很好;否则,将进行 REST 调用以获取这些值:
private async void AddPushpinToListOfPushpins(string location, string fullAddress, string mapDetailNotes, double latitude, double longitude, string pushpinColor)
{
iContentCounter = iContentCounter + 1;
string toolTip = string.Empty;
// if already have the record, including the coordinates, no need to make the REST call
if ((latitude != 0.0) && (longitude != 0.0))
{
if (mapDetailNotesExist(location))
{
toolTip = String.Format("{0}{1}{2}{1}{3},{4}{1}{5}", location, Environment.NewLine, fullAddress, latitude, longitude, mapDetailNotes.Trim());
}
else
{
toolTip = String.Format("{0}{1}{2}{1}{3},{4}", location, Environment.NewLine, fullAddress, latitude, longitude);
}
var _mapLocation = new Microsoft.Maps.MapControl.WPF.Location(latitude, longitude);
this.Pins.Add(new Pushpin()
{
Location = _mapLocation,
ToolTip = toolTip,
Content = iContentCounter,
Background = new SolidColorBrush(GetColorForDesc(pushpinColor))
});
}
else
{
// from
var request = new GeocodeRequest();
request.BingMapsKey = "heavens2MurgatroidAndSufferinSuccotash";
request.Query = fullAddress;
var result = await request.Execute();
if (result.StatusCode == 200)
{
var toolkitLocation = (result?.ResourceSets?.FirstOrDefault())
?.Resources?.FirstOrDefault()
as BingMapsRESTToolkit.Location;
var _latitude = toolkitLocation.Point.Coordinates[0];
var _longitude = toolkitLocation.Point.Coordinates[1];
var mapLocation = new Microsoft.Maps.MapControl.WPF.Location(_latitude, _longitude);
this.Pins.Add(new Pushpin()
{
Location = mapLocation,
ToolTip = String.Format("{0}{1}{2}{1}{3},{4}{1}{5}", location, Environment.NewLine, fullAddress, _latitude, _longitude, mapDetailNotes),
Content = iContentCounter,
Background = new SolidColorBrush(GetColorForDesc(pushpinColor))
});
UpdateLocationWithCoordinates(location, _latitude, _longitude);
}
}
}
当直接到达上面代码中的“else”块时(当数据库中尚不存在坐标时),第一次不添加图钉。但是,如果我立即再次 运行 相同的代码,它就可以工作 - 即使没有停止并重新启动应用程序。
我知道问题与异步代码有关,但为什么它第一次(到目前为止)没有工作,但第二次(到目前为止)总是工作?
更重要的是,有没有一种方法可以让我确保它在第一次就可以正常工作?
更新
Reza Aghaei:
private void UpdateLocationWithCoordinates(string location, double latitude, double longitude)
{
String query = "UPDATE CartographerDetail " +
"SET Latitude = @Latitude, " +
"Longitude = @Longitude " +
"WHERE FKMapName = @FKMapName AND LocationName = @LocationName";
try
{
var con = new SqliteConnection(connStr);
con.Open();
SqliteCommand cmd = new SqliteCommand(query, con);
// FKMapName and LocationName are for the WHERE clause
cmd.Parameters.AddWithValue("@FKMapName", mapName);
cmd.Parameters.AddWithValue("@LocationName", location);
// Updated values
cmd.Parameters.AddWithValue("@Latitude", latitude);
cmd.Parameters.AddWithValue("@Longitude", longitude);
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
这是“加载现有地图”表格:
它的“加载所选地图”按钮单击会执行此操作:
private void btnLoadSelectedMap_Click(object sender, EventArgs e)
{
string latitudeAsStr = string.Empty;
string longitudeAsStr = string.Empty;
List<MapDetails> lstMapDetails = new List<MapDetails>();
MapDetails md;
. . .
try
{
Form1.currentMap = mapName;
string qry = "SELECT LocationName, Address1, Address2, City, StateOrSo, PostalCode, " +
"MapDetailNotes, Latitude, Longitude " +
"FROM CartographerDetail " +
"WHERE FKMapName = @FKMapName";
var con = new SqliteConnection(connStr);
con.Open();
SqliteCommand cmd = new SqliteCommand(qry, con);
cmd.Parameters.AddWithValue("@FKMapName", mapName);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
md = new MapDetails();
md.LocationName = reader.GetValue(LOC_NAME).ToString().Trim();
. . .
latitudeAsStr = reader.GetValue(LATITUDE).ToString();
if (string.IsNullOrEmpty(latitudeAsStr))
{
md.Latitude = 0.0;
}
else
{
md.Latitude = Convert.ToDouble(reader.GetValue(LATITUDE).ToString());
}
longitudeAsStr = reader.GetValue(LONGITUDE).ToString();
if (string.IsNullOrEmpty(longitudeAsStr))
{
md.Longitude = 0.0;
}
else
{
md.Longitude = Convert.ToDouble(reader.GetValue(LONGITUDE).ToString());
}
lstMapDetails.Add(md);
}
AddPushpinsToListOfPushpins(lstMapDetails);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
// All pushpins have been loaded
this.Close();
}
它的Form_Closed和关闭按钮代码是:
private void mdlDlgFrm_LoadExistingMap_FormClosed(object sender, FormClosedEventArgs e)
{
this.DialogResult = DialogResult.OK; // Would this be a problem, assuming "OK" is always okay?
}
private void btnClose_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
}
作为奖励,AddPushpinsToListOfPushpins() 代码 - 在循环中调用 AddPushpinToListOfPushpins() - 是:
private void AddPushpinsToListOfPushpins(List<MapDetails> lstMapDetails)
{
string fullAddress;
foreach (MapDetails _md in lstMapDetails)
{
fullAddress = string.Format("{0} {1} {2} {3}", _md.Address, _md.City, _md.StateOrSo, _md.PostalCode).Trim();
AddPushpinToListOfPushpins(_md.LocationName, fullAddress, _md.MapDetailNotes, _md.Latitude, _md.Longitude, _md.PushpinColor);
}
}
添加图钉前需要set/update位置坐标吗?
如果不能调试这个,很难说。但感觉如果你 运行 UpdateLocationWithCoordinates(location, _latitude, _longitude);在添加您的引脚之前调用方法,它将第一次为您工作。
我的想法是,该方法在第一个 运行 的添加引脚部分之后调用 运行s。这就是为什么它 运行s 并在第二个 运行 上成功添加引脚,每次“到目前为止”。
据我所知,代码中的主要问题是在不使用 await
运算符的情况下调用 async
方法。使用async/await时请注意以下几点:
async void
适用于事件处理程序,但不适用于需要在代码中直接调用的方法。
变化:
private async void AddPushpinToListOfPushpins(string location, string fullAddress, string mapDetailNotes, double latitude, double longitude, string pushpinColor)
至:
private async Task AddPushpinToListOfPushpins(string location, string fullAddress, string mapDetailNotes, double latitude, double longitude, string pushpinColor)
当你想调用returns Task
或Task<T>
的方法时,你需要使用await
运算符。
变化:
AddPushpinToListOfPushpins(_md.LocationName, fullAddress, _md.MapDetailNotes, _md.Latitude, _md.Longitude, _md.PushpinColor);
至:
await AddPushpinToListOfPushpins(_md.LocationName, fullAddress, _md.MapDetailNotes, _md.Latitude, _md.Longitude, _md.PushpinColor);
当需要使用await
运算符时,包含该行代码的方法,应该声明为async
,并且return类型方法应从 void
更改为 Task
(或从 T
更改为 Task<T>
)。 (唯一的例外是事件处理程序,将它们更改为 async void
。)
变化:
private void AddPushpinsToListOfPushpins(List<MapDetails> lstMapDetails)
至:
private async Task AddPushpinsToListOfPushpins(List<MapDetails> lstMapDetails)
关于第二点提到的,修改:
AddPushpinsToListOfPushpins(lstMapDetails);
至:
await AddPushpinsToListOfPushpins(lstMapDetails);
关于第三点(也是第一点)所说的,修改:
private void btnLoadSelectedMap_Click(object sender, EventArgs e)
至
private async void btnLoadSelectedMap_Click(object sender, EventArgs e)
其他几点(与问题无关,但很好解决):
当你想关闭对话框时,设置对话框结果即可,所以在btnLoadSelectedMap_Click
中,将this.Close()
替换为this.DialogResult = DialogResult.OK;
,则不会需要 mdlDlgFrm_LoadExistingMap_FormClosed
.
当您打开一个连接时,您有责任关闭它,因此当您不再需要该连接时,任何 con.Open();
应该由 con.Close();
耦合。
在我的地图应用程序中,当我用除坐标(纬度和经度)之外的所有关于位置的数据填充 table 时 - 我不希望用户 know/provide ),这些值稍后会在数据表示的地图首次加载到应用程序时以编程方式确定。
当我从主窗体加载现有地图时,代码是这样的:
private void loadExistingMapToolStripMenuItem_Click(object sender, EventArgs e)
{
bool pushpinsAdded = false;
RemoveCurrentPushpins();
using (var frmLoadExistingMap = new mdlDlgFrm_LoadExistingMap())
{
if (frmLoadExistingMap.ShowDialog() == DialogResult.OK)
{
foreach (Pushpin pin in frmLoadExistingMap.Pins)
{
this.userControl11.myMap.Children.Add(pin);
pushpinsAdded = true;
}
}
}
if (pushpinsAdded)
{
RightsizeZoomLevelForAllPushpins();
}
lblMapName.Text = currentMap;
lblMapName.Visible = true;
}
“加载现有地图”表单 (frmLoadExistingMap) 中的代码确定地址的坐标。如果坐标已经存在,那很好;否则,将进行 REST 调用以获取这些值:
private async void AddPushpinToListOfPushpins(string location, string fullAddress, string mapDetailNotes, double latitude, double longitude, string pushpinColor)
{
iContentCounter = iContentCounter + 1;
string toolTip = string.Empty;
// if already have the record, including the coordinates, no need to make the REST call
if ((latitude != 0.0) && (longitude != 0.0))
{
if (mapDetailNotesExist(location))
{
toolTip = String.Format("{0}{1}{2}{1}{3},{4}{1}{5}", location, Environment.NewLine, fullAddress, latitude, longitude, mapDetailNotes.Trim());
}
else
{
toolTip = String.Format("{0}{1}{2}{1}{3},{4}", location, Environment.NewLine, fullAddress, latitude, longitude);
}
var _mapLocation = new Microsoft.Maps.MapControl.WPF.Location(latitude, longitude);
this.Pins.Add(new Pushpin()
{
Location = _mapLocation,
ToolTip = toolTip,
Content = iContentCounter,
Background = new SolidColorBrush(GetColorForDesc(pushpinColor))
});
}
else
{
// from
var request = new GeocodeRequest();
request.BingMapsKey = "heavens2MurgatroidAndSufferinSuccotash";
request.Query = fullAddress;
var result = await request.Execute();
if (result.StatusCode == 200)
{
var toolkitLocation = (result?.ResourceSets?.FirstOrDefault())
?.Resources?.FirstOrDefault()
as BingMapsRESTToolkit.Location;
var _latitude = toolkitLocation.Point.Coordinates[0];
var _longitude = toolkitLocation.Point.Coordinates[1];
var mapLocation = new Microsoft.Maps.MapControl.WPF.Location(_latitude, _longitude);
this.Pins.Add(new Pushpin()
{
Location = mapLocation,
ToolTip = String.Format("{0}{1}{2}{1}{3},{4}{1}{5}", location, Environment.NewLine, fullAddress, _latitude, _longitude, mapDetailNotes),
Content = iContentCounter,
Background = new SolidColorBrush(GetColorForDesc(pushpinColor))
});
UpdateLocationWithCoordinates(location, _latitude, _longitude);
}
}
}
当直接到达上面代码中的“else”块时(当数据库中尚不存在坐标时),第一次不添加图钉。但是,如果我立即再次 运行 相同的代码,它就可以工作 - 即使没有停止并重新启动应用程序。
我知道问题与异步代码有关,但为什么它第一次(到目前为止)没有工作,但第二次(到目前为止)总是工作?
更重要的是,有没有一种方法可以让我确保它在第一次就可以正常工作?
更新
Reza Aghaei:
private void UpdateLocationWithCoordinates(string location, double latitude, double longitude)
{
String query = "UPDATE CartographerDetail " +
"SET Latitude = @Latitude, " +
"Longitude = @Longitude " +
"WHERE FKMapName = @FKMapName AND LocationName = @LocationName";
try
{
var con = new SqliteConnection(connStr);
con.Open();
SqliteCommand cmd = new SqliteCommand(query, con);
// FKMapName and LocationName are for the WHERE clause
cmd.Parameters.AddWithValue("@FKMapName", mapName);
cmd.Parameters.AddWithValue("@LocationName", location);
// Updated values
cmd.Parameters.AddWithValue("@Latitude", latitude);
cmd.Parameters.AddWithValue("@Longitude", longitude);
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
这是“加载现有地图”表格:
它的“加载所选地图”按钮单击会执行此操作:
private void btnLoadSelectedMap_Click(object sender, EventArgs e)
{
string latitudeAsStr = string.Empty;
string longitudeAsStr = string.Empty;
List<MapDetails> lstMapDetails = new List<MapDetails>();
MapDetails md;
. . .
try
{
Form1.currentMap = mapName;
string qry = "SELECT LocationName, Address1, Address2, City, StateOrSo, PostalCode, " +
"MapDetailNotes, Latitude, Longitude " +
"FROM CartographerDetail " +
"WHERE FKMapName = @FKMapName";
var con = new SqliteConnection(connStr);
con.Open();
SqliteCommand cmd = new SqliteCommand(qry, con);
cmd.Parameters.AddWithValue("@FKMapName", mapName);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
md = new MapDetails();
md.LocationName = reader.GetValue(LOC_NAME).ToString().Trim();
. . .
latitudeAsStr = reader.GetValue(LATITUDE).ToString();
if (string.IsNullOrEmpty(latitudeAsStr))
{
md.Latitude = 0.0;
}
else
{
md.Latitude = Convert.ToDouble(reader.GetValue(LATITUDE).ToString());
}
longitudeAsStr = reader.GetValue(LONGITUDE).ToString();
if (string.IsNullOrEmpty(longitudeAsStr))
{
md.Longitude = 0.0;
}
else
{
md.Longitude = Convert.ToDouble(reader.GetValue(LONGITUDE).ToString());
}
lstMapDetails.Add(md);
}
AddPushpinsToListOfPushpins(lstMapDetails);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
// All pushpins have been loaded
this.Close();
}
它的Form_Closed和关闭按钮代码是:
private void mdlDlgFrm_LoadExistingMap_FormClosed(object sender, FormClosedEventArgs e)
{
this.DialogResult = DialogResult.OK; // Would this be a problem, assuming "OK" is always okay?
}
private void btnClose_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
}
作为奖励,AddPushpinsToListOfPushpins() 代码 - 在循环中调用 AddPushpinToListOfPushpins() - 是:
private void AddPushpinsToListOfPushpins(List<MapDetails> lstMapDetails)
{
string fullAddress;
foreach (MapDetails _md in lstMapDetails)
{
fullAddress = string.Format("{0} {1} {2} {3}", _md.Address, _md.City, _md.StateOrSo, _md.PostalCode).Trim();
AddPushpinToListOfPushpins(_md.LocationName, fullAddress, _md.MapDetailNotes, _md.Latitude, _md.Longitude, _md.PushpinColor);
}
}
添加图钉前需要set/update位置坐标吗? 如果不能调试这个,很难说。但感觉如果你 运行 UpdateLocationWithCoordinates(location, _latitude, _longitude);在添加您的引脚之前调用方法,它将第一次为您工作。 我的想法是,该方法在第一个 运行 的添加引脚部分之后调用 运行s。这就是为什么它 运行s 并在第二个 运行 上成功添加引脚,每次“到目前为止”。
据我所知,代码中的主要问题是在不使用 await
运算符的情况下调用 async
方法。使用async/await时请注意以下几点:
async void
适用于事件处理程序,但不适用于需要在代码中直接调用的方法。变化:
private async void AddPushpinToListOfPushpins(string location, string fullAddress, string mapDetailNotes, double latitude, double longitude, string pushpinColor)
至:
private async Task AddPushpinToListOfPushpins(string location, string fullAddress, string mapDetailNotes, double latitude, double longitude, string pushpinColor)
当你想调用returns
Task
或Task<T>
的方法时,你需要使用await
运算符。变化:
AddPushpinToListOfPushpins(_md.LocationName, fullAddress, _md.MapDetailNotes, _md.Latitude, _md.Longitude, _md.PushpinColor);
至:
await AddPushpinToListOfPushpins(_md.LocationName, fullAddress, _md.MapDetailNotes, _md.Latitude, _md.Longitude, _md.PushpinColor);
当需要使用
await
运算符时,包含该行代码的方法,应该声明为async
,并且return类型方法应从void
更改为Task
(或从T
更改为Task<T>
)。 (唯一的例外是事件处理程序,将它们更改为async void
。)变化:
private void AddPushpinsToListOfPushpins(List<MapDetails> lstMapDetails)
至:
private async Task AddPushpinsToListOfPushpins(List<MapDetails> lstMapDetails)
关于第二点提到的,修改:
AddPushpinsToListOfPushpins(lstMapDetails);
至:
await AddPushpinsToListOfPushpins(lstMapDetails);
关于第三点(也是第一点)所说的,修改:
private void btnLoadSelectedMap_Click(object sender, EventArgs e)
至
private async void btnLoadSelectedMap_Click(object sender, EventArgs e)
其他几点(与问题无关,但很好解决):
当你想关闭对话框时,设置对话框结果即可,所以在
btnLoadSelectedMap_Click
中,将this.Close()
替换为this.DialogResult = DialogResult.OK;
,则不会需要mdlDlgFrm_LoadExistingMap_FormClosed
.当您打开一个连接时,您有责任关闭它,因此当您不再需要该连接时,任何
con.Open();
应该由con.Close();
耦合。