1

So I am only a few days into learning about wcf services, specifically duplex, and I am starting with a test app. The goal is to have a Service that has an internal (static?) class which stores variables, and a Client that fetches for those variables.

Currently I have two variables in the Storage class, one which is a list of Subscribers (ObservableCollection<IMyContractCallBack>) and one which is an ObservableCollection<string>, where each string gets sent in the callback method to the client.

I would like to be able to have the client Fetch (which first Subscribes if not already, by adding its context to the collection on the server side) the strings in the collection on the server side. That part works as expected. However, I would also like to Push a string from the server to every client in the subscription list, as well as Add strings to the collection of strings. That's where my issues crop up.

Anytime I Fetch, it adds to string list "test1..." and "test2..." and sends them, then the client updates a textblock UI (wpa) so if I fetch twice I'll have "test1...","test2...","test1...","test2..." because right now there's no checking for duplicates. That proves that the collection can get updated and remembered on the server side from Fetch to Fetch. However, when I try to Add or Send a given text, all variables are forgotten, so the subscriber list is null, and the list-to-add-to is empty. Yet when I then Fetch again, the old list is back (now with 6 things, test1...,test2... etc...)

I have this before the class

[ServiceBehavior(InstanceContextMode= InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]

and I also tried a Singleton context mode to no avail. Changing the ConcurrencyMode to Multiple doesn't do anything different either. Any ideas as to why my static data is being reset only when internal commands come from the server itself?

Here is the code for my Service:

namespace WcfService3
{   
[ServiceBehavior(InstanceContextMode= InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
public class Service1 : IService1
{
    public static event Action NullContext;

    public static ObservableCollection<IMyContractCallBack> Subscriptions;

    public void     NormalFunction()
    {

        //Only sends to Subs that are STILL Open
        foreach (IMyContractCallBack user in Subscriptions)
        {
            //Removes the Closed users, because they are hanging around from last session
            if (((ICommunicationObject)user).State != CommunicationState.Opened)
            {
                Subscriptions.Remove(user);
            }
            else
            {
                ObservableCollection<string> holder = Storage.GetList();

                foreach (string str in holder)
                {
                    user.CallBackFunction(str);
                }
            }
        }
    }
    public static void Send(string str)
    {
        try
        {
            foreach (IMyContractCallBack user in Subscriptions)
            {
                user.CallBackFunction(str);
            }
        }
        catch
        {
            //For some reason 'Subscriptions' is always null
            NullContext.Invoke();
        }
    }
    public static void Add(string str)
    {
        //For some reason 'SendList' is always null here, too
        Storage.AddToList(str);
        if (Subscriptions != null)
        {
            //For same reason 'Subscriptions' is always null
            foreach (IMyContractCallBack user in Subscriptions)
            {
                user.CallBackFunction(str);
            }
        }
    }

    public void Subscribe()
    {
        //Adds the callback client to a list of Subscribers
        IMyContractCallBack callback = OperationContext.Current.GetCallbackChannel<IMyContractCallBack>();
        if (Subscriptions == null)
        {
            Subscriptions = new ObservableCollection<IMyContractCallBack>();
        }
        if(!Subscriptions.Contains(callback))
        {
            Subscriptions.Add(callback);
        }
    }

and here is my code for the Storage class:

namespace WcfService3
{
public static class Storage
{

    public static readonly ObservableCollection<string> SendList = new ObservableCollection<string>();

    public static IMyContractCallBack callback;

    public static ObservableCollection<string> GetList()
    {

        if (SendList.Count == 0)
        {
            AddToList("Test1...");
            AddToList("Test2...");
        }

        return SendList;
    }

    public static void AddToList(string str)
    {


            SendList.Add(str);

    }
}

}

I can provide more code if needed.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Adam
  • 488
  • 6
  • 19

1 Answers1

0

Are you using the ThreadStatic attribute anywhere? (just do a quick search) Thats a real long shot and probably not your issue.

You probably have a threading issue. Do all your clients connect at the same time (i really mean in close succession?) If yes, you are going to have threading issues with this code in your Subscribe method:

   if (Subscriptions == null)
    {
        Subscriptions = new ObservableCollection<IMyContractCallBack>();
    }

You should better constrain access to your Subscriptions method so you can see who modifies it and when and use Console statments to figure out where you're going wrong.

Community
  • 1
  • 1
wal
  • 17,409
  • 8
  • 74
  • 109
  • Yeah, I really think it's a threading issue so I added ThreadStatic to `holder` in the `Storage` class (which is now shown above) but that just made it null when called from the service, as opposed to just empty, since it gets initialized. So the `ThreadStatic` made it only initialize once, but still null on the service thread – Adam Jul 23 '12 at 18:04
  • Well thats your answer then, there is no way that should be ThreadStatic as its going to be accessed by a bunch of different threads. remove that and see what impact it has (and/or post more code). Do you understand what impact `ThreadStatic` will have? – wal Jul 23 '12 at 23:42
  • Perhaps not? I thought it would keep it static between threads. I started without `ThreadStatic`, but when it wasn't working properly I added it in to test and it worked worse. Without `ThreadStatic` it acts as described in my original question: it gets reinitialized when called by `Service1`. – Adam Jul 24 '12 at 12:35
  • Oh I suppose I'm looking for the opposite of `ThreadStatic`. I want it to be the same synced between threads, however I also tried `lock` – Adam Jul 24 '12 at 12:39
  • the opposite of ThreadStatic is not to have it defined at all. 'synced between threads' ie you want it to be a global (static) variable.? also you said, without `Threadstatic` it gets reinitialized - what is reinitlaizing it? add a console.writeline out or set a breakpoint so you know when it gets reinitialized – wal Jul 24 '12 at 12:44
  • by "reinitialized" i mean it goes through `public static readonly ObservableCollection SendList = new ObservableCollection();` again. So yes, I suppose global static is what I want! – Adam Jul 24 '12 at 12:46
  • how can it possibly go thru there twice in the code you posted? put a breakpoint on that, the first time it gets hit its valid, the 2nd time must be invalid (if it does occur) – wal Jul 24 '12 at 12:51
  • Yes, it does occur. First time callstack: `WcfService3.DLL!WcfService3.Storage.Storage() Line 12 C#, [External Code]` and second time: `> WcfService3.dll!WcfService3.Storage.Storage() Line 12 C#, [External Code], HostUI.exe!HostUI.MainWindow.button2_Click(object sender, System.Windows.RoutedEventArgs e) Line 41 + 0x1b bytes C#, [External Code]` where first time is `Fetch()` from the client, and second is `Add` from the host, and WcfService3 is a wpf client app – Adam Jul 24 '12 at 12:56
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/14343/discussion-between-wal-and-adam) – wal Jul 24 '12 at 12:56
  • So the problem is that my UI for the host wasn't using the WcfService as a service reference, just a normal reference. Thanks Wal for helping me! – Adam Jul 24 '12 at 13:37