How To: Improve Performance by Using the Instant Feedback Mode

  • 6 minutes to read

Overview

The Instant Feedback mode is a feature of the Grid control that improves the responsiveness of its UI and reduces the amount of data requested from a data source. There are limitations to the Instant Feedback mode and that is why it is not enabled by default.

Every scaffolded application already has the required code to enable the Instant Feedback mode. Initially, generated CollectionViewModels inherit from the base CollectionViewModel class. To enable the Instant Feedback mode, change the base class to InstantFeedbackCollectionViewModel. The API of this class is very similar to that of the CollectionViewModel and in many cases a InstantFeedbackCollectionViewModel can be used as a drop-in replacement.

For example, if you have a generated CollectionViewModel for the Order entity:

public partial class OrderCollectionViewModel : CollectionViewModel<Order, long, IDevAVDbUnitOfWork> {
        public static OrderCollectionViewModel Create(IUnitOfWorkFactory<IDevAVDbUnitOfWork> unitOfWorkFactory = null) {
            return ViewModelSource.Create(() => new OrderCollectionViewModel(unitOfWorkFactory));
        }

        protected OrderCollectionViewModel(IUnitOfWorkFactory<IDevAVDbUnitOfWork> unitOfWorkFactory = null)
            : base(unitOfWorkFactory ?? UnitOfWorkSource.GetUnitOfWorkFactory(), x => x.Orders)) {
        }
}

To enable the Instant Feedback mode, change the base class to InstantFeedbackCollectionViewModel<Order, long, IDevAVDbUnitOfWork>.

There is also a second variant of the InstantFeedbackCollectionViewModel class with four type parameters. Use it when you need a projection function (more on projections below).

The Instant Feedback mode inside detail views

It is also possible to use InstantFeedbackCollectionViewModel on a detail view if the collection of associated entities is big enough to benefit from the Instant Feedback mode. For example, imagine we have a collection view model for the Department entity, which in turn contains a collection of the Course entities. An example of the collection property in the corresponding DepartmentViewModel is shown in the code snipped below.

public partial class DepartmentViewModel : SingleObjectViewModel<Department, int, IDepartmentContextUnitOfWork> {
//...
        public CollectionViewModel<Course, int, IDepartmentContextUnitOfWork> DepartmentCoursesDetails {
            get { return GetDetailsCollectionViewModel((DepartmentViewModel x) => x.DepartmentCoursesDetails, x => x.Courses, x => x.DepartmentID, (x, key) => x.DepartmentID = key); }
        }
}

To enable the Instant Feedback mode in the Grid that displays the Course collection, replace the property type with the InstantFeedbackCollectionViewModel and also replace the GetDetailsCollectionViewModel invocation with the GetDetailsInstantFeedbackCollectionViewModel. Everything else stays the same. Now, the view model should look as follows.

public partial class DepartmentViewModel : SingleObjectViewModel<Department, int, IDepartmentContextUnitOfWork> {
//...
    public InstantFeedbackCollectionViewModel<Course, int, IDepartmentContextUnitOfWork> DepartmentCoursesDetails {
        get { return GetDetailsInstantFeedbackCollectionViewModel((DepartmentViewModel x) => x.DepartmentCoursesDetails, x => x.Courses, x => x.DepartmentID, (x, key) => x.DepartmentID = key); }
    }
}

Extended Data Query and WCF Data Services

Changing the base class is enough to enable the Instant Feedback mode. But in the case of WCF Data Services, the performance of complex operations such as grouping, calculating row summaries and sorting, can be less than desirable for a large number of rows. This inefficiency is caused by limitations of the WCF Data Services query language, which forces the Grid control to issue excessive requests and to fetch more data than necessary.

The extended data query mechanism is used to boost performance and decrease the server load. Its implementation is split between client and server. For the server to support the extended data query, a special method for each table needs to be implemented.

Once the WCF service has access to all the necessary methods, the extended data query on the client side can be enabled.

The scaffolding wizard generates a UnitOfWork class for the selected DataServiceContext. This class is used to create IRepository instances for each entity.

A typical UnitOfWork class looks like this:

public class NorthwindEntitiesUnitOfWork : DbUnitOfWork<NorthwindEntities>, INorthwindEntitiesUnitOfWork {
//…
IRepository<Customer, string> INorthwindEntitiesUnitOfWork.Customers {
get { return GetRepository(x => x.Customers, x => x.CustomerID); }
}

IRepository<Employee, int> INorthwindEntitiesUnitOfWork.Employees {
get { return GetRepository(x => x.Employees, x => x.EmployeeID); }
}
    //…
}

The extended data query doesn't have to be enabled for all entities at once. To enable it for the Customer entity, pass an additional Boolean argument to the corresponding GetRepository invocation.

IRepository<Customer, string> INorthwindEntitiesUnitOfWork.Customers {
    get { return GetRepository(x => x.Customers, x => x.CustomerID, useExtendedDataQuery: true); }
}

Projections

To further improve performance and reduce the server load, consider using projections. Projections can help to reduce the amount of data transferred from the server by specifying which properties should be loaded instead of loading the whole entity.

The code below demonstrated the use of a projection, which eliminates an unnecessary lazy request for CustomerName.

public class OrderInfo {
        public long Id { get; set; }
        public long InvoiceNumber { get; set; }
        public string CustomerName { get; set; }
}
public partial class OrderCollectionViewModel : InstantFeedbackCollectionViewModel<Order, OrderInfo, long, IDevAVDbUnitOfWork> {
        public static OrderCollectionViewModel Create(IUnitOfWorkFactory<IDevAVDbUnitOfWork> unitOfWorkFactory = null) {
            return ViewModelSource.Create(() => new OrderCollectionViewModel(unitOfWorkFactory));
        }
        protected OrderCollectionViewModel(IUnitOfWorkFactory<IDevAVDbUnitOfWork> unitOfWorkFactory = null)
            : base(unitOfWorkFactory ?? UnitOfWorkSource.GetUnitOfWorkFactory(), x => x.Orders, OrderInfoProjection) {
        }
        static IQueryable<OrderInfo> OrderInfoProjection(IRepositoryQuery<Order> query) {
            return query.Select(order => new OrderInfo {
                Id = order.Id,
                InvoiceNumber = order.InvoiceNumber,
                CustomerName = order.Customer.Name
            });
    }
}

Without the projection, you would have to issue a request to the server each time you needed to know the CourseCount. For example, for a Grid with N entries, the client would generate N additional requests.

The Instant Feedback mode for the EntityFramework supports all of these operations, while WCF Data Services are limited to the Include() and Where() operations.

More information on projections can be found in the How to: Use Projections to Create Custom Queries and Optimize Performance tutorial.