Referenced Relationships
referenced relationships require a bit of special handling. a one-to-one relationship is defined using the One<T>
class and one-to-many as well as many-to-many relationships are defined using the Many<TChild,TParent>
class and you have to initialize the Many<TChild,TParent>
child properties in the constructor of the parent entity as shown below.
public class Book : Entity
{
public One<Author> MainAuthor { get; set; }
public Many<Author, Book> CoAuthors { get; set; }
[OwnerSide]
public Many<Genre, Book> Genres { get; set; }
public Book()
{
this.InitOneToMany(() => CoAuthors);
this.InitManyToMany(() => Genres, genre => genre.Books);
}
}
public class Genre : Entity
{
[InverseSide]
public Many<Book, Genre> Books { get; set; }
public Genre()
{
this.InitManyToMany(() => Books, book => book.Genres);
}
}
notice the parameters of the InitOneToMany
and InitManyToMany
methods above. the first method only takes one parameter which is just a lambda pointing to the property you want to initialize.
the next method takes 2 parameters. first is the property to initialize. second is the property of the other side of the relationship.
also note that you specify which side of the relationship a property is using the attributes [OwnerSide]
or [InverseSide]
for defining many-to-many relationships.
One-to-one
a reference can be assigned in any of the following two ways:
book.MainAuthor = author.ToReference(); //call ToReference on a child
book.MainAuthor = new(author); //assign a child instance
book.MainAuthor = new("AuthorID"); //assign just the ID value of a child
await book.SaveAsync(); //call save on parent to store
Reference removal
book.MainAuthor = null;
await book.SaveAsync();
the original author
in the Authors
collection is unaffected.
Entity deletion
if you delete an entity that is referenced as above, all references pointing to that entity would then be invalid. as such, book.MainAuthor.ToEntityAsync()
will then return null
. the .ToEntityAsync()
method is described below.
for example:
book A has 1:1 relationship with author A
book B has 1:1 relationship with author A
book C has 1:1 relationship with author A
now, if you delete author A, the results would be the following:
await bookA.MainAuthor.ToEntityAsync() //returns null
await bookB.MainAuthor.ToEntityAsync() //returns null
await bookC.MainAuthor.ToEntityAsync() //returns null
One-to-many & many-to-many
await book.Authors.AddAsync(author); //one-to-many
await book.Genres.AddAsync(genre); //many-to-many
there's no need to call book.SaveAsync()
again because references are automatically saved using special join collections. you can read more about them in the Schema Changes section.
however, do note that both the parent entity (book) and child (author/genre) being added has to have been previously saved so that they have their ID
values populated. otherwise, you'd get an exception instructing you to save them both before calling AddAsync()
.
alternatively when you don't have access to the parent entity and you only have the parent ID
value, you can use the following to access the relationship:
await DB.Entity<Book>("BookID").Authors.AddAsync(author);
there are other overloads for adding relationships with multiple entities or just the string IDs.
click here to see a full example of a referenced one-to-many relationship.
Reference removal
await book.Authors.RemoveAsync(author);
await book.Genres.RemoveAsync(genre);
the original author
in the Authors
collection is unaffected. also the genre
entity in the Genres
collection is unaffected. only the relationship between entities are deleted.
there are other overloads for removing relationships with multiple entities or just the string IDs.
Entity deletion
when you delete an entity that's in a one-to-many
or many-to-many
relationship, all the references (join records) for the relationship in concern are automatically deleted from the join collections.
for example:
| author A has 3 referenced books:
|-- book A
|-- book B
|-- book C
| author B has 3 referenced book:
|-- book A
|-- book B
|-- book C
now, if you delete book B, the children of authors A and B would look like this:
| author A:
|-- book A
|-- book C
| author B:
|-- book A
|-- book C
ToEntityAsync() shortcut
a reference can be turned back in to an entity with the ToEntityAsync()
method.
var author = await book.MainAuthor.ToEntityAsync();
you can also project the properties you need instead of getting back the complete entity like so:
var author = await book.MainAuthor
.ToEntityAsync(a => new Author
{
Name = a.Name,
Age = a.Age
});
Transaction support
adding and removing related entities require passing in the session when used within a transaction. see here for an example.