Data Access has been discontinued. Please refer to this page for more information.

How to: Manage One-to-Many Associations

Suppose, you have the SofiaCarRental model. The model includes two entities related with a one-to-many association. The Car entity has one instance of Category. You can use the Car.Category property to access the Category instance that is associated with an instance of the Car entity. The Category entity has many instances of Car. You can use the Category.Cars property to access the Car instances that are associated with an instance of the Category entity.

In this scenario, by default the association is not managed. Furthermore, if you add IsManaged to the mapping configuration of Car.Category only, it will have no effect. To change this, you need to mark the Category.Cars property as managed as well. For example:

categoryConfiguration.HasAssociation(x => x.Cars).
    HasFieldName("_cars").WithOpposite(x => x.Category).
    ToColumn("CategoryID").HasConstraint((y, x) =>  x.CategoryID == y.CategoryID ).
    IsManaged().WithDataAccessKind(DataAccessKind.ReadWrite);
carConfiguration.HasAssociation(x => x.Category).
    HasFieldName("_category").WithOpposite(x => x.Cars).
    ToColumn("CategoryID").HasConstraint((x, y) =>  x.CategoryID == y.CategoryID ).
    IsManaged().WithDataAccessKind(DataAccessKind.ReadWrite);
categoryConfiguration.HasAssociation(x => x.Cars).
    HasFieldName("_cars").WithOpposite(x => x.Category).
    ToColumn("CategoryID").HasConstraint((y, x) =>  x.CategoryID == y.CategoryID ).
    IsManaged().WithDataAccessKind(DataAccessKind.ReadWrite)
carConfiguration.HasAssociation(x => x.Category).
    HasFieldName("_category").WithOpposite(x => x.Cars).
    ToColumn("CategoryID").HasConstraint((x, y) =>  x.CategoryID == y.CategoryID ).
    IsManaged().WithDataAccessKind(DataAccessKind.ReadWrite)

Below you can find how to insert update and delete objects from domain entities related through a one-to-many association:

IsManaged notifies Telerik Data Access whether to populate the foreign key column of the child table in the database with the appropriate value when a new child object is added to the collection navigation property of a parent object. Since Car.Category is not a collection, IsManaged is disabled.

In case you need to insert a new category with a related car, you can do it like this:

  1. Set Category.Cars and Car.Category as managed by adding IsManaged in their configurations.
  2. Here is a complete code snippet showing you how to set a new relation by using the collection property. Note that it is recommended to add the new objects to the context first, and then set the relation through the collection property. The final step is to invoke the SaveChanges() method.

    using (FluentModel dbContext = new FluentModel())
    {
        Car newCar = new Car();
        newCar.Make = "Audi";
        newCar.Model = "A8";
        Category newCategory = new Category();
        newCategory.CategoryName = "MyCategory";
        dbContext.Add(newCategory);
        newCategory.Cars.Add(newCar);
        dbContext.SaveChanges();
    }
    
    Using dbContext As New FluentModel()
        Dim newCar As New Car()
        newCar.Make = "Audi"
        newCar.Model = "A8"
        Dim newCategory As New Category()
        newCategory.CategoryName = "MyCategory"
        dbContext.Add(newCategory)
        newCategory.Cars.Add(newCar)
        dbContext.SaveChanges()
    End Using
    

If you don't set the IsManaged property to True, you may persist two new records in the database, respectively in the Cars and Categories tables without relating them (the car.CategoryId column allows NULL values) or get an exception (the car.CategoryId column does not allow NULL values).

Note that in this case only the newCategory is added to the context and newCar is added to the navigation property. After the call to the SaveChanges() method both objects are saved in the database keeping the relationship between them. This is called persistence by reachability, which means that if you build a graph of related objects, adding the top most one to the context will cause the context to track the rest too.

For general information about inserting objects you can check the How to: Insert Objects article.

In case you need to delete a category from Categories (the parent table), you need to make sure that the corresponding objects in Cars is removed first. For example:

using (FluentModel dbContext = new FluentModel())
{
    Category someCategory = dbContext.Categories.FirstOrDefault();
    dbContext.Delete(someCategory.Cars);
    dbContext.Delete(someCategory);
    dbContext.SaveChanges();
}
Using dbContext As New FluentModel()
    Dim someCategory As Category = dbContext.Categories.FirstOrDefault()
    dbContext.Delete(someCategory.Cars)
    dbContext.Delete(someCategory)
    dbContext.SaveChanges()
End Using

If the cars are not removed prior to the removal of the category you will experience DataStoreException: The DELETE statement conflicted with the REFERENCE constraint.

In case you need to remove only the cars about a given category, you can pass someCategory.Cars to the Delete() method of the context and then call SaveChanges().

For general information about deleting objects you can check the How to: Delete Objects article.

In case you need to change the category a given car belongs to, you need to make sure that IsManaged is True for the Category.Cars navigation property and simply move the car from the collection of the first category to the collection of the second category. For example:

using (FluentModel dbContext = new FluentModel())
{
    Category someCategory = dbContext.Categories.
        FirstOrDefault(sc => sc.CategoryID == 1);
    Category otherCategory = dbContext.Categories.
        FirstOrDefault(sc => sc.CategoryID == 2);
    Car someCar = dbContext.Cars.
        FirstOrDefault(sc => sc.CategoryID == someCategory.CategoryID);
    someCategory.Cars.Remove(someCar);
    otherCategory.Cars.Add(someCar);
    dbContext.SaveChanges();
}
Using dbContext As New FluentModel()
    Dim someCategory As Category = dbContext.Categories. _
        FirstOrDefault(Function(sc) sc.CategoryID = 1)
    Dim otherCategory As Category = dbContext.Categories. _
        FirstOrDefault(Function(sc) sc.CategoryID = 2)
    Dim someCar As Car = dbContext.Cars. _
        FirstOrDefault(Function(sc) sc.CategoryID = someCategory.CategoryID)
    someCategory.Cars.Remove(someCar)
    otherCategory.Cars.Add(someCar)
    dbContext.SaveChanges()
End Using

Removing a car object from the collection of given category will not remove it from the database. It will only set car.CategoryID to NULL, if the column allows NULL values.

For general information about updating objects you can check the How to: Update Objects article.