MVVM AsyncCommand NullReference

时间:2015-12-26 18:48:39

标签: c# .net wpf asynchronous mvvm

前段时间我决定学习MVVM和async / await。 使用实体框架代码优先,我的应用程序可以将新用户写入数据库。 在MSDN async databinding article我得到了测试异步绑定类NotifyTaskCompletion.cs(重命名为TaskPropertyWatcher.cs),用于从数据库异步加载用户。这班工作。 之后阅读second article。 我从文章中复制粘贴完整的异步类,将AsyncCommand绑定到按钮。

问题:单击绑定按钮时出现NullReferenceException。 这不是编译器错误。 也许有人可以帮助这个“神奇”?

AsyncCommandBase类错误调试信息:

enter image description here

AsyncCommand类错误调试信息: enter image description here

Example solution from MSDN(first item at page)工作正常......

我的DAL AddUser方法:

   public static async Task AddUser(User usr)
    {
        using (var cntx = new ServiceDBContext())
        {
            cntx.Users.Add(usr);
            await cntx.SaveChangesAsync();
        }
    }

实体模型:

 [Table("Users")]
public partial class User
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ClientId { get; set; }
    [StringLength(60)]
    public string ClientType { get; set; }
    [StringLength(160)]
    public string ClientName { get; set; }
    [StringLength(60)]
    public string Mobile { get; set; }
    [StringLength(50)]
    public string EMail { get; set; }

  }

我的ViewModel部分:

   public CustomerAddViewModel()
    {
       AddClient = AsyncCommand.Create(() => DAL.DbService.AddUser(Client));
    }
    private User _user = new User();
    public User Client
    {
        get
        {
            return _user;
        }
        set {
            _user = value;
            RaisePropertyChanged();
        }

    }

    private void RaisePropertyChanged([CallerMemberName]string propertyName = "") 
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;

查看构造函数:

    public CustomerAddView()
    {

        DataContext = new CustomerAddViewModel();
        InitializeComponent();
    }

我的视图数据绑定部分:

<Button Command="{Binding AddClient}" x:Name="button" Content="Add user" HorizontalAlignment="Left" Margin="5,185,0,0" VerticalAlignment="Top" Width="365" Height="26"/>
<TextBox Text="{Binding Client.ClientName}" HorizontalAlignment="Left" Margin="5,34,0,0" VerticalAlignment="Top" Width="365" ></TextBox>
<TextBox Text="{Binding Client.ClientType}" HorizontalAlignment="Left" Margin="5,34,0,0" VerticalAlignment="Top" Width="365" ></TextBox>
<TextBox Text="{Binding Client.EMail}" HorizontalAlignment="Left" Margin="5,34,0,0" VerticalAlignment="Top" Width="365" ></TextBox>
<TextBox Text="{Binding Client.Phone}" HorizontalAlignment="Left" Margin="5,34,0,0" VerticalAlignment="Top" Width="365" ></TextBox>

MSDN代码:

public interface IAsyncCommand : ICommand
{
    Task ExecuteAsync(object parameter);
}
public abstract class AsyncCommandBase : IAsyncCommand
{
    public abstract bool CanExecute(object parameter);

    public abstract Task ExecuteAsync(object parameter);

    public async void Execute(object parameter)
    {
        await ExecuteAsync(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    protected void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }
}
public class AsyncCommand<TResult> : AsyncCommandBase, INotifyPropertyChanged
{
    private readonly Func<CancellationToken, Task<TResult>> _command;
    private readonly CancelAsyncCommand _cancelCommand;
    private TaskPropertyWatcher <TResult> _execution;

    public AsyncCommand(Func<CancellationToken, Task<TResult>> command)
    {
        _command = command;
        _cancelCommand = new CancelAsyncCommand();
    }

    public override bool CanExecute(object parameter)
    {
        return Execution == null || Execution.IsCompleted;
    }

    public override async Task ExecuteAsync(object parameter)
    {
        _cancelCommand.NotifyCommandStarting();
        Execution = new TaskPropertyWatcher<TResult>(_command(_cancelCommand.Token));
        RaiseCanExecuteChanged();
        await Execution.TaskCompletion;
        _cancelCommand.NotifyCommandFinished();
        RaiseCanExecuteChanged();
    }

    public ICommand CancelCommand
    {
        get { return _cancelCommand; }
    }

    public TaskPropertyWatcher<TResult> Execution
    {
        get { return _execution; }
        private set
        {
            _execution = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    private sealed class CancelAsyncCommand : ICommand
    {
        private CancellationTokenSource _cts = new CancellationTokenSource();
        private bool _commandExecuting;

        public CancellationToken Token { get { return _cts.Token; } }

        public void NotifyCommandStarting()
        {
            _commandExecuting = true;
            if (!_cts.IsCancellationRequested)
                return;
            _cts = new CancellationTokenSource();
            RaiseCanExecuteChanged();
        }

        public void NotifyCommandFinished()
        {
            _commandExecuting = false;
            RaiseCanExecuteChanged();
        }

        bool ICommand.CanExecute(object parameter)
        {
            return _commandExecuting && !_cts.IsCancellationRequested;
        }

        void ICommand.Execute(object parameter)
        {
            _cts.Cancel();
            RaiseCanExecuteChanged();
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        private void RaiseCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }
    }
}

public static class AsyncCommand
{
    public static AsyncCommand<object> Create(Func<Task> command)
    {
        return new AsyncCommand<object>(async _ => { await command(); return null; });
    }

    public static AsyncCommand<TResult> Create<TResult>(Func<Task<TResult>> command)
    {
        return new AsyncCommand<TResult>(_ => command());
    }

    public static AsyncCommand<object> Create(Func<CancellationToken, Task> command)
    {
        return new AsyncCommand<object>(async token => { await command(token); return null; });
    }

    public static AsyncCommand<TResult> Create<TResult>(Func<CancellationToken, Task<TResult>> command)
    {
        return new AsyncCommand<TResult>(command);
    }
}

P.S抱歉我的英语不好。在此先感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

您收到NullReferenceException,因为TaskCompletionnull

第二篇(异步命令)文章的原始代码下载中存在错误,如果在构造NotifyTaskCompletion之前完成任务,则TaskCompletion将为空NotifyTaskCompletion

第一篇文章中没有这个错误(根本没有任何TaskCompletion),并且在不久前修复了第二篇文章。我建议你重新下载它。