2 Stimmen

Verwendung des CCR mit asynchronem WCF-Dienst

Ich lerne, wie man die CCR (Concurrency and Coordination Runtime) in Verbindung mit einem asynchronen WCF Web Service verwendet.

Dies ist der Test-WCF-Dienst:

    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();
        }
    }

Er nimmt eine ID-Nummer und gibt den dazugehörigen Kontonamen zurück (falls vorhanden).

Ich habe eine CCR-Funktion geschrieben, die das/die passende(n) Konto/Konten finden soll (natürlich ist noch viel Arbeit nötig - dies ist nur ein Proof of Concept) An dieser Stelle komme ich nicht weiter.

Wie gebe ich die Ergebnisse zurück (Global port?) UND was noch wichtiger ist: Wie kann ich den CCR in den WCF Asynchronous Service Call einbinden, ohne einen Thread zu blockieren?

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(); 
    }

1voto

Andrew Harry Punkte 13617

OK, endlich bin ich mit all dem weitergekommen!

Der erste: Sie benötigen eine eigene AsyncResult-Klasse

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, dann müssen wir dies mit den Async WCF Methodenaufrufen verbinden:

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;
            }
        }
    }

Dann brauchen Sie nur die IEnumerator-Methode, um die Antwort an den Port zu senden

0voto

Mike Punkte 7368

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X