Search code examples
c#asp.net-web-apiasp.net-apicontroller

.NET Web Api InvalidOperationException accessing global Object


what I'm trying to do:

I use a WebApiApplication to control a music player on a server (imagine it as a kind of democratic music player: my housemates and me all have access to it via smartphone and can control it in the same way).

Web calls are made like this:

http://localhost:64199/api/Music?category=Ambient&action=play

or

http://localhost:64199/api/Music?category=Ambient&action=next

My controller looks like this:

public class MusicController : ApiController
{

    const string MUSICPATH = @"...\Songs";

    public HttpResponseMessage Get(string category, string action, string song = "", bool shuffle = false)
    {

        // DirMusicPlayer.Category = category;
        DirMediaPlayer dmp = DirMediaPlayer.getInstance(category);

        System.Diagnostics.Debug.WriteLine("Thread-ID (Ctrl): " + Thread.CurrentThread.ManagedThreadId);

        switch (action)
        {
            case "play":
                if (song.Equals(""))
                {
                    dmp.PlayAll();
                }
                else
                {
                    dmp.PlaySong(song);
                }
                break;
            case "stop":

                dmp.Stop();
                break;
            case "next":
                dmp.PlayNext();
                break;
            case "prev":
                dmp.PlayPrevious();
                break;
            case "shuffle":
                dmp.Shuffle = shuffle;
                break;
            default:
                break;
        }


        return Request.CreateResponse(HttpStatusCode.OK, "Playing");
    }

}

The Player is a specialised System.Windows.Media.MediaPlayer and implemented as a Singleton. It ist first instantiated in the Global.asax to start with a random song.

Whenever I try to access it afterwards (via web call), the application crashes with an InvalidOperationException, because it cannot access the thread the player is running at.

I have figured out what happens with the Threads:

  1. Thread-ID (AppStart): 1 // Global.asax-Thread
  2. Thread-ID (MediaPlayer): 1
  3. Thread-ID (MusicController): 7 //Controller-instance from Thread-pool
  4. Thread-ID (MediaPlayer): 7 // crashes, because Media-Player instance belongs to Thread 1

As you see, I have an idea of what the problem is, but as I'm pretty new to .NET, I just don't know how to solve it. I need something like a global unique controller which controlls the player and is called by those ApiControllers.

I hope this question has not been asked before, I tried to look it up but I didn't exactly know what to search for.


Solution

  • The MediaPlayer is inherited from a DispatcherObject and it states:

    Only the thread that the Dispatcher was created on may access the DispatcherObject directly. To access a DispatcherObject from a thread other than the thread the DispatcherObject was created on, call Invoke or BeginInvoke on the Dispatcher the DispatcherObject is associated with.

    Now I don't have a setup available to test this and I don't think that MediaPlayer class was designed for use in a Web Application, but you might try this:

     dmp.Dispatcher.Invoke(() => dmp.PlayNext(); );
    

    It will invoke the PlayNext method on the correct thread. If that doesn't result in success I'm afraid you have to accept it will not work.