Telerik OpenAccess Classic

Telerik OpenAccess ORM Send comments on this topic.
How to: Implement an OpenAcess data provider for RadScheduler(web)
Programmer's Guide > OpenAccess ORM Classic (Old API) > OpenAccess Tasks > Integration and Installation > How to: Implement an OpenAcess data provider for RadScheduler(web)

Glossary Item Box

This documentation article is a legacy resource describing the functionality of the deprecated OpenAccess Classic only. The contemporary documentation of Telerik OpenAccess ORM is available here.

Custom data providers are very powerful feature when used with RadScheduler. These kind of providers are explained in this section of RadScheduler’s documentation.

This article will present how to implement a data provider based on OpenAccess.
For the purpose of the example the forward-mapping approach is used with OpenAccess and the sample class-model looks like this:

 

There is a MyAppointment class which has all the fields that are obligatory for an appointment in RadScheduler (idAppointment, description, startDate, endDate, reccurenceRule, parrentId) and “address” and “contact” which are treated as resource types by the RadScheduler.

NOTE :The name of the class MyAppointment is chosen to avoid type resolution with the Appointment class from the Telerik.Web.UI namespace which is required by the RadScheduler and is commonly used in the later sections of this example.

To make it more clear a base class called OpenAccessProviderBase is created where  the object scope that will be used in all inherited classes is declared and initialized.
This base class inherits the SchedulerProviderBase which contains the methods and resources required  for a RadScheduler data provider.

C# Copy Code
public abstract class OpenAccessProviderBase : SchedulerProviderBase
   {
       
private IObjectScope scope;

       
protected IObjectScope Scope
       {
           get {
return scope; }
           set { scope = value; }
       }

       
protected OpenAccessProviderBase()
       {
       }
       
       
public override void Initialize(string name, NameValueCollection config)
       {
           
if (config == null)
           {
               
throw new ArgumentNullException("config");
           }
           
if (string.IsNullOrEmpty(name))
           {
               name =
"OpenAccessProvider";
           }
           
if (scope == null)
           {
               scope = OpenAccessProvider.ObjectScopeProvider1.GetNewObjectScope();
           }

           
base.Initialize(name, config);
       }
   }  
VB .NET Copy Code
Public MustInherit Class OpenAccessProviderBase
    Inherits SchedulerProviderBase
    Private _scope As IObjectScope
    Protected Property Scope() As IObjectScope
        Get
            Return _scope
        End Get
        Set(ByVal value As IObjectScope)
            _scope = value
        End Set
    End Property
    Protected Sub New()
    End Sub
    Public Overloads Overrides Sub Initialize(ByVal name As String, ByVal config As NameValueCollection)
        If config Is Nothing Then
            Throw New ArgumentNullException("config")
        End If
        If String.IsNullOrEmpty(name) Then
            name = "OpenAccessProvider"
        End If
        If _scope Is Nothing Then
            Scope = Descor.fPlanner.ObjectScopeProvider1.GetNewObjectScope()
        End If
        MyBase.Initialize(name, config)
    End Sub
End Class

 

Next step is to create the OpenAccessCustomProvider class that will be used as the main class for the provider.

Because both SchedulerProviderBase and OpenAccessProviderBase are abstract classes, the new provider class must implement for the abstract methods GetAppointments, Insert, Update, Delete, GetResourceTypes, and GetResourcesByType.

Before one starts implementing the methods that deal with inserting, deleting, updating and loading appointments the developer should implement the GetResourceTypes and GetResourcesByType methods which will allow usage of custom resources as Contact and Address.

C# Copy Code
public override IEnumerable<ResourceType> GetResourceTypes(RadScheduler owner)
{
   ResourceType[] resourceTypes =
new ResourceType[2];
   resourceTypes[0] =
new ResourceType("Address", false);
   resourceTypes[1] =
new ResourceType("Contact", false);

   
return resourceTypes;
}  
VB .NET Copy Code
    Public Overloads Overrides Function GetResourceTypes(ByVal owner As RadScheduler) As IEnumerable(Of ResourceType)
        'The lenght of the resource type is changed from 2 to 1 because unlike in C# in vb the
        'the lenght of a collection equals the last index of it
        'That was the reason for the exception
        Dim resourceTypes As ResourceType() = New ResourceType(1) {}
        resourceTypes(0) = New ResourceType("Address", False)
        resourceTypes(1) = New ResourceType("Contact", False)
        Return resourceTypes
    End Function

The second parameter in the ResourceType constructor points whether there is a requirement to insert more than one instance from the given resource for one appointment. That is not created in this project as there is only one location and contact for an appointment.

For the GetResourcesByType implementation two helper methods are implemented for the two types of resources “Address” and “Contact”. These helper methods load all the instances of the specified type from the database and then are manually “converted” to the Resource type which is recognized by the RadScheduler.

C# Copy Code
private List<Resource> GetContacts()
   {
       List<Resource> contactResources =
new List<Resource>();

       var contacts = from c
in Scope.Extent<Contact>()
                      select c;

       
foreach (Contact cont in contacts)
       {
           Resource newResource =
new Resource();
           newResource.Type =
"Contact";
           newResource.Key = cont.IdContact;
           newResource.Text = cont.Name;
           newResource.Attributes[
"Telephone"] = cont.Telephone;
           contactResources.Add(newResource);
       }
       
return contactResources;
   }

   
private List<Resource> GetAddresses()
   {
       List<Resource> addressResources =
new List<Resource>();

       var addresses = from a
in Scope.Extent<Address>()
                      select a;

       
foreach (Address add in addresses)
       {
           Resource newResource =
new Resource();
           newResource.Type =
"Address";
           newResource.Key = add.IdAddress;
           newResource.Text = add.LocationAddress;
           newResource.Attributes[
"City"] = add.City;
           newResource.Attributes[
"Country"] = add.Country;
           addressResources.Add(newResource);
       }
       
return addressResources;
   }
VB .NET Copy Code
    Private Function GetContacts() As List(Of Resource)
        Dim contactResources As New List(Of Resource)()
        Dim contacts = From c In Scope.Extent(Of Contact)() _
         Select c
        For Each cont As Contact In contacts
            Dim newResource As New Resource()
            newResource.Type = "Contact"
            newResource.Key = cont.IdContact
            newResource.Text = cont.VerboseDescription
            newResource.Attributes("Telephone") = cont.Telephone
            contactResources.Add(newResource)
        Next
        Return contactResources
    End Function
    Private Function GetAddresses() As List(Of Resource)
        Dim addressResources As New List(Of Resource)()
        Dim addresses = From a In Scope.Extent(Of Address)() _
         Select a
        For Each add As Address In addresses
            Dim newResource As New Resource()
            newResource.Type = "Address"
            newResource.Key = add.IdAddress
            newResource.Text = add.LocationAddress
            newResource.Attributes("City") = add.City
            newResource.Attributes("Country") = add.Country
            addressResources.Add(newResource)
        Next
        Return addressResources
    End Function

Here is the implementation of the GetResourcesByType:

C# Copy Code
public override IEnumerable<Resource> GetResourcesByType(RadScheduler owner, string resourceType)
   {
       
switch (resourceType)
       {
           
case ("Contact"):
               {
                   
return GetContacts();
               }
           
case ("Address"):
               {
                   
return GetAddresses();
               }
           
default:
               
throw new InvalidOperationException("Unknown resource type: " + resourceType);
       }
   }
VB .NET Copy Code
    Public Overloads Overrides Function GetResourcesByType(ByVal owner As RadScheduler, ByVal resourceType As String) As IEnumerable(Of Resource)
        Select Case resourceType
            Case "Contact"
                If True Then
                    Return GetContacts()
                End If
            Case "Address"
                If True Then
                    Return GetAddresses()
                End If
            Case Else
                Throw New InvalidOperationException("Unknown resource type: " + resourceType)
        End Select
        Return Nothing
    End Function

After these steps are accomplished the methods that take care of the CRUD operations required by the provider can be implemented. First there is the GetAppointments() method. It should return a collection of all the appointments to the RadScheduler, so all the appointments from the database need to be extracted their properties assigned to instances of the Telerik.Web.UI.Appointment class which the RadScheduler expects.

C# Copy Code
public override IEnumerable<Appointment> GetAppointments(RadScheduler owner)
   {
       List<Appointment> appList =
new List<Appointment>();
       var myAppointments = from ma
in Scope.Extent<MyAppointment>()
                            select ma;
       
foreach (MyAppointment myApp in myAppointments)
       {
           Appointment newApponitment =
new Appointment();
           newApponitment.Owner = owner;
           newApponitment.ID = myApp.IdAppointment;
           newApponitment.Subject = myApp.Description;
           newApponitment.Start = myApp.StartDate;
           newApponitment.End = myApp.EndDate;
           newApponitment.RecurrenceRule = myApp.ReccurenceRule;
           newApponitment.RecurrenceParentID = myApp.ParrentId;
           
if (newApponitment.RecurrenceParentID != null)
           {
               newApponitment.RecurrenceState = RecurrenceState.Exception;
           }
           
else
           {
               
if (newApponitment.RecurrenceRule != string.Empty)
               {
                   newApponitment.RecurrenceState = RecurrenceState.Master;
               }
           }
           LoadResources(newApponitment, myApp);
           appList.Add(newApponitment);
       }
       
return appList;
   }
VB .NET Copy Code
Public Overloads Overrides Function GetAppointments(ByVal owner As RadScheduler) As IEnumerable(Of Appointment)
        Dim appList As New List(Of Appointment)()
        Dim myAppointments = From ma In Scope.Extent(Of fAppointment)() _
         Select ma
        For Each myApp As fAppointment In myAppointments
            Dim newAppointment As New Appointment()
            newAppointment.Owner = owner
            newAppointment.ID = myApp.IdAppointment
            newAppointment.Subject = myApp.Description
            newAppointment.Start = myApp.StartDate
            newAppointment.[End] = myApp.EndDate
            newAppointment.RecurrenceRule = myApp.RecurrenceRule
            newAppointment.RecurrenceParentID = myApp.RecurrencePID
            If newAppointment.RecurrenceParentID <> Nothing Then
                newAppointment.RecurrenceState = RecurrenceState.Exception
            Else
                If newAppointment.RecurrenceRule <> String.Empty Then
                    newAppointment.RecurrenceState = RecurrenceState.Master
                End If
            End If
            LoadResources(newAppointment, myApp)
            appList.Add(newAppointment)
        Next
        Return appList
    End Function

Before  adding an Appointment instance to the result collection a helper method is called which loads the “Address” and “Contact” resources for the given appointment. Here is the body of the LoadResources() method:

C# Copy Code
private void LoadResources(Appointment apt, MyAppointment myApt)
   {
       
if (myApt.Address != null)
       {
           Resource address = apt.Owner.Resources.GetResource(
"Address", myApt.Address.IdAddress);
           apt.Resources.Add(address);
       }
       
if (myApt.Contact != null)
       {
           Resource contact = apt.Owner.Resources.GetResource(
"Contact", myApt.Contact.IdContact);
           apt.Resources.Add(contact);
       }
   }  
VB .NET Copy Code
    Private Sub LoadResources(ByVal apt As Appointment, ByVal myApt As fAppointment)
        If myApt.AppointmentAddress IsNot Nothing Then
            Dim address As Resource = apt.Owner.Resources.GetResource("Address", myApt.AppointmentAddress.IdAddress)
            apt.Resources.Add(address)
        End If
        If myApt.AppointmentContact IsNot Nothing Then
            Dim contact As Resource = apt.Owner.Resources.GetResource("Contact", myApt.AppointmentContact.IdContact)
            apt.Resources.Add(contact)
        End If
    End Sub
The Insert method in the provider has the following implementation:

 

C# Copy Code
public override void Insert(RadScheduler owner, Appointment appointmentToInsert)
   {
       MyAppointment app = PopulateMyAppointmentInstance(appointmentToInsert);
       app.Contact = FindContactByResourceKey(appointmentToInsert);
       app.Address = FindAddressByResourceKey(appointmentToInsert);
       Scope.Transaction.Begin();
       Scope.Add(app);
       Scope.Transaction.Commit();
       
   }
VB .NET Copy Code
    Public Overrides Sub Insert(ByVal owner As Telerik.Web.UI.RadScheduler, ByVal appointmentToInsert As Telerik.Web.UI.Appointment)
        Dim app As New fAppointment
        Populate_fAppointmentInstance(appointmentToInsert, app)
        app.AppointmentContact = FindContactByResourceKey(appointmentToInsert)
        app.AppointmentAddress = FindAddressByResourceKey(appointmentToInsert)
        Scope.Transaction.Begin()
        Scope.Add(app)
        Scope.Transaction.Commit()
    End Sub

 Additional helper methods in this scenario are used because the fields of the new appointment to be inserted by OpenAccess should be populated with the values entered for the Appointment in the RadScheduler UI.

FindContactByResourceKey() and FindAddressByResourceKey() methods are also used because the RadScheduler gives access to the id and properties of the resources selected in the insert/update wizard but they need to physically be extracted from the database and assigned to the new MyAppointment instance that one wants to persist.

Here is the code for these helper methods:

C# Copy Code
private void PopulateMyAppointmentInstance(Appointment app, MyAppointment myapp)
   {
       myapp.StartDate = app.Start;
       myapp.EndDate = app.End;
       myapp.Description = app.Subject;
       myapp.ReccurenceRule = app.RecurrenceRule;
       myapp.ParrentId = (
int?)app.RecurrenceParentID;
   }

   
private Contact FindContactByResourceKey(Appointment app)
   {
       
if (app.Resources.GetResourceByType("Contact")!= null)
       {
           
int contactID = (int)app.Resources.GetResourceByType("Contact").Key;

           Contact cont = (from c
in Scope.Extent<Contact>()
                           where c.IdContact == contactID
                           select c).FirstOrDefault();
           
return cont;
       }
       
return null;
   }

   
private Address FindAddressByResourceKey(Appointment app)
   {
       
if (app.Resources.GetResourceByType("Address")!= null)
       {
           
int addressID = (int)app.Resources.GetResourceByType("Address").Key;
           Address add = (from a
in Scope.Extent<Address>()
                          where a.IdAddress == addressID
                          select a).FirstOrDefault();
           
return add;
       }
       
return null;
   }  
VB .NET Copy Code
    Private Sub Populate_fAppointmentInstance(ByVal app As Appointment, ByVal myapp As fAppointment)
        myapp.StartDate = app.Start
        myapp.EndDate = app.[End]
        myapp.Description = app.Subject
        myapp.RecurrenceRule = app.RecurrenceRule
        myapp.RecurrencePID = DirectCast(app.RecurrenceParentID, System.Nullable(Of Integer))
    End Sub
    Private Function FindContactByResourceKey(ByVal app As Appointment) As Contact
        If app.Resources.GetResourceByType("Contact") <> Nothing Then
            Dim contactID As Integer = DirectCast(app.Resources.GetResourceByType("Contact").Key, Integer)
            Dim cont As Contact = (From c In Scope.Extent(Of Contact)() _
             Where c.IdContact = contactID _
             Select c).FirstOrDefault()
            Return cont
        End If
        Return Nothing
    End Function
    Private Function FindAddressByResourceKey(ByVal app As Appointment) As Address
        If app.Resources.GetResourceByType("Address") <> Nothing Then
            Dim addressID As Integer = DirectCast(app.Resources.GetResourceByType("Address").Key, Integer)
            Dim add As Address = (From a In Scope.Extent(Of Address)() _
             Where a.IdAddress = addressID _
             Select a).FirstOrDefault()
            Return add
        End If
        Return Nothing
    End Function

The Update method’s body is very similar to the one of the Insert method only that additional helper method is called that will load the MyAppointment instance which should be updated in the database:

C# Copy Code
public override void Update(RadScheduler owner, Appointment appointmentToUpdate)
   {
       MyAppointment app = LoadMyAppointmentByID(appointmentToUpdate);
       Scope.Transaction.Begin();
       PopulateMyAppointmentInstance(appointmentToUpdate, app);
       app.Contact = FindContactByResourceKey(appointmentToUpdate);
       app.Address = FindAddressByResourceKey(appointmentToUpdate);
       Scope.Transaction.Commit();
   }
VB .NET Copy Code
    Public Overrides Sub Update(ByVal owner As Telerik.Web.UI.RadScheduler, ByVal appointmentToUpdate As Telerik.Web.UI.Appointment)
        Dim app As fAppointment = LoadMyAppointmentByID(appointmentToUpdate)
        Scope.Transaction.Begin()
        Populate_fAppointmentInstance(appointmentToUpdate, app)
        app.AppointmentContact = FindContactByResourceKey(appointmentToUpdate)
        app.AppointmentAddress = FindAddressByResourceKey(appointmentToUpdate)
        Scope.Transaction.Commit()
    End Sub
This is the implementation of the LoadMyAppointmentByID() helper which is also used in the Delete method of the provider:
C# Copy Code
private MyAppointment LoadMyAppointmentByID(Appointment app)
   {
       
int id = (int)app.ID;
       MyAppointment myApp = (from ma
in Scope.Extent<MyAppointment>()
                              where ma.IdAppointment == id
                              select ma).FirstOrDefault();
       
return myApp;
   }
VB .NET Copy Code
    Private Function LoadMyAppointmentByID(ByVal app As Appointment) As fAppointment
        Dim id As Integer = DirectCast(app.ID, Integer)
        Dim myApp As fAppointment = (From ma In Scope.Extent(Of fAppointment)() _
         Where ma.IdAppointment = id _
         Select ma).FirstOrDefault()
        Return myApp
    End Function
The Delete method comes last for implementation:
C# Copy Code
public override void Delete(RadScheduler owner,Appointment appointmentToDelete)
   {
       MyAppointment app = LoadMyAppointmentByID(appointmentToDelete);
       Scope.Transaction.Begin();
       Scope.Remove(app);
       Scope.Transaction.Commit();
   }
VB .NET Copy Code
    Public Overrides Sub Delete(ByVal owner As Telerik.Web.UI.RadScheduler, ByVal appointmentToDelete As Telerik.Web.UI.Appointment)
        Dim app As fAppointment = LoadMyAppointmentByID(appointmentToDelete)
        Scope.Transaction.Begin()
        Scope.Remove(app)
        Scope.Transaction.Commit()
    End Sub
Since the OpenAccessCustomProvider class is ready it should be registered in the web.config file and then it can be used in any web page.
Locate the “configSections” tag which is in the top of the web.config and add a section group for the RadScheduler inside it:
web.config Copy Code
<sectionGroup name="telerik.web.ui">
     
<section name="radScheduler"
              
type="Telerik.Web.UI.RadSchedulerConfigurationSection,
                  
Telerik.Web.UI, PublicKeyToken=121fae78165ba3d4"
              
allowDefinition="MachineToApplication"
              
requirePermission="false"/>
   
</sectionGroup>

After the handler is declared there should be added a <telerik.web.ui> section in the web.config where the provider should be declared:

web.config Copy Code
<telerik.web.ui>
   
<radScheduler defaultAppointmentProvider="Integrated">
     
<appointmentProviders>
       
<add name="OpenAccessProvider" type="OpenAccessCustomProvider"/>
     
</appointmentProviders>
   
</radScheduler>
 
</telerik.web.ui>  

Now the provider is ready for use and it can easily be taken advantage of by dragging a RadScheduler control into a web page and set its “ProviderName” property to the name of the just created provider as registered  in the configuration file. That is “OpenAccessProvider” in our example.

web.config Copy Code
<telerik:RadScheduler ID="RadScheduler1" ProviderName="OpenAccessProvider" runat="server">
</telerik:RadScheduler>