HierarchyId in Clean Architecture

120 Views Asked by At

I want to migrate an existing .NET project to the Clean Architecture model but the current EF Core entities are using the HierarchyId data type (from the EntityFrameworkCore.SqlServer.HierarchyId package).

This type provides features that are useful even as domain entities (like the path of the node in the tree, the GetLevel() method, etc.)

How am I supposed to handle this if the core project has no dependency on EF Core?

1

There are 1 best solutions below

2
Artyom Yeprikyan On BEST ANSWER

Instead of reimplementing the entire functionality of the HierarchyId data type, you can create your own simplified representation of hierarchical data within your domain layer. This can be achieved by defining a custom type or class that encapsulates the hierarchical information relevant to your application.

For example, you could define a HierarchyNode class with properties such as Id, ParentId, and Level, which represent the hierarchical structure of your data. Here's a basic example of how you could implement such a class:

public class HierarchyNode
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public int Level { get; set; }
    // additional properties
}

Next, you'll need to map between your HierarchyNode class and the HierarchyId data type when interacting with your database. This can be done using mapping methods or utilities within your infrastructure layer. For example, when retrieving data from the database, you would map the HierarchyId values to instances of your HierarchyNode class, and vice versa when storing data.

Here's a simplified example of how you could perform mapping between your custom type and the HierarchyId data type:

public static class HierarchyMapper
{
    public static HierarchyNode MapFromHierarchyId(HierarchyId hierarchyId)
    {
        // Implement logic to map HierarchyId to HierarchyNode
        // Example:
        return new HierarchyNode
        {
            Id = hierarchyId.Value,
            ParentId = hierarchyId.GetAncestor().Value,
            Level = hierarchyId.GetLevel()
        };
    }

    public static HierarchyId MapToHierarchyId(HierarchyNode node)
    {
        return HierarchyId.Parse($"/{node.Id}/"); 
    }
}

Using these mapping methods, you can seamlessly integrate hierarchical data into your Clean Architecture project without the need to directly manipulate the HierarchyId data type within your domain layer.

I hope this provides a helpful starting point for implementing mapping between your own type and the HierarchyId data type. If you have any further questions or need clarification, feel free to ask.

UPD:

Use a ValueConverter to map between your custom type (HierarchyNode) and the HierarchyId data type

public class HierarchyNodeConverter : ValueConverter<HierarchyNode, HierarchyId>
{
    public HierarchyNodeConverter() : base(
        v => v != null ? HierarchyId.Parse($"/{v.Id}/") : default,
        v => v != null ? new HierarchyNode { Id = v.Value, ParentId = v.GetAncestor().Value, Level = v.GetLevel() } : null)
    { }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<YourEntity>()
        .Property(e => e.NodePath)
        .HasConversion(new HierarchyNodeConverter());
}

For Complex Queries consider using stored procedures or raw SQL queries.

var result = context.YourEntities
    .FromSqlRaw("SELECT * FROM YourEntities WHERE NodePath.IsDescendantOf({0})", manager.HierarchyIdParameter)
    .ToList();

This will execute the SQL directly, leveraging the database's capabilities for efficient hierarchical operations.