0

我在 WinRt 中使用 C# 和 SQLite 从 Windows 应用商店应用程序调用 WinRt 类库...对象在意外时间返回空引用错误...

我正在尝试实现一个登录服务,通过检查数据文件是否存在来检查当前用户是否已经登录,然后检查用户是否是当前登录的用户......

用户只需输入他们的 ID 并单击登录按钮即可。它创建一个包装 SQLite 数据库的 DataService 对象,然后将其“注入”到 UserStartupService 中。

UserStartupService 使用依赖注入、单例并实现 IDisposable。

问题 1)如果用户再次单击登录按钮,则 UserStartupService 对象构造函数不会运行,并且当使用内部对象时,即使它在退出 using 块后通过 dispose 方法运行,也会引发空引用错误,这迫使我停用登录按钮,这充其量是一个kluge修复。新用户必须退出程序才能以新用户身份登录。(原始代码没有实现 IAsyncOperationWithProgress,但这应该没关系...)

问题 2)我现在正在尝试实现 IAsyncOperationWithProgress 以将进度传递回 UI,并且在尝试在线使用 _dataFeedService 时会收到空引用错误:

var json = await _dataFeedService.ValidateUser(userId);

即使它按预期在 using 语句的顶部运行构造函数......

我认为这里缺少一个范围/线程问题。也许有什么明显的...

有任何想法吗?谢谢!

// logon button pressed...
private void LogOn_Click(object sender, RoutedEventArgs e)
{
    // Create database service for DI
    DataService _dataService = new DataService("MyData.sqlite");

    // using statement for scope control
    using (UserStartupService uss = UserStartupService.GetInstance(_dataService))
    {
        // progress bar...
        CurrentProgress.Visibility = Windows.UI.Xaml.Visibility.Visible;

        // create op and call...
        IAsyncOperationWithProgress<string, int> op;
        op = uss.SetUpUser(txtUserId.Text);

        op.Progress = (info, progress) =>
        {
            CurrentProgress.Value = progress;
        };

        op.Completed = (info, status) =>
        {
            var results = info.GetResults();
            // when completed...
            if (status == AsyncStatus.Completed)
            {
                txtMessage.Text = "Current user data already loaded...";
                CurrentProgress.Value = 100;
            } // if cancelled...
            else if (status == AsyncStatus.Canceled)
            {
                // Operation canceled - not implemented...
            }
        };
    }
    btnLogon.IsEnabled = false;
}


public sealed class UserStartupService : IDisposable
{
    #region properties

    // services
    private static DataService _dataService;
    private static DataFeedService _dataFeedService;
    private static SqliteService _sqlMAFService;
    private static SerialiseDeserialiseService _serializeService;

    private string _token = String.Empty;

    #endregion properties

    #region constructors with DI and singleton pattern

    // use this code to implement singleton patter...
    // private constructor = can't instance without GetInstance...
    private UserStartupService(DataService dataService)
    {
        // guard clause...
        if (dataService == null)
        {
            throw new ArgumentNullException("DataService");
        }
        _dataService = dataService;
        _dataFeedService = new DataFeedService();
        _sqlMAFService = new SqliteService(_dataService);
        _serializeService = new SerialiseDeserialiseService();
    }

    // implement singleton
    public static UserStartupService GetInstance(DataService dataService)
    {
        _dataService = dataService;
        return MyNestedSingletonClass.singleton;
    }

    class MyNestedSingletonClass
    {
        internal static readonly UserStartupService singleton = new UserStartupService(_dataService);

        static MyNestedSingletonClass() { }
    }

    #endregion constructors with DI and singleton pattern

    public IAsyncOperationWithProgress<string, int> SetUpUser(string userId)
    {
        return AsyncInfo.Run<string, int>((token, progress) =>
            Task.Run<string>(async () =>
            {
                progress.Report(1);

                try
                {
                    // validate user against server REST feed and get token
                    var json = await _dataFeedService.ValidateUser(userId);

                                                        // ... it never gets here due to _dataFeedService null exception
                    // ...more code ... never gets here...


                }
                catch (Exception ex)
                {
                    return ex.Message;
                }

                progress.Report(100);

                return "";
            }, token));
    }

    #region implement IDisposable
    public void Dispose()
    {
        _serializeService = null;
        _sqlMAFService.Dispose();
        _sqlMAFService = null;
        _dataFeedService.Dispose();
        _dataFeedService = null;
        _dataService.CloseConnection();
        _dataService = null;
    }
    #endregion implement IDisposable
}
4

1 回答 1

0

using块将uss在执行完成之前进行处理,因此这就是您的空引用异常的来源(对于这两个问题)。

如果UserStartupService是单例,并且可以多次使用,则不要丢弃它。

另外,我建议使用await而不是回调(它通常更简单),所以这样的事情应该可以工作:

private async void LogOn_Click(object sender, RoutedEventArgs e)
{
  btnLogon.IsEnabled = false;

  // Create database service for DI
  DataService _dataService = new DataService("MyData.sqlite");

  var uss = UserStartupService.GetInstance(_dataService);

  // progress bar...
  CurrentProgress.Visibility = Windows.UI.Xaml.Visibility.Visible;

  // create op and call...
  var op = uss.SetUpUser(txtUserId.Text)
      .AsTask(progress => { CurrentProgress.Value = progress; });
  var result = await op;

  txtMessage.Text = "Current user data already loaded...";
  CurrentProgress.Value = 100;

  btnLogon.IsEnabled = true;
}
于 2012-12-07T16:40:47.317 回答