2

我正在反序列化 XML 文件中的对象列表,并希望通过 ViewModel 绑定到我的视图中这些对象的实际内容。问题是文件操作是这样的async,并且这一直冒泡到 ViewModel,其中属性 getter 不能被标记为这样......

问题

  • 我将文件夹中的所有 XML 文件反序列化为Profile对象并将它们存储在List<Profile>. 此方法(必须)标记async

        public static async Task<List<Profile>> GetAllProfiles()
        {
            DataContractSerializer ser = new DataContractSerializer(typeof(Profile));
            StorageFolder folder = await ApplicationData.Current.RoamingFolder.CreateFolderAsync("Profiles", CreationCollisionOption.OpenIfExists);
    
            List<Profile> profiles = new List<Profile>();
            foreach (var f in await folder.GetFilesAsync())
            {
                var fs = await f.OpenStreamForReadAsync();
                profiles.Add((Profile)ser.ReadObject(fs));
               fs.Dispose();
            }
    
            return profiles;
        }
    

理想解决方案 1

  • 然后,我的 ViewModel 中的绑定属性将理想地调用该静态方法,如下所示

    public async Task<ObservableCollection<string>> Lists
    {
        get 
        {
            return new ObservableCollection<string>(GetAllProfiles().Select(p => p.Name));
        }
    }
    
  • 但是属性不能被标记async

理想方案2

    public ObservableCollection<string> Lists
    {
        get 
        {
            return new ObservableCollection<string>((GetAllProfiles().Result).Select(p => p.Name));
        }
    }
  • await folder.GetFilesAsync()但是这永远不会执行(由于某种原因它在调用中阻塞)

当前解决方案

调用asyncInitialize() 方法,将函数的结果加载到GetProfiles()变量中,然后进行NotifyPropertyChanged("Lists")调用:

    public ViewModel()
    {
        Initialize();
    }

    public async void Initialize()
    {
        _profiles = await Profile.GetAllProfiles();
        NotifyPropertyChanged("Lists");
    }

    private List<Profile> _profiles;
    public ObservableCollection<string> Lists
    {
        get
        {
            if (_profiles != null)
                return new ObservableCollection<string>(_profiles.Select(p => p.Name));
            else
                return null;
        }
    }

问题

有没有更好的办法?有没有我还没有发现的模式/方法?

编辑

问题的根源出现在做非 UI 代码时,你不能依赖 NotifyPropertyChanged 来做一些线程同步的事情。-- Initialize 方法必须等待,并且ctors 不能是异步的,所以本质上这是模式是无用的。

    public MyClass()
    {
        Initialize();
    }

    public async void Initialize()
    {
        _profiles = await Profile.GetAllProfiles();
    }

    private ObservableCollection<Profile> _profiles;
    public ObservableCollection<string> Lists
    {
        get
        {
            return _profiles; // this will always be null
        }
    }
4

1 回答 1

1

属性不能是异步的,所以这个解决方案不会像你提到的那样工作。Task.Result 等待任务完成,但这会阻塞 I/O 操作的异步回调返回的 UI 线程,因此您将死锁您的应用程序,因为从不调用回调。您的解决方案确实是最好的方法。不过可以改进。

  • 您应该将 _profiles 字段设置为 ObservableCollection,这样您就不需要在每次访问列表时都将列表转换为 OC。
  • 由于您正在执行可能需要任意时间的 I/O 操作 - 您应该在它进行时启用某种进度指示器。
  • 在某些情况下,您可能希望 Lists 属性更惰性,并且仅在第一次访问时调用 Init 方法。
于 2012-08-07T15:53:28.280 回答