简体   繁体   English

API的异步方法会调整变量,然后该变量处于其原始状态

[英]Async method to API adjusts variable and then afterwards the variable is in its original state

Alright so I use an eID-cardreader to get Name etc from an ID-card and then I use the following method to look if there's already someone in the database with the exact same info: 好的,所以我使用eID卡读卡器从ID卡中获取Name等,然后使用以下方法查看数据库中是否已经有人具有完全相同的信息:

    private async void GetCustomer()
    {
        using (HttpClient client = new HttpClient())
        {
            HttpResponseMessage response = await client.GetAsync("http://localhost:32576/api/customer?name=" + EIDCustomer.CustomerName + "&address=" + EIDCustomer.Address);
            if(response.IsSuccessStatusCode)
            {
                string json = await response.Content.ReadAsStringAsync();

                ApplicationVM.customer = JsonConvert.DeserializeObject<Customer>(json);
            }
            else
            {
                Console.Write("api fail");
            }
        }
    }

Alright so ApplicationVM.customer is the following: " public static Customer customer = null; " GetCustomer() is being called in the following method: 好了, ApplicationVM.customer如下:“ public static Customer customer = null; ”正在以下方法中调用GetCustomer()

private Customer GetCustomerFromIDReader()
{
    Customer c = new Customer();

    try
    {
        //BEID_ReaderSet.initSDK();
        BEID_ReaderSet readerSet = BEID_ReaderSet.instance();
        BEID_ReaderContext reader = readerSet.getReader();

        if(reader.isCardPresent())
        {
            BEID_EIDCard card = reader.getEIDCard();
            BEID_EId doc = card.getID();

            string firstName = doc.getFirstName();
            string lastName = doc.getSurname();
            int postCode = Int32.Parse(doc.getZipCode());
            string straatEnNr = doc.getStreet().Trim();
            string gemeente = doc.getMunicipality();
            c.CustomerName = firstName + "" + lastName;
            c.Address = straatEnNr + ";" + postCode + ";" + gemeente;
            c.Picture = card.getPicture().getData().GetBytes();
        }
        else
        {
            c = null;
        }

        BEID_ReaderSet.releaseSDK();
    }
    catch (BEID_Exception ex)
    {
        Console.Write("Er is iets misgelopen bij het inlezen van de eID");
        return null;
    }

    EIDCustomer = c;

    GetCustomer();

    return c;
}

EIDCustomer is a property of the type Customer. EIDCustomer是“客户”类型的属性。 Alright so then the program is supposed to check if c is null, if it's not then it checks if ApplicationVM.customer is null. 好的,因此该程序应该检查c是否为空,否则,它将检查ApplicationVM.customer是否为空。 If so you get redirected to a window where you're asked to make a new account. 如果是这样,您将被重定向到一个窗口,要求您创建一个新帐户。 If ApplicationVM.customer is not null, you get to go to the next window. 如果ApplicationVM.customer不为null,则转到下一个窗口。 This is the code: 这是代码:

public ICommand LoginCommand
{
    get
    {
        return new RelayCommand(Login);
    }
}

public void Login()
{
    ApplicationVM appvm = App.Current.MainWindow.DataContext as ApplicationVM;

    Customer c = new Customer();

    c = GetCustomerFromIDReader();

    if(c == null)
    {
        Console.Write("Er is iets fout gelopen bij het inlezen van de e-IDkaart");
    }
    else if (ApplicationVM.customer == null)
    {
        appvm.ChangePage(typeof(KlantNieuwVM));
    }
    else
    {
        appvm.ChangePage(typeof(KlantenInterfaceVM));
    }
}

( I work with UserControls to change pages, I'm always on MainWindow, but I don't think that's of any importance to the question. ) (我使用UserControls来更改页面,我一直在MainWindow上,但是我认为这对这个问题没有任何意义。)

This is the method being called in the API: 这是在API中调用的方法:

public Customer Get(string name, string address)
{
    return CustomerDA.CheckCustomerLogin(name, address);
}

CustomerDA.CheckCustomerLogin is just an sql statement which checks if there's someone in the database with the exact same name and address. CustomerDA.CheckCustomerLogin只是一条sql语句,用于检查数据库中是否有人名称和地址完全相同。

The problem you are experiencing is that your GetCustomer() method is asynchronous and you are not awaiting it. 您遇到的问题是您的GetCustomer()方法是异步的,并且您没有在等待它。 So when you hit the HttpResponseMessage line: 因此,当您点击HttpResponseMessage行时:

HttpResponseMessage response = await client.GetAsync("");

control is returned to your command and execution continues prior to the request coming back. 控制权返回给您的命令,并在请求返回之前继续执行。

You can fix this by making your command async. 您可以通过使命令异步来解决此问题。

When you use the async/await pattern, the entire call chain needs to be awaitable, or else you will have this problem. 当您使用异步/等待模式时,整个呼叫链需要等待,否则您将遇到此问题。

GetCustomerFromIDReader GetCustomerFromIDReader

private async Task<Customer> GetCustomerFromIDReader()
{
    Customer c = new Customer();

    // .... redacted for clarity

    await GetCustomer();

    return c;
}

Command usage 命令用法

public ICommand LoginCommand
{
    get
    {
        return new RelayCommand(async () => await Login());
    }
}

public async Task Login()
{
    ApplicationVM appvm = App.Current.MainWindow.DataContext as ApplicationVM;

    Customer c = new Customer();

    c = await GetCustomerFromIDReader();

    if(c == null)
    {
        Console.Write("Er is iets fout gelopen bij het inlezen van de e-IDkaart");
    }
    else if (ApplicationVM.customer == null)
    {
        appvm.ChangePage(typeof(KlantNieuwVM));
    }
    else
    {
        appvm.ChangePage(typeof(KlantenInterfaceVM));
    }
}

Lastly change your GetCustomer to return a Task and not be void. 最后,将您的GetCustomer更改为返回Task且不作废。

GetCustomer GETCUSTOMER

private async Task GetCustomer()
{
    using (HttpClient client = new HttpClient())
    {
        HttpResponseMessage response = await client.GetAsync("");
        if(response.IsSuccessStatusCode)
        {
            string json = await response.Content.ReadAsStringAsync();

            ApplicationVM.customer = JsonConvert.DeserializeObject<Customer>(json);
        }
        else
        {
            Console.Write("api fail");
        }
    }
}

As I explain in my MSDN article on async best practices , avoid async void . 正如我在有关异步最佳实践的MSDN文章中所解释的那样,请避免async void

So, your async void method should be async Task : 因此,您的async void方法应为async Task

private async Task GetCustomerAsync()
{
  ...
}

And when it's called, it should be await ed, which in turn makes the calling method async : 并且在调用它时,应该await它,这又使调用方法async

private async Task<Customer> GetCustomerFromIDReaderAsync()
{
  ...

  EIDCustomer = c;

  await GetCustomerAsync();

  return c;
}

And the same with the method that calls that one: 与调用该方法的方法相同:

public async Task LoginAsync()
{
  ApplicationVM appvm = App.Current.MainWindow.DataContext as ApplicationVM;

  Customer c = new Customer();

  c = await GetCustomerFromIDReaderAsync();

  if(c == null)
  {
    Console.Write("Er is iets fout gelopen bij het inlezen van de e-IDkaart");
  }
  else if (ApplicationVM.customer == null)
  {
    appvm.ChangePage(typeof(KlantNieuwVM));
  }
  else
  {
    appvm.ChangePage(typeof(KlantenInterfaceVM));
  }
}

Finally, you end up with the ICommand implementation, where you could use async void (since it is logically an event handler): 最后,你最终的ICommand执行,在那里你可以使用async void (因为它是逻辑上的事件处理程序):

public ICommand LoginCommand
{
  get
  {
    return new RelayCommand(async () => { await LoginAsync(); });
  }
}

However, you'll want to consider several points when dealing with asynchronous commands. 但是,在处理异步命令时,您需要考虑几点。 This simple example will not disable the ICommand while it is executing, nor will it handle exceptions cleanly. 这个简单的示例不会在执行ICommand时禁用它,也不会干净地处理异常。 I have another MSDN article specifically on asynchronous MVVM commands . 我还有另一篇专门针对异步MVVM命令的MSDN文章

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM