Home / Community & Support / Knowledge Base / Telerik OpenAccess ORM / General / How to map a One to One relationship

How to map a One to One relationship

Article Info

Rating: Not rated

 

Article information

Article relates to

Telerik OpenAccess ORM

Created by

Ivailo Ivanov

Last modified

April 06, 2012

Last modified by

Pencho Popadiyn


DESCRIPTION

One To One relationships are frequently found in database models. Telerik OpenAccess ORM is prepared to map such models, but not in a straight forward way – there is no 1:1 association currently available in the Domain Model.

There are generally two approaches for implementing this mapping – using vertical inheritance or using an One to Many relationship while manually maintaining some synchronization.

In this Knowledge Base article we will describe how this mapping can be achieved in different scenarios and provide examples for the approaches available.


SOLUTIONS

Take for example the following MS SQL database:



Here we have two tables, CustomerMain and CustomerDetail. There is a foreign key, defined in CustomerDetail ID column, pointing to the CustomerMain ID column. Depending on the scenario, the ID of CustomerMain can be auto-calculated or manually inserted.

Mapping such a structure in a Domain Model can be achieved using one of the following approaches:

I. VERTICAL INHERITANCE (recommended)
With vertical inheritance, the data access will be straight forward – the association between the objects is maintained automatically and there is no need of manual code to support the approach.
In order to implement the inheritance, you need first to create the Domain Model and map it to a database. Depending on the scenario, you can create the model first or the database first.

A. In case of a forward mapping scenario you will have to implement a standard vertical inheritance between the two objects.

1) Create the the classes and the properties you need. In our example, we are going to have an integer primary key in the tables.
 

The ID of the parent object (CustomerMain) is set as a primary key (Identity = True) and the IdentityMechanism is “DatabaseServerCalculated”, meaning that Telerik OpenAccess ORM will expect the database server to generate the ID value during insert operations.

Note: The vertical inheritance approach is demonstrated with an auto-incremented identity, since this is the more complex case as a matter of relational mapping. When the primary key is manually maintained in the code, there will be no difference in the implementation.
 
2) Create an inheritance between the objects. 
 

 

The class representing the source table for the foreign key should be the child in the inheritance. In our example that is CustomerDetail.

3) Set the inheritance of the child class to Vertical
 
 

4) Delete the ID property of the child class (CustomerDetail) – it will implicitly use the ID of the parent class (CustomerMain). The final model should look this this:

 

5) Create data tables matching the classes

6) Save the Domain Model

7) Apply the Domain Model to the database. Note that a foreign key will be created for the CustomerDetail table, pointing to the CustomerMain.

8) Set the identity calculation of the CustomerMain ID column in the database

B. In case of a reverse mapping scenario, the approach is implemented in the following steps:

1) Update the relevant Domain Model classes from the database. By default, in case of One to One relationship, a One to Many association will be created in the model:

 

 

2) Delete the association

3) Add an inheritance between the classes, making sure that the class corresponding to the table that holds the foreign key is the child class.

4) Set the inheritance of the child class to Vertical

 


 
5) Set the Discriminator Value for the base class (CustomerMain) to {no}.


6) Delete the ID property of the child class (CustomerDetail) – it will implicitly use the ID of the parent class (CustomerMain)

 

 
 
7) Select the child class (CustomerDetail) and ensure that it is mapped to the corresponding table (in this example, to the CustomerDetails table)




8) Save the Domain Model

After the model is properly configured through the steps above, you are ready to begin using it. The relationship between the two classes will be maintained by Telerik OpenAccess ORM.

In order to manipulate your data, you should have in mind that because of the inheritance, the fields of the parent object (CustomerMain) will be visible and accessible through the child object (CustomerDetails), but not vice versa. When you are manipulating a CustomerDetails object, CustomerMain table will be implicitly referred to when necessary. However, when you are using the CustomerMain object, no automatic linkage will be available to the CustomerDetails object.

Here is a short demonstration of CRUD operations on CustomerDetails:

EntitiesModel model = new EntitiesModel();
 
// test the functionality through the child table
CustomerDetail newCustDetail = new CustomerDetail();
 
newCustDetail.Name = "Inheritance Customer - child";
newCustDetail.Type = "Type";
newCustDetail.Address = "Inheritance customer address";
newCustDetail.Phone = "Inheritance customer phone";
 
model.Add(newCustDetail);
model.SaveChanges();
 
// select the new customer detail
CustomerDetail custDetail = model.CustomerDetails.First(c => c.ID == newCustDetail.ID);
 
// test the update both in the parent and the child table through the child object
custDetail.Name = "New name";
custDetail.Address = "New address";
 
model.SaveChanges();
 
// select the new customer detail to check the updated fields
CustomerDetail custDetailUpdated = model.CustomerDetails.First(c => c.ID == custDetail.ID);
 
// delete the customer
model.Delete(custDetailUpdated);
model.SaveChanges();

And here are the CRUD operations for CustomerMain:

EntitiesModel model = new EntitiesModel();
 
// test the functionality through the parent table
CustomerMain newCustMain = new CustomerMain();
 
newCustMain.Name = "Inheritance Customer - parent";
newCustMain.Type = "Type";
 
model.Add(newCustMain);
model.SaveChanges();
 
// test the update
newCustMain.Type = "New Inheritance customer type";
 
model.SaveChanges();
 
// select the new customer main to check the updated field
CustomerMain custMainUpdated = model.CustomerMains.First();
 
// delete the customer
model.Delete(custMainUpdated);
model.SaveChanges();


II. ONE TO MANY RELATIONSHIP
Another approach to the One to One relationship mapping is to model the link as one to many. That would still make use of some automation from Telerik OpenAccess ORM, but as you will see below there will be some limitations that require custom code to be manually written in order to maintain the relation properly. For that reason this approach is not recommended and should be used only for the cases where the vertical inheritance cannot be utilized.

The main limitation of this modeling approach comes with the auto-generated identity fields. In case the foreign key field is auto-generated by the database, the operation for inserting a new pair of objects becomes more complex – it needs to be done in the following manner:
  1. Insert the object without a foreign key
  2. Retrieve the auto-generated primary key of the inserted row
  3. Insert the second object, the one with a foreign key, using the retrieved primary key of the first object

Those operations will not be performed by Telerik OpenAccess ORM automatically on SaveChanges() and should be coded by the user instead. For that reason we are going to present separate examples of the One to Many relationship approach – one for auto-generated primary key and one for manually maintained primary key.

A. Manually maintained primary key
For this scenario, consider that the primary key is GUID rather than an integer.
In case of reverse mapping, we will have the Domain Model generated by default without any intervention. As for the forward mapping, make sure the table with the foreign key constraint is the “one” in the One to Many relationship. The model will look like this:



In this case we have several things to take care of:

1) We need to make sure the primary keys are kept in sync. For simplicity I would suggest defining partial classes for both entities with an ID property:

public partial class CustomerDetail
{
public Guid CustomerId
{
get
{
return ID;
}
set
{
CustomerMain.ID = value;
ID = value;
}
}
}
 
     

2) The relationship needs to be maintained as One to One. A CustomerMain object should have only one CustomerDetail in its collection of CustomerDetails. One approach to help us maintain that is:

public partial class CustomerMain
{
public Guid CustomerId
{
get
{
return ID;
}
set
{
if (CustomerDetail != null)
CustomerDetail.ID = value;
ID = value;
}
}
public CustomerDetail CustomerDetail
{
get
{
if (CustomerDetails.Count > 0)
{
return CustomerDetails[0];
}
return null;
}
set
{
if (CustomerDetails.Count > 0)
CustomerDetails[0] = value;
else
CustomerDetails.Add(value);
}
}
}

 

3) If we are using the child object (CustomerDetails), the insert approach is straight forward as in the vertical inheritance. Unfortunately that is not the case while working with CustomerMain - we would have to maintain the insertion sequence manually in the following manner:

EntitiesModel model = new EntitiesModel();
// test the functionality through the child table
CustomerMain newCustMain = new CustomerMain();
newCustMain.Name = "One2Many customer - parent";
newCustMain.Type = "One2Many customer type";
newCustMain.CustomerId = new Guid(); // Create manually the ID
model.Add(newCustMain);
model.SaveChanges();
newCustMain.CustomerDetail = new CustomerDetail();
// Make sure the IDs are in sync
newCustMain.CustomerDetail.ID = newCustMain.CustomerId; // it cannot be achieved by the CustomerId, since the CustomerDetail was not yet instantiated
newCustMain.CustomerDetail.Address = "One2Many customer address";
model.SaveChanges();


4) If the cascading delete option is not enabled for the foreign key, the deletion of the object should be done separately, first for CustomerDetails and then for CustomerMain:

model.Delete(newCustMain.CustomerDetail);
model.SaveChanges();
model.Delete(newCustMain);
model.SaveChanges();


B. Auto-generated primary key
For this case let’s consider that the primary key is an auto-generated Int32 property.


 
The limitations of this approach are similar:

1) Keeping in sync the IDs of the object is important, but cannot be done with a property as in case A. Instead, after saving the parent object, we need to manually assign the same key to the child object


2) As for the case A, the relationship between the objects needs to be maintained as One to One. A CustomerMain object should have only one CustomerDetail in its collection of CustomerDetails. One approach to help us maintain that is:

public partial class CustomerDetail
{
public CustomerMain CustomerMain
{
get
{
if (CustomerMains.Count > 0)
{
return CustomerMains[0];
}
return null;
}
set
{
if (CustomerMains.Count > 0)
CustomerMains[0] = value;
else
CustomerMains.Add(value);
}
}
}
 

3) Both for CustomerDetail and for CustomerMain the insert operation should be maintained by calling SaveChanges() method for each of the objects:

EntitiesModel model = new EntitiesModel();
// test the functionality through the child table
CustomerMain newCustMain = new CustomerMain();
newCustMain.Name = "One2Many customer - parent";
newCustMain.Type = "One2Many customer type";
model.Add(newCustMain);
model.SaveChanges();
newCustMain.CustomerDetail = new CustomerDetail();
newCustMain.CustomerDetail.ID = newCustMain.ID;
newCustMain.CustomerDetail.Address = "One2Many customer address";
model.SaveChanges();

4) As in case A, if the foreign key is without cascade delete option, the delete operation should be performed first for the CustomerMain and then for CustomerDetail:

model.Delete(newCustMain.CustomerDetail);
model.SaveChanges();
model.Delete(newCustMain);
model.SaveChanges();



Find below the demo C# and VB projects demonstrating all the described scenarios.


SUMMARY

One to One relationship is not directly supported in the Visual Designer, but can be modeled with Telerik OpenAccess ORM in two different ways depending on the scenario. The one we recommend is through vertical inheritance, which requires the least manual efforts for maintaining the relationship. If inheritance is not suitable, you can also model the relation as One to Many and fill the gaps the approach introduces through some additional coding.

Comments

If you'd like to comment on this KB article, please, send us a Support Ticket.
Thank you!

Please Sign In to rate this article.