UWP 使用发布模式丢失序列化数据
UWP losing serialize data with release mode
我正在尝试使用 Visual Studio Community 2015 在 UWP 中创建一个日记应用程序。因此所有日记条目都保存在一个 ObservableCollection 中(属于具有三个字符串的 DiaryEntry 类型)。在 App class 的 OnSuspending 方法中,我序列化(使用 XmlSerializer)并将 ObservableCollection 保存到应用程序的 LocalFolder。在应用程序的 OnLaunched 方法中 class 我反序列化数据并将其再次放入 ObservableCollection。
当我 运行 这个应用程序处于调试模式时,它每次都以 100% 的速度运行,但在发布模式下,一旦我关闭并再次打开该应用程序,我就会丢失 ObservableCollection。其他时候它工作一次或两次,但我仍然丢失数据。我想知道的是如何让我的应用程序在发布模式下运行?
这些是我正在使用的方法:
private async void SaveCollection(string xml)
{
//Serializing our observablecollection and saving it to the local folder
StorageFile sf = await ApplicationData.Current.LocalFolder.CreateFileAsync("Diary.txt", CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteTextAsync(sf, xml);
}
private async Task<string> GetSavedCollection()
{
try
{
Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
Windows.Storage.StorageFile sampleFile = await storageFolder.GetFileAsync("Diary.txt");
string text = await Windows.Storage.FileIO.ReadTextAsync(sampleFile);
return text;
}
catch (FileNotFoundException e)
{
return "";
}
}
public static string ToXml(ObservableCollection<DiaryEntry> d)
{
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<DiaryEntry>));
StringBuilder stringBuilder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings()
{
Indent = true,
OmitXmlDeclaration = true,
};
using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder, settings))
{
serializer.Serialize(xmlWriter, d);
}
return stringBuilder.ToString();
}
// Deserialize from xml
public static ObservableCollection<DiaryEntry> FromXml(string xml)
{
if (xml == "")
{
return new ObservableCollection<DiaryEntry>();
}
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<DiaryEntry>));
ObservableCollection<DiaryEntry> value;
using (StringReader stringReader = new StringReader(xml))
{
object deserialized = serializer.Deserialize(stringReader);
value = (ObservableCollection<DiaryEntry>)deserialized;
}
return value;
}
我是这样调用这些方法的:
日记 = FromXml(等待 GetSavedCollection());
SaveCollection(ToXml(日记));
如果没有对您的代码进行完整重现,很难确定(请参阅 https://whosebug.com/help/mcve),但我怀疑这与对应用程序生命周期以及您将代码添加到 OnLaunched
和 OnSuspending
方法。
要了解生命周期,请参阅 the docs here and this blog article。
现在这些方法。
我们先处理 OnSuspending
,这样比较简单。您的代码应如下所示:
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
SaveCollection(ToXml(Diaries));
deferral.Complete();
}
请注意,如果您在 deferral.Complete();
之后调用 SaveCollection
,它可能不会在应用程序终止之前完成,并且可能是没有
的原因
OnLaunching
方法比较复杂。我怀疑你有这样的代码:
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
Diaries = FromXml(await GetSavedCollection());
}
根据模板中的 TODO 注释,这似乎是合乎逻辑的事情,但不适合您希望在重新启动应用程序时始终从磁盘加载的场景。例如,当应用程序先前被用户关闭时,上面的内容不会加载。我假设您也想加载日记条目。 (因为您没有提供完整的重现步骤,所以我无法确切知道您是如何测试和重现您所看到的行为的。)
相反,如果需要,在 'PrelaunchActivated
检查之前加载日记。
if (Diaries == null)
{
Diaries = FromXml(await GetSavedCollection());
}
if (e.PrelaunchActivated == false)
或
您定义和调用的方式存在问题 SaveCollection
。
您已将 SaveCollection
定义为 async void
。这是不好的。唯一应该 async void
的是事件处理程序。您应该将其定义为 async Task
并等待对其的调用:
private async Task SaveCollection(string xml)
{
System.Diagnostics.Debug.WriteLine(xml);
//Serializing our observablecollection and saving it to the local folder
StorageFile sf = await ApplicationData.Current.LocalFolder.CreateFileAsync("Diary.txt", CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteTextAsync(sf, xml);
}
await SaveCollection(ToXml(Diaries));
如果不等待此调用,它可能无法正确完成。这种行为在 .NetNative 编译下更加明显,我不会感到惊讶。
或者它可能是这两个问题的组合。
我正在尝试使用 Visual Studio Community 2015 在 UWP 中创建一个日记应用程序。因此所有日记条目都保存在一个 ObservableCollection 中(属于具有三个字符串的 DiaryEntry 类型)。在 App class 的 OnSuspending 方法中,我序列化(使用 XmlSerializer)并将 ObservableCollection 保存到应用程序的 LocalFolder。在应用程序的 OnLaunched 方法中 class 我反序列化数据并将其再次放入 ObservableCollection。
当我 运行 这个应用程序处于调试模式时,它每次都以 100% 的速度运行,但在发布模式下,一旦我关闭并再次打开该应用程序,我就会丢失 ObservableCollection。其他时候它工作一次或两次,但我仍然丢失数据。我想知道的是如何让我的应用程序在发布模式下运行?
这些是我正在使用的方法:
private async void SaveCollection(string xml)
{
//Serializing our observablecollection and saving it to the local folder
StorageFile sf = await ApplicationData.Current.LocalFolder.CreateFileAsync("Diary.txt", CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteTextAsync(sf, xml);
}
private async Task<string> GetSavedCollection()
{
try
{
Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
Windows.Storage.StorageFile sampleFile = await storageFolder.GetFileAsync("Diary.txt");
string text = await Windows.Storage.FileIO.ReadTextAsync(sampleFile);
return text;
}
catch (FileNotFoundException e)
{
return "";
}
}
public static string ToXml(ObservableCollection<DiaryEntry> d)
{
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<DiaryEntry>));
StringBuilder stringBuilder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings()
{
Indent = true,
OmitXmlDeclaration = true,
};
using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder, settings))
{
serializer.Serialize(xmlWriter, d);
}
return stringBuilder.ToString();
}
// Deserialize from xml
public static ObservableCollection<DiaryEntry> FromXml(string xml)
{
if (xml == "")
{
return new ObservableCollection<DiaryEntry>();
}
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<DiaryEntry>));
ObservableCollection<DiaryEntry> value;
using (StringReader stringReader = new StringReader(xml))
{
object deserialized = serializer.Deserialize(stringReader);
value = (ObservableCollection<DiaryEntry>)deserialized;
}
return value;
}
我是这样调用这些方法的:
日记 = FromXml(等待 GetSavedCollection());
SaveCollection(ToXml(日记));
如果没有对您的代码进行完整重现,很难确定(请参阅 https://whosebug.com/help/mcve),但我怀疑这与对应用程序生命周期以及您将代码添加到 OnLaunched
和 OnSuspending
方法。
要了解生命周期,请参阅 the docs here and this blog article。
现在这些方法。
我们先处理 OnSuspending
,这样比较简单。您的代码应如下所示:
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
SaveCollection(ToXml(Diaries));
deferral.Complete();
}
请注意,如果您在 deferral.Complete();
之后调用 SaveCollection
,它可能不会在应用程序终止之前完成,并且可能是没有
OnLaunching
方法比较复杂。我怀疑你有这样的代码:
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
Diaries = FromXml(await GetSavedCollection());
}
根据模板中的 TODO 注释,这似乎是合乎逻辑的事情,但不适合您希望在重新启动应用程序时始终从磁盘加载的场景。例如,当应用程序先前被用户关闭时,上面的内容不会加载。我假设您也想加载日记条目。 (因为您没有提供完整的重现步骤,所以我无法确切知道您是如何测试和重现您所看到的行为的。)
相反,如果需要,在 'PrelaunchActivated
检查之前加载日记。
if (Diaries == null)
{
Diaries = FromXml(await GetSavedCollection());
}
if (e.PrelaunchActivated == false)
或
您定义和调用的方式存在问题 SaveCollection
。
您已将 SaveCollection
定义为 async void
。这是不好的。唯一应该 async void
的是事件处理程序。您应该将其定义为 async Task
并等待对其的调用:
private async Task SaveCollection(string xml)
{
System.Diagnostics.Debug.WriteLine(xml);
//Serializing our observablecollection and saving it to the local folder
StorageFile sf = await ApplicationData.Current.LocalFolder.CreateFileAsync("Diary.txt", CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteTextAsync(sf, xml);
}
await SaveCollection(ToXml(Diaries));
如果不等待此调用,它可能无法正确完成。这种行为在 .NetNative 编译下更加明显,我不会感到惊讶。
或者它可能是这两个问题的组合。