2

So I made a request wrapper for my clients, and everything was working fine. But suddenly (I have no clue why) JsonConvert.DeserializeObject<T>(c) throws the classic exception

The calling thread cannot access this object because a different thread owns it

Well I don't see any other thread but this one. All of them are local variables and according to Newtonsoft https://github.com/JamesNK/Newtonsoft.Json/issues/469

A new JsonSerializerInternalReader is created each time you deserialize an object

Do you have any clue where is the another thread this exception is talking about?

    public static Task<Response<T>> _reqWrapper<T>(Func<Task<HttpResponseMessage>> request) 
        where T : class
    {
        return Task.Run(async () =>
        {
            var response = new Response<T>();

            var hrm = await request().ConfigureAwait(false);                              
            var c = await hrm.Content.ReadAsStringAsync().ConfigureAwait(false);
            response.Content = JsonConvert.DeserializeObject<T>(c);

            return response;
        });

Already tried this without luck.

response.Content = await Task.Run(() => JsonConvert.DeserializeObject<T>(c));

Update

To be sure that that line is the one throwing I made this:

T t = null;
try
{
    t = JsonConvert.DeserializeObject<T>(c);
}
catch { }
response.Content = t

And everything is running fine. Any clues?

Update 2

Stack trace

What I see here is that the serializer is trying to access the main window. I have to say that this is happening inside a ShowDialog() window, so I guess the main window is not available. But I'm not sure if I am correct or how to fix this.

at System.Windows.Threading.Dispatcher.VerifyAccess() at System.Windows.Application.get_MainWindow() at ControliWindows.Globals.Controli.get_Window() in C:... at ControliWindows.Globals.Framework.Modalizer.SaveableModel1..ctor() in C:... at ControliWindows.Views.Modals.AccountMm..ctor() in C:... at CreateControliWindows.Views.Modals.AccountMm() at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value) at ControliWindows.Globals.Connection.<>c__DisplayClass39_0`1.<<_reqWrapper>b__0>d.MoveNext() in C:...

15
  • 2
    Once you await something, you may well be running on a different thread once you resume. Not sure why that matters though in this case. Commented Oct 13, 2015 at 2:21
  • 2
    Try it without the ConfigureAwait(false). Commented Oct 13, 2015 at 2:24
  • 1
    @bto.rdz Please post your entire code, because it's almost definitely not related to your snippet above. Somewhere, there will be a piece of code interacting with a UI component. At the very least, the exception is happening on your PostAsync, which means your response code which you've posted hasn't even executed yet. Commented Oct 13, 2015 at 2:29
  • 2
    @EricJ. normally code after await will run on original thread for WinForms/WPF/ASP.Net... Indeed it is not the case in OP's code as they explicitly request not to come back to original thread (twice) - once with Task.Run and just to be absolutely sure with ConfigureAwait(false)... Commented Oct 13, 2015 at 2:36
  • 1
    I reckon take the Task.Run, etc out of the picture here - just await things and don't use ConfigureAwait(false). You won't be switching to other logical call contexts then and perhaps it'll all just be fine. If you do then need to spin up a separate thread, and thus have things run genuinely in parallel (rather than allowing overlapping response via async/await), introduce it gently and see what causes your failure to return. Commented Oct 13, 2015 at 6:33

3 Answers 3

2

at System.Windows.Threading.Dispatcher.VerifyAccess() at System.Windows.Application.get_MainWindow() at ControliWindows.Globals.Controli.get_Window() in C:... at ControliWindows.Globals.Framework.Modalizer.SaveableModel1..ctor() in C:... at ControliWindows.Views.Modals.AccountMm..ctor() in C:... at

That is the source of your problem. Here is the series of events that is happening:

  • Your T is a ControliWindows.Views.Modals.AccountMm and DeserializeObject must make a new one of them
  • AccountMm's constructor it is creating a ControliWindows.Globals.Framework.Modalizer.SaveableModel1
  • SaveableModel1's constructor it is reading the property ControliWindows.Globals.Controli.Window
  • in Controli.Window it is reading the property System.Windows.Application.Window
  • Application.Window can only be read from the UI thread, this whole chain of events happens on a Threadpool thread and causes your exception.

The easiest solution is have ControliWindows.Globals.Controli.Window detect if it is not on the UI thread and if it is not invoke to the UI to get the value of Application.Window.

public static class Controli
{
    public Window Window
    {
        get
        {
            var application = Application.Current;
            if(application == null)
                return null;
            try
            {
                return application.MainWindow;
            }
            catch(InvalidOperationException)
            {
                return application.Dispatcher.Invoke(() => application.MainWindow);
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

I am amazed that you described my code just with the stack trace I wish I could read all that. what you say makes a lot of sense I will try it. Thanks
@bto.rdz I updated with a example of how to invoke if nessessary, this code was written in the website so there may be a error or two.
@bto.rdz see how easy it is to diagnose such problems when looking at the full exception information?
@usr Thanks for that tip, I guess I need practice reading stack traces. That did not mean too much for me, but there is everything
@bto.rdz in the future use the Copy Exception Details to Clipboard link in the debugger then just paste the text inside <pre></pre> tags in your question (The tags help keep the formatting of the text). That gives you the exception name, the message, and the stack trace all in a single click (it is coping the output of .ToString() of your exception, when you want to log a exception use .ToString() to get the same message in your code)
0

By using ConfigureAwait(false) you are explicitly telling your await to not try to resume the execution of the code on the same thread after performing your await.

From the documentation:

continueOnCapturedContext

Type: System.Boolean

true to attempt to marshal the continuation back to the original context captured; otherwise, false.

Try using ConfigureAwait(true) instead.

2 Comments

Alternatively, just don't call ConfigureAwait at all - the default behaviour is it to mimic ConfigureAwait(true).
that is quite surprising, then. can you make sure you really are not on the same thread ? With the Thread debug tools you can get their ID or even name them. If it doesn't work, you could try, ContinueWith using FromCurrentSynchronizationContext, instead of awaiting the task, but I thought awaits were already doing that.
0

Deserilization happens in another thread. It opens own thread

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.