序列化时间过长

时间:2017-03-03 12:11:00

标签: c# xml serialization xamarin uwp

所以我正在研究一个我在便携式类库(Xamarin)中创建的UWP应用程序。我需要保存用户在xml文件中输入的信息(例如在TextBoxes中)。

因此我在PCL中创建了一个类,我从那些TextBox中获取信息:

namespace myProject
{
    public class XMLData
    {
        [XmlRoot("MyRootElement")]
        public class MyRootElement
        {
            [XmlAttribute("MyAttribute1")] //name of the xml element
            public string MyAttribute1     //name of a textboxt e.g.
            {
                get;
                set;
            }
            [XmlAttribute("MyAttribute2")]
            public string MyAttribute2
            {
                get;
                set;
            }
            [XmlElement("MyElement1")]
            public string MyElement1
            {
                get;
                set;
            }
    }
}

在每个页面上都有一个"继续"按钮。单击时,数据将被保存:

async void Continue_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new Page2());
            XMLData.MyRootElement mre = new XMLData.MyRootElement
            {
                MyAttribute1 = editor1.ToString(),
                MyAttribute2 = editor2.ToString(),
                MyElement1 = editor3.ToString()
            };
        }

在最后一个按钮上单击应创建并保存的文件:

private void CreateandSave_Clicked(object sender, EventArgs e)
        {
            var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL)
            XMLData xmldat = new XMLData();
            using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(XMLData));
                serializer.Serialize(sw, xmldat);
            }
        }

这里是UWP类中的代码(对于dependencyService,我创建了一个名为FileHelper的类来获取流并创建保存位置+文件)

namespace myProject.UWP
{
    public class FileHelper: IFileHelper //IFileHelper is a simple interface I made with the Stream MakeFileStream(); method in it
    {
        public async Task<IRandomAccessStream> MakeFileStreamAsync()
        {
            StorageFolder sf = KnownFolders.DocumentsLibrary;

            var file = await sf.CreateFileAsync("data.xml", CreationCollisionOption.OpenIfExists);
            using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
            {
                return stream;
            }
        }
        Stream IFileHelper.MakeFileStream()
        {
            var task = MakeFileStreamAsync();
            task.Wait();
            return task.Result.AsStreamForWrite();
        }
    }
}

问题是每当我到达CreateandSave按钮并单击它时,应用程序就会冻结。没有错误,没有,一切看起来都很好。在我打破调试后,我可以看到在我想要的文件夹中创建了一个xml文件,但它是空的(0字节)。代码有什么问题?有人有想法吗?

2 个答案:

答案 0 :(得分:2)

在这段代码中:

using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
    return stream;
}

您正在返回using块创建的实例。它将在返回之前处理,因此,您将返回已处置的对象。

将其更改为return stream。您使用的StreamWriter本身位于using块中,因此在处理期间it will dispose the underlying stream

  

调用StreamWriter时,Dispose()对象会在提供的Stream对象上调用StreamWriter.Dispose

答案 1 :(得分:1)

你的冻结问题是Task.Wait()指令阻塞主UI线程,直到MakeFileStreamAsync()方法完成执行。

您应该使用此方法async并返回Task<Stream>类型,并使用await关键字调用MakeFileStreamAsync方法:

async Task<Stream> IFileHelper.MakeFileStream()
{
    var stream = await MakeFileStreamAsync();
    return stream.AsStreamForWrite();
}

因此,您创建点击的代码应该是:

private void CreateandSave_Clicked(object sender, EventArgs e)
{
    var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL)
    XMLData xmldat = new XMLData();

    // Here you should await your `s` Task:
    await s;

    using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8))
    {
        XmlSerializer serializer = new XmlSerializer(typeof(XMLData));
        serializer.Serialize(sw, xmldat);
    }
}

希望它有所帮助!

修改 关于你的空xml文件问题,我认为这是因为你将数据保存在另一个页面中,但不对它做任何事情。这意味着您在加载Page2时会丢失它们。因此,它们在CreateandSave_Clicked方法中不可用,并且您当前保存了一个空的XMLData对象。

更直观的方法是将数据传递给Page2构造函数,并将此数据类型添加为Page2的公共属性。所以你的Continue_Clicked方法看起来像:

async void Continue_Clicked(object sender, EventArgs e)
{
    // Note you must REVERSE instructions here
    // Create first your object (save the user data in it)
    XMLData.MyRootElement mre = new XMLData.MyRootElement
    {
        MyAttribute1 = editor1.ToString(),
        MyAttribute2 = editor2.ToString(),
        MyElement1 = editor3.ToString()
    };
    // Pass it to Page2 through the constructor
    await Navigation.PushAsync(new Page2(mre));
}

所以Page2类/构造函数变为:

public class Page2 : SomeParentClass
{
   ...
   // add your XMLData property
   public XMLData.MyRootElement mre { get; set; }
   ...
   // the constructor
   public Page2(XMLData.MyRootElement data){
       // Save the user data in xmldat property. So this data could be reused later.
       this.mre = data;
   }
}

然后,如果它的Page2负责创建和保存XML文件本身,则可以重用通过构造函数传递的对象。

private void CreateandSave_Clicked(object sender, EventArgs e)
{
    var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL)
    // You want to remove that here as you created a public property of type XMLData.MyRootElement (called mre) holding user data instead 
    //XMLData xmldat = new XMLData();

    // Here you should await your `s` Task:
    await s;

    using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8))
    {
        // Change of serialized type here
        XmlSerializer serializer = new XmlSerializer(typeof(XMLData.MyRootElement));
        // Here, just seralize the property saved through constructor
        serializer.Serialize(sw, mre);
    }
}

请注意,如果它是Page3或负责保存XML文件的Page,请继续将已保存的用户数据从一个页面传递到另一个页面并在调用时保存一次CreateandSave_Clicked方法。

另一点是,我不确定将MyRootElement类嵌套到XMLData中是否有用。您可以删除XMLData嵌套类,并将MyRootElement类保留为&#34; main&#34;之一。

您也可以使用静态字段来实现相同的功能,因此不使用构造函数。但从我的观点来看,它不那么直观,而且不太干净。

享受,