SynchronizationContext.Current is null on resolvin

2019-02-13 22:56发布

问题:

I have a WPF Code which looks something like this.

public class AlphaProductesVM : BaseModel
{
    private  ObservableCollection<Alphabetical_list_of_product> _NwCustomers;
    private int i = 0;

    public AlphaProductesVM ()
    {
        _NwCustomers = new ObservableCollection<Alphabetical_list_of_product>();
        var repository = new NorthwindRepository();
           repository
               .GetAllProducts()
               .ObserveOn(SynchronizationContext.Current)
               .Subscribe(AddElement);
    }
    public void AddElements(IEnumerable<Alphabetical_list_of_product> elements)
    {
        foreach (var alphabeticalListOfProduct in elements)
        {
            AddElement(alphabeticalListOfProduct);
        }
    }


    public ObservableCollection<Alphabetical_list_of_product> NwCustomers
    {
        get { return _NwCustomers; }
        set { _NwCustomers = value; }
    }}

I use Unity to Resolve the above AlphaProductesVM. This is instant when the Module is discovered using PRISM and the UnityBootstrapper. At runtime .ObserveOn(SynchronizationContext.Current) throws an exception and SynchronizationContext.Current has a null value in it.

回答1:

The SynchronizationContext.Current property will only return a value when invoked on the main thread.

If you need to use a SynchronizationContext object in threads other than the main thread, you could pass the SynchronizationContext instance associated to the main thread to the classes that need it as a dependency.

If you choose this solution, you could register the SynchronizationContext object obtained from the SynchronizationContext.Current property on the main thread as a singleton in your container. That way all requests for a SynchronizationContext from that point on will automatically be satisfied by the container with the singleton:

// Must run in the main thread
container.RegisterInstance(SynchronizationContext.Current);


回答2:

Although there is an implementation of SynchronizationContextfor WPF it is not recommended for use. WPF has the Dispatcher to build responsive applications.

In addition SynchronizationContext.Current only has a value if you are on the UI thread. If your logic runs in a background thread Current will always be null.



回答3:

I'm not sure if this will be a popular suggestion, but you could lazily create and subscribe to your collection. Then the first access to NwCustomers from the UI thread will kick everything off correctly.

public AlphaProductesVM (){}

public ObservableCollection<Alphabetical_list_of_product> NwCustomers
{
    get { 
          if(_NwCustomers == null)
          {
              _NwCustomers = new ObservableCollection<Alphabetical_list_of_product>();
              var repository = new NorthwindRepository();
                  repository
                  .GetAllProducts()
                  .ObserveOn(SynchronizationContext.Current)
                  .Subscribe(AddElement);
          }
          return _NwCustomers; 
    }
}

or, if you inject the UI thread's dispatcher into your view model you can subscribe on that in the constructor.

              var repository = new NorthwindRepository();
                  repository
                  .GetAllProducts()
                  .ObserveOn(theUIdispatcher)
                  .Subscribe(AddElement);