Async Entity Framework for WPF

– Sample application , implemented ObjectContext, – base class for your entity data modal classes in WPF project, where data is used via ADO.net instead of WCF. The extension adds Async capability to your existing entity data model ..This is based on .net 4.0 Task..The below mentioned approach is one way implementing Async entity call , wrapped inside thread call and finally exposed event when results are available.Another prefred approach to achive this is via Reactive Extension library.

The Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators. Using Rx, developers represent asynchronous data streams with Observables, query asynchronous data streams using LINQ operators, and parameterize the concurrency in the asynchronous data streams using Schedulers. Simply put, Rx = Observables + LINQ + Schedulers.

 My next article is about Reactive Extensions for streaming data similar to SQL Server Management Studio.This way the grid would show first 100 available rows in the beginning and then when the next 100 rows were loaded they would be added to the grid and so on until the query completes.

Create class ObjectContext

public class ObjectContext :EntityContext, IDisposable
    {
        public LoadOperation LoadQuery(Action query)
        {
            return new LoadOperation(query);
        }

        public LoadOperation LoadQuery(Func<object> query)
        {
            return new LoadOperation(query);
        }

        public void Dispose()
        {
            //throw new NotImplementedException();
        }
    }

public sealed class QueryResultEventArgs : EventArgs
    {
        internal QueryResultEventArgs(Exception exception)
        {
            this.Error = exception;
            this.HasError = true;
        }

        internal QueryResultEventArgs(Exception exception, bool isCancelled, bool isCompleted, bool hasError)
            : this(exception)
        {
            this.IsCanceled = isCancelled;
            this.IsComplete = isCompleted;
            this.HasError = hasError;
            /// if (hasError == false) this.Error = null;

        }

        internal QueryResultEventArgs(object result)
        {
            this.HasError = false;
            this.Result = result;
            this.IsComplete = true;
            this.IsCanceled = false;
        }

        public object Result { get; private set; }
        public Exception Error { get; private set; }
        public bool HasError { get; private set; }
        public bool IsCanceled { get; private set; }
        public bool IsComplete { get; private set; }
    }

    public sealed class LoadOperation : DispatcherObject, IDisposable
    {
        public event EventHandler<QueryResultEventArgs> Completed;
        private readonly CancellationTokenSource _taskCancelProvider;
        private readonly Task _task;
        private LoadOperation()
        {
            this._taskCancelProvider = new CancellationTokenSource();
        }

        internal LoadOperation(Func<object> query)
            : this()
        {

            Task<object> task = new Task<object>(query, this._taskCancelProvider.Token);
            _task = task;
            this.SetTaskInternal(task);
        }

        internal LoadOperation(Action query)
            : this()
        {
            Task task = new Task(query, this._taskCancelProvider.Token);
            _task = task;
            this.SetTaskInternal(task);
        }

        private void SetTaskInternal(Task<object> task)
        {
            task.ContinueWith(delegate
            {
                try
                {
                    RaiseCompletetionEvent(new QueryResultEventArgs(task.Result));
                }
                catch (Exception ex)
                {
                    RaiseCompletetionEvent(new QueryResultEventArgs(ex.InnerException, task.IsCanceled, task.IsCompleted, task.IsFaulted));

                }
            });
        }

        private void SetTaskInternal(Task task)
        {
            task.ContinueWith(delegate
            {
                try
                {

                    RaiseCompletetionEvent(new QueryResultEventArgs(null));
                }
                catch (Exception ex)
                {
                    RaiseCompletetionEvent(new QueryResultEventArgs(ex));

                }
            });
        }

        public bool CanCancel { get { return this._taskCancelProvider.Token.CanBeCanceled; } }

        public void Cancel()
        {
            this._taskCancelProvider.Cancel();
        }

        public void Cancel(bool throwOnFirstException)
        {
            this._taskCancelProvider.Cancel(throwOnFirstException);
        }

        private void RaiseCompletetionEvent(QueryResultEventArgs e)
        {
            //Wrap in DispatchThread
            this.Dispatcher.Invoke(
          System.Windows.Threading.DispatcherPriority.Normal,
          new Action(
            delegate()
            {
                if (Completed != null)
                    Completed(this, e);
            }
        ));

        }

        public void Execute(bool Async = true)
        {
            if (Async)
            {
                _task.Start();
            }
            else
            {
                _task.RunSynchronously();
            }
        }

        public void Dispose()
        {
            _task.Dispose();
        }
    }

// EntityContext - Base for all your entity classes (LocalEntities - generated by entity framework)

public class EntityContext
    {
        public LocalEntities ObjectContext
        {
            get;
            private set;
        }

        public  EntityContext()
        {
            ObjectContext = new LocalEntities();
        }
    }

// How to Executing Async load of data from entity (serviceUsersRepository is ObjectContext driven entity)

            serviceUsersRepository = new ServiceUsersRepository();
            LoadOperation result = serviceUsersRepository.LoadQuery(() => serviceUsersRepository.GetServiceUsers(objSearchCriteria.SearchText));

            result.Completed += ((object sensexSx, QueryResultEventArgs ex) =>
            {
                this.AllServiceUsers = ex.Result as List<ServiceUser>;
            });
            result.Execute();

// example of serviceUsersRepository is described below (where ServiceUsers is entity generated by entity framework)

class ServiceUsersRepository : ObjectContext�
    {
        public ServiceUsersRepository()
        {

        }

        /// <summary>
        /// Returns a shallow-copied list of all Service Users in the repository.
        /// </summary>
        public List<ServiceUser> GetServiceUsers(string sText)
        {
                var result = from serviceuser in ObjectContext.ServiceUsers
                             select serviceuser;
                return result.ToList<ServiceUser>();
        }
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s