This blog post could be helpful for those who seek a way to store and retrieve all appointments information like Resources, TimeMarkers, Categories. It also provides a handy way to handle recurrence exceptions without any additional code.
1. Database design (This step can be avoided if a DataModel is initially created and then a database is generated from it).
The key points that need to be taken into consideration when planning the database are:
Here is the database schema used in the described sample:
As shown above, the SqlAppointments table is used to store all information about appointments – including exceptions whenever the appointment is recurrent. This is achieved by a self reference implemented by the relationship ParentId -> Id, i.e. a single SqlAppointment can have many child appointments (exceptions). This means that if we want to save a recurrent appointment with 3 exceptions, it will “cost” 4 records in our database – one for the Master appointment and 3 more for the exceptions with their ParentId field set to the value of the Master.Id. This is quite convenient since we do not have to deal with any kind of serialization when we need to store a recurring appointment with exceptions.
2. Create the ADO.NET Entity Data Model classes using the database in the Web project that will host our Silverlight client. All the tables existing in the database are needed for that:
3. Create a Domain Service class using the data model, which will generate code for client interaction with the database. Make sure you check all entities and enable them for edit:
After the domain service classes are created, we will need to do some changes in order to ensure that all associated data (coming from relationships) will be loaded along with each requested entity. In our case when we load all resources, we need all of the resource types as well. However, because they come from a separate table, we will need to add the Include attribute to the Resource metadata class:
[Include]
public ResourceType ResourceType { get; set; }
Then change the query for getting the Resources to include the dependent entities in the query results:
public IQueryable<Resource> GetResources()
{
return this.ObjectContext.Resources.Include("ResourceType");
}
In the same manner, do the same for SqlAppointments (in the metadata class):
[Include]
public EntityCollection<AppointmentResource> AppointmentResources { get; set; }
…and in the get query:
public IQueryable<SqlAppointment> GetSqlAppointments()
{
return this.ObjectContext.SqlAppointments.Include("AppointmentResources");
}
Thus when we get all appointments, we will have all their related resources.
4. Next, we move to the client. Basically, the logic needed for it to interact with the database can be summarized in several steps:
private void LoadResources(Action completedCallback)
{
var loadResourcesOperation = domainContext.Load<Resource>(domainContext.GetResourcesQuery());
loadResourcesOperation.Completed += (sender, args) =>
{
// populate scheduler.ResourceTypes collection with all ResourceTypes and Resources,
// then call the completedCallback which should load the appointments
};
}
private void LoadAppointments()
{
var appointmentsLoadOperation = this.domainContext.Load<SqlAppointment>(this.domainContext.GetSqlAppointmentsQuery());
appointmentsLoadOperation.Completed += (s, a) =>
{
// create IAppointment objects and populate RadScheduler with them
};
}
Adding:
void OnSchedulerAppointmentAdded(object sender, AppointmentAddedEventArgs e)
{
var addedAppointment = e.Appointment as Appointment;
// use the added appointment's properties to create new appointment entity
// and add it to the domain context
}
Editing:
void OnSchedulerAppointmentEdited(object sender, AppointmentEditedEventArgs e)
{
Appointment editedAppointment;
if (e.ExceptionOccurrence == null)
{
editedAppointment = e.Appointment as Appointment;
}
else
{
editedAppointment = e.ExceptionOccurrence.Master as Appointment;
}
// use the edited appointment's Id to catch the right appointment entity to edit in the db
// use the e.ExceptionAction property to determine how a recurrent appointment is being edited
}
Deleting:
void OnSchedulerAppointmentDeleted(object sender, AppointmentDeletedEventArgs e)
{
var deletedAppointment = e.Appointment as Appointment;
// get the appointment entity which is to be deleted using the deletedAppointment.Id
// first delete its appointment exceptions (if recurrent) and then itself
}
Please find the concrete implementations of these steps in the solution attached in the related KB article:
Also, find related information in our help section and in Evan Hutnick’s learning series for RadScheduler
And below is how it looks like:
Rossitza Fakalieva is a Technical Manager, Microsoft MVP in Developer Technologies and a Director of the Bulgarian chapter of the global Women Who Code organization. She previously worked on the Telerik engineering team and defines herself as .NET enthusiast. She loves to empower others to grow in their career and in the tech field—by teaching, by delivering courses and presentations, and as part of her daily job.