wcfasynchronousccr

Using the CCR with ASynchronous WCF Service


I'm learning how to use the CCR (Concurrency and Coordination Runtime) in conjunction with a Asynchronous WCF Web Service.

This is the Test WCF Service:

    public class Service : IService
    {
        private Accounts.Manager accountManager = new Accounts.Manager();
        public IAsyncResult BeginGetAccount(int id, AsyncCallback callback, object state)
        {
            //How Do I Call the CCR Function without blocking a Thread?
            throw new NotImplementedException();
        }

        public string EndGetAccount(IAsyncResult result)
        {
            //How Do I Finish the Call and Pass back the Result?
            throw new NotImplementedException();
        }
    }

It will take a ID number and return the Matching Account Name (if Any)

I have written a CCR function that should find the matching account(s) (obviously needs a lot of work - this is just proof of concept) Here is where I come unstuck.

How do I pass back the results (Global port?) AND more importantly: How do I plug the CCR into the WCF Asynchronous Service Call without blocking a Thread?

public IEnumerator<ITask> GetAccount(int id)
    {
        SqlDataReader reader = null; 
        SqlConnection connection = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=BizData;Integrated Security=True;Async=True;"); 
        string query = "SELECT * FROM Account WHERE AccountID = @AccountID"; 
        SqlCommand command = new SqlCommand(query, connection);
        SqlParameter accountID = new SqlParameter("AccountID", id);
        command.Parameters.Add(accountID);

        connection.Open(); 
        yield return Arbiter.Choice(SQLAdapter.GetReader(command), 
            delegate(SqlDataReader r) { reader = r; }, 
            delegate(Exception e) { Console.Write("Failed to get SQL data"); }); 

        if (reader == null) yield break;       

        while (reader.Read())       
        {
            Account account = new Account { ID = Convert.ToInt32(reader["AccountID"]), 
                Name = reader["Account"].ToString(), 
                ParkingNo = Convert.ToInt32(reader["ParkingNo"]), 
                Password = reader["Password"].ToString() };
            //Post account?
        }
        connection.Close(); 
    }

Solution

  • OK i finally got somewhere with all of this!

    First up: You need a custom AsyncResult Class

    class AsyncResult : IAsyncResult , IDisposable
        {
            object _state;
            ManualResetEvent _waitHandle = new ManualResetEvent(false);
            bool _isCompleted;
    
            #region IAsyncResult Members
            public object AsyncState
            {
                get { return _state; }
            }
    
            public System.Threading.WaitHandle AsyncWaitHandle
            {
                get { return _waitHandle; }
            }
    
            public bool CompletedSynchronously
            {
                get { return false; }
            }
    
            public bool IsCompleted
            {
                get { return _isCompleted; }
            }
            #endregion
    
            Exception _exception;
            internal Exception Exception
            {
                get { return _exception; }
            }
    
            Accounts.Account _result;
            internal Accounts.Account Result
            {
                get { return _result; }
            }
    
            internal AsyncResult(PortSet<Accounts.Account, Exception> port, DispatcherQueue queue, AsyncCallback callback, object state)
            {
                _state = state;
    
                Arbiter.Activate(queue,
                    Arbiter.Choice(port,
                        r =>
                        {
                            _result = r;
                            Complete(callback);
                        },
                        e =>
                        {
                            _exception = e;
                            Complete(callback);
                        }
                    )
                );
            }
    
            private void Complete(AsyncCallback callback)
            {
                _isCompleted = true;
                _waitHandle.Set();
    
                if (callback != null)
                {
                    ThreadPool.QueueUserWorkItem(s => callback(this));
                }
            }
    
            private bool disposedValue = false;
    
            public void Dispose()
            {
                if (!this.disposedValue)
                {
                    _waitHandle.Close();
                    _waitHandle = null;
                    _state = null;
                }
                this.disposedValue = true;
            }
        }
    

    Ok, then we need to tie this in with the Async WCF Method calls:

    public class Service : IService
        {
            private Dispatcher dispatcher;
            private DispatcherQueue dq;
    
            public Service() 
            {
                 dispatcher = new Dispatcher();
                 dq = new DispatcherQueue("CCR DispatcherQueue", dispatcher);
            }
    
            public IAsyncResult BeginGetAccount(int id, AsyncCallback callback, object state)
            {
                PortSet<Accounts.Account, Exception> port = new PortSet<Accounts.Account, Exception>();
                Accounts.Manager manager = new Accounts.Manager();
                manager.GetAccountData(dq, port, id);
    
                AsyncResult result = new AsyncResult(port, dq, callback, state);
                return result;
            }
    
            public string EndGetAccount(IAsyncResult result)
            {
                {
                    var AccountName = string.Empty;
                    if ((result != null))
                    {
                        using (Common.AsyncResult asyncResult = result as Common.AsyncResult)
                        {
    
                            if (asyncResult == null)
                            {
                                throw new NullReferenceException("IAsynchResult Parameter is Null");
                            }
    
                            asyncResult.AsyncWaitHandle.WaitOne();
                            if (asyncResult.Result != null) 
                            { 
                                AccountName = asyncResult.Result.Name; 
                            }
                        }
                    }
                    return AccountName;
                }
            }
        }
    

    Then you just need the IEnumerator method to post the answer to the port