Although they are more difficult to implement, custom providers are more powerful than using data sources because you
have more control over every detail of data access operations. Only custom providers can provide the support to allow
multi-valued resources (resources that can have multiple values assigned to a single appointment).
The provider is instantiated once per application domain and is shared across threads.
RadScheduler ensures basic thread safety by encapsulating each provider in a wrapper
that provides locks around each of its public methods. However, you should take care of synchronizing access to
instance field members where appropriate.
This example walks through the steps required to build a custom database-driven provider that supports
multi-valued resources. The complete source code of the sample provider can be found in the App_Code directory of
the Quick Start Framework.(C:\Program Files (x86)\Telerik\RadControls for ASP.NET AJAX Q3 2011\Live Demos\App_Code\Scheduler))
This example uses a database containing student classes, teachers and students. Each class has one
teacher and multiple students. Any student can attend any class. The complete schema for the database is shown below:
Each class is treated by the scheduler as an appointment. Teachers and students are treated as resource types.
Note that we keep a cross-link table, ClassStudents, to store the many-to-many relationships
between classes and students.
The base class
To make the implementation of database-driven RadScheduler providers easier, Telerik.Web.UI includes an abstract
base class - DbSchedulerProviderBase. This base class takes care of common configuration and
initialization tasks. When initialization is complete, you can use the DbFactory object or the shortcut methods to create
connections, parameters, etc. An outline of DbSchedulerProviderBase is shown below:
Implementing the provider
Declare the provider class, inheriting from DbSchedulerProviderBase:
Both SchedulerProviderBase and DbSchedulerProviderBase are abstract classes.
The new provider class must provide an implementation for the abstract methods GetAppointments,
Insert, Update, Delete,
GetResourceTypes, and GetResourcesByType.
Before implementing the methods that deal with appointments, lay the groundwork by implementing the
support for custom resources. The first step is to implement GetResourceTypes to supply the
scheduler with a list of available resource types. GetResourceTypes returns only basic information:
the name of the resource and a boolean value indicating whether the provider supports multiple resource values for that type.
Because this provider supports multiple values of the Student resource, we indicate that now:
Implement the GetResourcesByType method to supply the scheduler with a list of possible values
given a resource type. This requires a query to the database to obtain the list of values. Once retrieved,
the resources are cached. The base class, DbSchedulerProviderBase, provides the infrastructure for querying the database:
The connection is established by calling OpenConnection() and database commands are
created using the DbFactory object:
While we are working with resources, create a private helper method to read the resources for an appointment
and assign them to the appointment object. This method will be useful when the provider reads appointments from the
database. Note that the resource objects are read from cache.
Provide the implementation for GetAppointments to supply the scheduler
with a list of all the appointments in the database. Note that this assigns an owner and a class ID
before calling LoadResources to load the resources for the appointment:
Note that this method reads UTC dates from the database. To make this clear to RadScheduler it calls
DateTime.SpecifyKind(). You should store dates in UTC format to ensure proper handling of
This method gets a reference to the scheduler as a parameter (owner). You can use the RadScheduler properties to optimize your
query. For example, the VisibleRangeStart and VisibleRangeEnd properties can be
used to limit the number of records that the query retrieves. Recurring appointments are evaluated in-memory, however, so
they should be always retrieved regardless of VisibleRangeStart and VisibleRangeEnd.
Before proceeding to the Insert, Update, and Delete
commands, the provider needs a few more helper functions. Because the provider is supporting multiple
students for each class, it needs helper functions to add and delete these many-to-many relationships in the
cross-link table (ClassStudents):
To simplify creating parameters in the Insert and Update methods, add another helper function:
Inserting appointments is a bit complicated as you need to retrieve the identity value. A stored procedure might be of help here.
However, in order to target both MS SQL Server and MS Access, the provider uses normal queries. The Insert
method breaks abstraction for the sake of data integrity: MS SQL Server provides the SCOPE_IDENTITY() function to retrieve the
identity value of the current transaction, unlike @@IDENTITY that is a global identity value. After inserting the new class and
obtaining its identity value, the identity value is passed to the FillClassStudents method, to create the
many-to-many relationship between classes and students. The Insert method works in a transaction to ensure data integrity.
The most challenging part of the update operation is to manage the many-to-many relationship. The provider needs to clear the cross-link
table entries for the appointment and recreate them from scratch:
The Delete method executes two queries: one to delete the entries for the appointment in the cross-link
table and another to delete the appointment itself.