Reputation: 984
Given following simple example of object:
public class Item
{
public int Id { get; set; }
public string Description { get; set; }
public Item ChildItem { get; set; }
}
Assume, that:
DbSet<Item>
.ChildItem
property is is configured as one-to-zero-or-many relationship with 'self'.builder.HasOne(x => x.ChildItem)
.WithMany()
.IsRequired(false)
.OnDelete(DeleteBehavior.Restrict);
public class ItemProjection
{
public int Id { get; set; }
public ItemProjection ChildItem { get; set; }
}
.AsNoTrackingWithIdentityResolution()
is used when creating query to retrieve Item
data.I am trying to find out if there is a correct way of returning entities as projections.
Options that came to my mind are:
Return projections using standard EF Core mechanisms, such as select to ItemProjection
or anonymous type - this is very simple, BUT when more then one parent has reference to the same child item, when projected - those child items are all separate instances.
Return custom mappings that will save the reference relationships between parent and child, so parent projections in case of pointing to the same child object will actually point to the same instance of child projection.
Mind that there are situations, where returned collection will contain parents that contain children and children by themselves as parents without children. In such situation first option also returns separate instances even though, at least in my mind, they should be the same instance.
Later in lifecycle of the application, those projections will be modified and translated back to entities.
Main question is - is there a preferred / proper way of handling such thing?
Upvotes: 0
Views: 308
Reputation: 34908
The idea behind projecting is to provide a consumer what a consumer needs rather than everything the data can provide. Since projections are typically used in transport layers between the server/service and the consumer/client, this often involves things like serialization in which case worrying about single vs. multiple instances becomes a non-concern. The end consumer will almost invariably be receiving multiple instances, and returning multiple instances when that data is sent back to the server.
Projections should ideally limit the data to just what the consumer will need to see, and that can mean flattening data relationships if there is no real benefit to maintaining them. It could also mean that while your domain model might be normalized down to Item Item -> Item Child, your projected "child" doesn't need to expose everything that it's underlying "Item" has, (including it's own possible child) in which case you can consider:
ItemProjection
{
Id,
// ... Item Fields
ChildId,
ChildField
// ...
}
or
ItemProjection
{
Id,
// ... Item Fields
ChildProjection Child
}
Where ChildProjection is a separate definition specific to the data needed to represent a child in this relationship. (Not necessarily all of the "Item" fields)
Most often with 1..1 / 0..1 relationships I opt to flatten the projections where the number of fields I care about from the child(ren) are relatively few. For cases where I do need more detail from a child I will use a child projection. I'd only consider something like an ItemProjection Child
in cases where my consumer specifically wants to build a self-referencing tree.
Upvotes: 2