1

I'm writing a WCF application that generates sets of data, and allows the client to create them, navigate through them, and retrieve data from them. The data is stored in static variables of the service class. Particuarly, they're stored in a dictionary, using GUID strings as the keys.

I'm setting the instancing mode of the service class to PerSession, which as far as I know should work. The client calls a function called CreateRecordSet, which adds an entry to the dictionary and returns the key string. The client then calls a function called First(), which should retrieve the first record in the data set. However, when it attempts this, the dictionary no longer contains any entries.

Strangely enough, if I set the instancing mode to Single, it works fine. But, if I set it to PerSession, it seems to lose the dictionary between the two calls, which I would expect if I had set it to PerCall.

Here's the interface definition:

namespace OrsonServiceLibrary
{
    [ServiceContract]
    public interface IService1
    {

        [OperationContract]
        string CreateRecordSet();

        [OperationContract]
        object First(string setkey);

        // Removed unused methods

    }

    [DataContract]
    [KnownType(typeof(JMMCustomer))]
    public class JMMCustomer
    {
        [DataMember]
        public string CUSTFNAME { get; set; }
        [DataMember]
        public string CUSTLNAME { get; set; }
        [DataMember]
        public string CUSTADDRESS { get; set; }
        [DataMember]
        public string CUSTCITY { get; set; }
        [DataMember]
        public string CUSTKEY { get; set; }

    }
}

Here's the service class code:

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    public class Service1 : IService1
    {
        protected static Dictionary<String,RecordSet> RecordSets;

        public Service1()
        {
            RecordSets = new Dictionary<string,RecordSet>();
        }

        public string CreateRecordSet()
        {
            List<object> c = new List<object>(GetAllCustomers());
            var rs = new RecordSet(c);
            string thekey = System.Guid.NewGuid().ToString();

            RecordSets.Add(thekey, rs);

            Console.WriteLine("End of CreateRecordSet().  RecordSets.Count = " + RecordSets.Count);

            return thekey;
        }

        public object First(string setkey)
        {
            Console.WriteLine("Beginning of First().  RecordSets.Count = " + RecordSets.Count);

            if (RecordSets[setkey].thelist.Count < 1)
                throw new Exception ("No items in the data set.");
            else
                RecordSets[setkey].cursor = 0;

            return RecordSets[setkey].thelist[RecordSets[setkey].cursor];
        }

        // Removed unused methods
   }

Here's the client code:

static void Main(string[] args)
{
    Console.WriteLine("Press Enter to begin.");
    Console.ReadLine();

    ServiceReference1.Service1Client MyService = new ServiceReference1.Service1Client("Service1");

    string sk = MyService.CreateRecordSet();

    ServiceReference1.JMMCustomer jc;
    jc = (ServiceReference1.JMMCustomer)MyService.First(sk);

    Console.WriteLine(jc.CUSTFNAME + " " + jc.CUSTLNAME);

    Console.WriteLine("Press Enter to exit.");
    Console.ReadLine();
}

As you can see, I add some debugging messages to the hosts's console to show the status of the dictionary. Here's the hosts's output:

Host started.  Press Enter to terminate host.
End of CreateRecordSet().  RecordSets.Count = 1
Beginning of First().  RecordSets.Count = 0

And then of course, I get an exception in the next line of code in First() when I try to reference RecordSets[setkey] because RecordSets is empty.

If I change the behavior to:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]

...it then works as expected. The host output becomes:

Host started.  Press Enter to terminate host.
End of CreateRecordSet().  RecordSets.Count = 1
Beginning of First().  RecordSets.Count = 1

... and then the client prints the customer names as expected, without errors.

So what am I missing? My understanding is that as long as I use the same ServiceReference (MyService) for both calls, then they should share the same static variables on the server if the server's InstanceContextMode is set to PerSession. Am I incorrect?

-Joe

JoeMjr2
  • 3,804
  • 4
  • 34
  • 62
  • What binding are you using. Not all support sessions. InstanceContextMode=InstanceContextMode.Single means that the class becomes a singleton – Robert Slaney Jan 03 '13 at 05:04
  • 1
    I would strongly advise against static variables as you'll inconsistences when there are multiple instances maintained by WCF ( ie more that 1 concurrent session ). Just make it an instance variable is it's designed to be re-used by 1 WCF client, otherwise you need to make it safe for concurrent access ( ie do not instantiate statics in the constructor - ever!!) – Robert Slaney Jan 03 '13 at 05:11
  • Robert, I am using BasicHttpBinding. How/where in the code do I declare/instantiate instance variables? – JoeMjr2 Jan 03 '13 at 06:23
  • 3
    BasicHttpBinding does not support sessions. Remove the static keyword from the Dictionary declaration to make it an instance scope. Then each session will have it's own copy when the service instance is created by WCF – Robert Slaney Jan 03 '13 at 06:29
  • Change your binding to WSHttpBinding. See http://stackoverflow.com/questions/7330030/does-wcf-basichttpbinding-support-persession – Robert Slaney Jan 03 '13 at 06:32
  • Ok, the problem is that the REAL app that I'm writing is a Window Store app, which (as far as I can tell) doesn't support sessions. So I guess my only option is to use Single if I need to persist variables? – JoeMjr2 Jan 04 '13 at 18:45
  • yes, you will need Single mode, with a backing session store with the key passed in ( as an HTTP header if the coding model allows it ) - Very similar to "classic" ASP.NET websites with Session – Robert Slaney Jan 07 '13 at 02:58
  • @RobertSlaney, is there any reason that everyone can't just share the one Dictionary? – JoeMjr2 Jan 07 '13 at 08:11
  • Nope, as long as you're careful about concurrency – Robert Slaney Jan 08 '13 at 20:31
  • 1
    @RobertSlaney, you have only posted your replies as comments, and not as a formal answer. If you'd like to summarize your replies as an "answer" on the site, then I will be happy to mark it as the accepted answer. – JoeMjr2 Jan 10 '13 at 21:16

0 Answers0