Skip to content

Mappers

Purpose and Overview

The Mapping Layer transforms intermediate RDF data structures (implementing IRdfDefinition) into strongly-typed domain entities and vice versa, with proper type safety, validation, and invariants. Mappers bridge the gap between generic RDF triple stores and rich domain models.

Core Goal: Provide bidirectional mapping between weakly-typed RDF property collections and domain entities that enforce: - Required property validation - Type safety (enums, value objects, complex types) - Business invariants - Identity resolution and normalization

Note: This is the base RDF library. Domain-specific mappers (e.g., CIM entity mappers) are implemented in application-specific projects that reference this library.

Key Responsibilities

1. RDF Type Recognition

Mappers identify which RDF types they can handle via the CanMap method: - Supports multiple naming conventions ("Equipment", "cim:Equipment") - Enables polymorphic mapping through the mapper factory - Provides flexible namespace handling

2. Identity Extraction and Validation

Mappers extract and validate entity primary keys using ICimIdentifierParser: - Parse CIM resource IDs (#_uuid-123, urn:uuid:...) - Support URN UUID form (preferred in IEC 61970-552 Edition 2) - Support Hash form (#_uuid) and rdf:ID form (_uuid) - Convert to typed keys (e.g., Guid) for keyed entities - Use string-based MRID for non-keyed entities - Reject entities with invalid or missing identifiers

3. Property Resolution

Mappers extract properties from RDF definitions: - Navigate properties via IRdfProperty interface - Handle different property types (literal, resource, compound) - Support multiple naming conventions for flexibility

4. Type Conversion and Validation

Mappers transform RDF literal strings into typed values using ICimValueParser: - Parse floating-point values with invariant culture (TryParseFloat) - Parse integer values with invariant culture (TryParseInt) - Parse boolean values with multiple format support (TryParseBool): "true/false", "1/0", "yes/no" - Parse enumerations with case-insensitive matching (TryParseEnum<TEnum>) - Validate required vs. optional properties

5. Entity Construction

Mappers instantiate domain entities with validated data: - Call entity constructors with required parameters - Enforce domain invariants during creation - Return null for invalid/incomplete data - Preserve entity integrity

6. Reverse Mapping (Entity → RDF)

Mappers optionally support converting entities back to RDF definitions: - Serialize entity properties to RDF definitions - Generate proper type names and property URIs - Support Insert, Update, and Upsert operations in repositories - Indicated by the CanUnmap property

Architecture

Component Structure

graph TB
    subgraph "Mapper Layer"
        Factory["RdfEntityMapperFactory"]
        BaseInterface["IRdfEntityMapper&lt;TRdfDefinition, TEntity&gt;"]
        KeyedInterface["IRdfEntityMapper&lt;TRdfDefinition, TEntity, TKey&gt;"]
        KeyedImpl["KeyedEntityMapper"]
        NonKeyedImpl["NonKeyedEntityMapper"]
    end

    Factory -->|"Resolves & Injects"| KeyedImpl
    Factory -->|"Resolves & Injects"| NonKeyedImpl
    BaseInterface -->|"Extends"| KeyedInterface
    KeyedInterface -->|"Implements"| KeyedImpl
    BaseInterface -->|"Implements"| NonKeyedImpl

    subgraph "Dependencies"
        IdentityParser["ICimIdentifierParser"]
        ValueParser["ICimValueParser"]
        NodeResolver["ICimNodeResolver"]
    end

    KeyedImpl -->|"Uses"| IdentityParser
    KeyedImpl -->|"Uses"| ValueParser
    NonKeyedImpl -->|"Uses"| IdentityParser
    NonKeyedImpl -->|"Uses"| ValueParser

    subgraph "Core Types"
        Definition["IRdfDefinition"]
        Property["IRdfProperty"]
    end

    Definition <-->|"Map / Unmap"| KeyedImpl
    Definition <-->|"Map / Unmap"| NonKeyedImpl

    subgraph "Domain"
        KeyedEntity["Entity&lt;TKey&gt;"]
        NonKeyedEntity["Entity"]
    end

    KeyedEntity <-->|"Map / Unmap"| KeyedImpl
    NonKeyedEntity <-->|"Map / Unmap"| NonKeyedImpl

Core Interfaces

IRdfEntityMapper<TRdfDefinition, TEntity>

The base interface for all entity mappers with default implementations for optional reverse mapping:

public interface IRdfEntityMapper<TRdfDefinition, TEntity>
    where TRdfDefinition : class, IRdfDefinition
    where TEntity : class, IEntity
{
    /// <summary>
    /// Returns true if this mapper can handle the given RDF class (type) of this resource.
    /// Matches both unqualified (e.g., "BaseVoltage") and qualified (e.g., "cim:BaseVoltage") names.
    /// </summary>
    bool CanMap(string className);

    /// <summary>
    /// Creates a domain entity from the given RDF definition.
    /// Returns null if the definition does not have required data.
    /// </summary>
    /// <param name="definition">The RDF definition to map from.</param>
    /// <returns>The mapped domain entity, or null if mapping is not possible.</returns>
    TEntity? Map(TRdfDefinition definition);

    /// <summary>
    /// Gets a value indicating whether this mapper supports reverse mapping (entity to RDF definition).
    /// </summary>
    /// <returns><c>true</c> if reverse mapping is supported; otherwise, <c>false</c>.</returns>
    bool CanUnmap => false;

    /// <summary>
    /// Creates an RDF definition from the given domain entity.
    /// Returns null if reverse mapping is not supported or the entity cannot be converted.
    /// </summary>
    /// <param name="entity">The entity to convert to an RDF definition.</param>
    /// <returns>An RDF definition representing the entity, or null if conversion fails or is not supported.</returns>
    TRdfDefinition? Unmap(TEntity entity) => null;
}

IRdfEntityMapper<TRdfDefinition, TEntity, TKey>

Extension for keyed entities - inherits all members from the base interface:

public interface IRdfEntityMapper<TRdfDefinition, TEntity, TKey> : IRdfEntityMapper<TRdfDefinition, TEntity>
    where TRdfDefinition : class, IRdfDefinition
    where TEntity : class, IEntity<TKey>
{
    // Inherits all methods from IRdfEntityMapper<TRdfDefinition, TEntity>
    // Used for type safety when working with keyed entities
}

Mapping Directions:

Direction Method Purpose
RDF → Entity Map(TRdfDefinition) Read operations (Find, GetList, etc.)
Entity → RDF Unmap(TEntity) Write operations (Insert, Update, Upsert)

IRdfEntityMapperFactory

Resolves entity-specific mappers with automatic discovery and dependency injection:

public interface IRdfEntityMapperFactory
{
    /// <summary>
    /// Gets the RDF entity mapper for the specified entity type.
    /// </summary>
    IRdfEntityMapper<TRdfDefinition, TEntity> GetMapper<TRdfDefinition, TEntity>()
        where TRdfDefinition : class, IRdfDefinition
        where TEntity : class, IEntity;

    /// <summary>
    /// Gets the RDF entity mapper for the specified entity type and key type.
    /// </summary>
    IRdfEntityMapper<TRdfDefinition, TEntity, TKey> GetMapper<TRdfDefinition, TEntity, TKey>()
        where TRdfDefinition : class, IRdfDefinition
        where TEntity : class, IEntity<TKey>;
}

RDF Core Types

Mappers work with these core types when building or parsing RDF definitions:

Type Purpose Description
IRdfDefinition Resource representation Contains Id, ClassName, and Properties collection
IRdfDescription Difference model element Used only in difference models, contains About and Properties collection
IRdfProperty Property contract Base interface for all property types with Name property

IRdfDefinition Interface

public interface IRdfDefinition
{
    /// <summary>
    /// The resource identifier, as used in rdf:ID or rdf:about.
    /// </summary>
    string Id { get; }

    /// <summary>
    /// The RDF type (class) of this resource, expressed as a qualified name.
    /// Example: "cim:ACLineSegment" in <cim:ACLineSegment rdf:ID="...">
    /// </summary>
    string ClassName { get; }

    /// <summary>
    /// Collection of properties defined on this resource.
    /// </summary>
    IList<IRdfProperty> Properties { get; }
}

IRdfDescription Interface

public interface IRdfDescription
{
    /// <summary>
    /// Gets the resource URI referenced by rdf:about.
    /// </summary>
    string About { get; }

    /// <summary>
    /// Collection of properties defined on this resource.
    /// </summary>
    IList<IRdfProperty> Properties { get; }
}

IRdfProperty Interface

public interface IRdfProperty
{
    /// <summary>
    /// Gets the qualified property name.
    /// </summary>
    string Name { get; }
}

Mapper Implementation Pattern

public class MyEntityMapper : IRdfEntityMapper<MyRdfDefinition, MyEntity, Guid>
{
    private readonly ICimIdentifierParser _identifierParser;
    private readonly ICimValueParser _valueParser;

    public MyEntityMapper(ICimIdentifierParser identifierParser, ICimValueParser valueParser)
    {
        _identifierParser = identifierParser;
        _valueParser = valueParser;
    }

    #region Forward Mapping (RDF → Entity)

    public bool CanMap(string className)
    {
        if (string.IsNullOrWhiteSpace(className))
            return false;

        var name = className.Trim();
        return name.Equals("MyEntity", StringComparison.Ordinal)
            || name.EndsWith(":MyEntity", StringComparison.Ordinal);
    }

    public MyEntity? Map(MyRdfDefinition definition)
    {
        if (definition == null)
            return null;

        // 1. Extract and validate identity using CIM identifier parser
        if (!_identifierParser.TryParseGuid(definition.Id, out var entityId))
            return null;

        // 2. Extract properties from definition
        var name = GetPropertyValue(definition, "name") ?? $"MyEntity:{entityId:D}";

        // 3. Parse typed values using value parser
        var valueStr = GetPropertyValue(definition, "value");
        if (!_valueParser.TryParseFloat(valueStr, out var value))
            return null;

        // 4. Construct domain entity with validated data
        return new MyEntity(entityId, name, value);
    }

    private string? GetPropertyValue(MyRdfDefinition definition, string propertyName)
    {
        // Implementation depends on your RdfDefinition structure
        return definition.Properties
            .FirstOrDefault(p => p.Name.EndsWith(propertyName, StringComparison.Ordinal))
            ?.ToString();
    }

    #endregion

    #region Reverse Mapping (Entity → RDF)

    public bool CanUnmap => true;

    public MyRdfDefinition? Unmap(MyEntity entity)
    {
        if (entity == null)
            return null;

        // Create definition with ID and type
        var definition = new MyRdfDefinition(
            id: entity.Id.ToString("D"),
            type: "ns:MyEntity"
        );

        // Add properties
        definition.Properties.Add(new MyLiteralProperty("ns:name", entity.Name));
        definition.Properties.Add(new MyLiteralProperty("ns:value", entity.Value.ToString()));

        return definition;
    }

    #endregion
}

Mapping Flow

Forward Mapping (Read Operations)

sequenceDiagram
    participant Repo as RdfGraphRepository
    participant Factory as RdfEntityMapperFactory
    participant Mapper as EntityMapper
    participant Parser as ICimIdentifierParser
    participant ValueParser as ICimValueParser
    participant Domain as Domain Entity

    Repo->>Factory: GetMapper<TRdfDefinition, TEntity>()
    Factory-->>Repo: Return mapper

    Repo->>Mapper: CanMap(definition.ClassName)
    Mapper-->>Repo: true

    Repo->>Mapper: Map(definition)
    Mapper->>Parser: TryParseGuid() or TryParseId()
    Parser-->>Mapper: Guid or string ID
    Mapper->>ValueParser: TryParseFloat(), TryParseEnum(), etc.
    ValueParser-->>Mapper: Parsed values
    Mapper->>Domain: new Entity(id, name, value, ...)
    Domain-->>Mapper: Entity instance
    Mapper-->>Repo: Entity

Reverse Mapping (Write Operations)

sequenceDiagram
    participant Repo as RdfGraphRepository
    participant Mapper as EntityMapper
    participant Writer as IRdfInstanceWriter
    participant Graph as IGraph

    Repo->>Mapper: CanUnmap
    Mapper-->>Repo: true

    Repo->>Mapper: Unmap(entity)
    Mapper->>Mapper: Create IRdfDefinition(id, type)
    Mapper->>Mapper: Add IRdfProperty instances
    Mapper-->>Repo: IRdfDefinition

    Repo->>Writer: Insert(graph, definition)
    Writer->>Graph: Assert triples
    Writer-->>Repo: triplesInserted

RdfEntityMapperFactory Implementation

The factory uses automatic discovery via assembly scanning with ABP framework integration:

public class RdfEntityMapperFactory : IRdfEntityMapperFactory, ITransientDependency
{
    private readonly IServiceProvider _serviceProvider;
    private readonly Lazy<Dictionary<Type, Type>> _mapperTypes;

    public RdfEntityMapperFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
        _mapperTypes = new Lazy<Dictionary<Type, Type>>(DiscoverMapperTypes);
    }

    private Dictionary<Type, Type> DiscoverMapperTypes()
    {
        var result = new Dictionary<Type, Type>();

        var assemblyFinder = _serviceProvider.GetService<IAssemblyFinder>();
        if (assemblyFinder == null)
            return result;

        foreach (var assembly in assemblyFinder.Assemblies)
        {
            Type[] types;
            try { types = assembly.GetTypes(); }
            catch { continue; }

            foreach (var type in types)
            {
                if (type.IsAbstract || type.IsInterface)
                    continue;

                var interfaces = type.GetInterfaces()
                    .Where(i => i.IsGenericType)
                    .Where(i =>
                    {
                        var def = i.GetGenericTypeDefinition();
                        return def == typeof(IRdfEntityMapper<,>) 
                            || def == typeof(IRdfEntityMapper<,,>);
                    });

                foreach (var iface in interfaces)
                {
                    result[iface] = type;
                }
            }
        }

        return result;
    }

    public virtual IRdfEntityMapper<TRdfDefinition, TEntity> GetMapper<TRdfDefinition, TEntity>()
        where TRdfDefinition : class, IRdfDefinition
        where TEntity : class, IEntity
    {
        var targetInterface = typeof(IRdfEntityMapper<TRdfDefinition, TEntity>);

        if (_mapperTypes.Value.TryGetValue(targetInterface, out var mapperType))
        {
            return (IRdfEntityMapper<TRdfDefinition, TEntity>)_serviceProvider.GetRequiredService(mapperType);
        }

        var mapper = _serviceProvider.GetService<IRdfEntityMapper<TRdfDefinition, TEntity>>();
        if (mapper != null)
            return mapper;

        throw new InvalidOperationException(
            $"No IRdfEntityMapper<{typeof(TRdfDefinition).Name}, {typeof(TEntity).Name}> implementation was found or registered. " +
            "Ensure a concrete mapper class implements IRdfEntityMapper<TRdfDefinition, TEntity> and is registered in DI.");
    }

    public virtual IRdfEntityMapper<TRdfDefinition, TEntity, TKey> GetMapper<TRdfDefinition, TEntity, TKey>()
        where TRdfDefinition : class, IRdfDefinition
        where TEntity : class, IEntity<TKey>
    {
        var targetInterface = typeof(IRdfEntityMapper<TRdfDefinition, TEntity, TKey>);

        if (_mapperTypes.Value.TryGetValue(targetInterface, out var mapperType))
        {
            return (IRdfEntityMapper<TRdfDefinition, TEntity, TKey>)_serviceProvider.GetRequiredService(mapperType);
        }

        var mapper = _serviceProvider.GetService<IRdfEntityMapper<TRdfDefinition, TEntity, TKey>>();
        if (mapper != null)
            return mapper;

        // Fallback: Try non-keyed mapper if it also implements keyed interface
        var nonKeyedMapper = _serviceProvider.GetService<IRdfEntityMapper<TRdfDefinition, TEntity>>();
        if (nonKeyedMapper is IRdfEntityMapper<TRdfDefinition, TEntity, TKey> keyedMapper)
            return keyedMapper;

        throw new InvalidOperationException(
            $"No IRdfEntityMapper<{typeof(TRdfDefinition).Name}, {typeof(TEntity).Name}, {typeof(TKey).Name}> implementation was found or registered. " +
            "Ensure a concrete mapper class implements IRdfEntityMapper<TEntity, TRdfDefinition, TKey> and is registered in DI.");
    }
}

Discovery Mechanism: - Scans all loaded assemblies via ABP's IAssemblyFinder - Identifies types implementing IRdfEntityMapper<,> or IRdfEntityMapper<,,> - Builds type registry mapping interface to implementation - Caches registry using Lazy<T> for thread-safe lazy initialization - Resolves instances via IServiceProvider with dependency injection - Provides fallback from keyed to non-keyed mapper resolution

Bidirectional Mapping Support

When to Implement Unmap

Implement reverse mapping (CanUnmap = true and Unmap method) when your repository needs to support:

Operation Requires Unmap Description
Insert ✅ Yes Create new entity in graph
Update ✅ Yes Modify existing entity
Upsert ✅ Yes Insert or update
Find ❌ No Read-only operation
GetList ❌ No Read-only operation
Delete ❌ No Uses ID only, not full entity

Read-Only Mapper (Default)

For read-only scenarios, mappers don't need to implement Unmap - default implementations return false and null:

public class ReadOnlyEntityMapper : IRdfEntityMapper<MyRdfDefinition, ReadOnlyEntity, Guid>
{
    public bool CanMap(string className) => className.EndsWith(":ReadOnlyEntity");

    public ReadOnlyEntity? Map(MyRdfDefinition definition)
    {
        // Forward mapping implementation
    }

    // CanUnmap defaults to false (from interface default)
    // Unmap defaults to returning null (from interface default)
}

Full CRUD Mapper

For full CRUD support, implement both directions by overriding the defaults:

public class CrudEntityMapper : IRdfEntityMapper<MyRdfDefinition, CrudEntity, Guid>
{
    // Forward mapping
    public bool CanMap(string className) => className.EndsWith(":CrudEntity");
    public CrudEntity? Map(MyRdfDefinition definition) { /* ... */ }

    // Reverse mapping - override defaults
    public bool CanUnmap => true;
    public MyRdfDefinition? Unmap(CrudEntity entity)
    {
        if (entity == null) return null;

        var definition = new MyRdfDefinition(entity.Id.ToString("D"), "ns:CrudEntity");
        // Add properties...
        return definition;
    }
}

Repository Integration

The RdfGraphRepository integrates with mappers for both read and write operations:

public class RdfGraphRepository<TGraphContext, TRdfDefinition, TEntity> : RdfRepositoryBase<TEntity>, IRdfGraphRepository<TEntity>
    where TGraphContext : class, IRdfGraphContext
    where TRdfDefinition : class, IRdfDefinition
    where TEntity : class, IEntity
{
    protected TGraphContext GraphContext { get; }
    protected IRdfInstanceReader<TRdfDefinition> Reader { get; }
    protected IRdfInstanceWriter<TRdfDefinition> Writer { get; }
    protected IRdfEntityMapperFactory MapperFactory { get; }

    protected IRdfEntityMapper<TRdfDefinition, TEntity> Mapper => 
        _mapper ??= MapperFactory.GetMapper<TRdfDefinition, TEntity>();
    private IRdfEntityMapper<TRdfDefinition, TEntity>? _mapper;

    // Repository uses Mapper.CanMap() and Mapper.Map() for read operations
    // Repository uses Mapper.CanUnmap and Mapper.Unmap() for write operations
}

Write Operations Flow

// Repository checks mapper capability before write operations
public virtual TEntity Insert(TEntity entity)
{
    if (!Mapper.CanUnmap)
        throw new NotSupportedException(
            $"Mapper for {typeof(TEntity).Name} does not support reverse mapping. " +
            "Implement CanUnmap and Unmap in your mapper.");

    var definition = Mapper.Unmap(entity);
    if (definition == null)
        throw new InvalidOperationException(
            $"Mapper returned null when converting {typeof(TEntity).Name} to RDF definition.");

    if (Writer.Exists(graph, definition.Id))
        throw new InvalidOperationException($"Instance with id '{definition.Id}' already exists. Use Update instead.");

    Writer.Insert(graph, definition);
    return entity;
}

Error Handling

Scenario Behavior Exception Type
CanUnmap = false Repository throws when attempting write operations NotSupportedException
Unmap returns null Repository throws with descriptive message InvalidOperationException
Entity already exists on Insert Repository throws InvalidOperationException
Entity not found on Update Repository throws InvalidOperationException
Invalid entity ID Return null from Unmap Caught by repository
Domain constructor throws Return null from Map Silently skipped
Mapper not registered Factory throws with guidance InvalidOperationException

Architectural Relationships

Key Integration Points

1. Factory → Mapper Resolution

Purpose: Automatic mapper discovery and instantiation

Step Action
1 Factory initialized with IServiceProvider
2 On first request, scans assemblies via IAssemblyFinder
3 Builds registry: IRdfEntityMapper<TRdfDef, TEntity>ConcreteMapper
4 Caches registry for subsequent lookups
5 Resolves mapper via DI with injected dependencies

2. Mapper → Repository Operations

Repository Method Mapper Method Used Description
Find(predicate) CanMap(), Map() Filter and map definitions
FindById(mrid) CanMap(), Map() Single definition lookup and map
GetCount() CanMap() Count matching definitions
GetList() CanMap(), Map() Map all matching definitions
GetPagedList() CanMap(), Map() Map with pagination
Insert(entity) CanUnmap, Unmap() Convert entity to definition
Update(entity) CanUnmap, Unmap() Convert entity to definition
Upsert(entity) CanUnmap, Unmap() Convert entity to definition
Delete(entity) CanUnmap, Unmap() Extract ID from entity
DeleteById(mrid) - Uses ID directly

Dependency Flow

graph LR
    subgraph "Application Layer"
        Service[Application Service]
    end

    subgraph "Repository Layer"
        Repo[RdfGraphRepository]
        Factory[RdfEntityMapperFactory]
        Writer["IRdfInstanceWriter&lt;T&gt;"]
        Reader["IRdfInstanceReader&lt;T&gt;"]
    end

    subgraph "Mapper Layer"
        Mapper["EntityMapper"]
        IdentityParser[ICimIdentifierParser]
        ValueParser[IRdfValueParser]
    end

    subgraph "Core Layer"
        Definition[IRdfDefinition]
        Properties[IRdfProperty]
    end

    subgraph "Data Layer"
        Graph[IGraph]
    end

    subgraph "Domain Layer"
        Entity["Domain Entity"]
    end

    Service -->|"Find/Upsert"| Repo
    Repo -->|"GetMapper()"| Factory
    Factory -->|"Resolve via DI"| Mapper

    Repo -->|"Map (read)"| Mapper
    Mapper -->|"Parse IDs"| IdentityParser
    Mapper -->|"Parse values"| ValueParser
    Definition -->|"Input"| Mapper
    Properties -->|"Contained in"| Definition
    Mapper -->|"Output"| Entity

    Repo -->|"Unmap (write)"| Mapper
    Entity -->|"Input"| Mapper
    Mapper -->|"Output"| Definition

    Repo -->|"Read"| Reader
    Reader -->|"Query"| Graph

    Repo -->|"Write"| Writer
    Writer -->|"Assert/Retract"| Graph

Design Principles

Separation of Concerns

  • Single Responsibility: Each mapper handles one entity type
  • Interface Segregation: Keyed and non-keyed mappers use appropriate interfaces
  • Generic RDF Definition: Mappers are parameterized by TRdfDefinition for flexibility

Bidirectional Mapping

  • Default Implementations: C# 8+ default interface methods reduce boilerplate
  • Bidirectional by Design: Support both read (Map) and write (Unmap) operations
  • Optional Write Support: Default implementation allows read-only mappers

Dependency Injection

  • Constructor Injection: Parsers injected via constructor for testability
  • Transient Lifetime: Factory registered as ITransientDependency via ABP
  • Lazy Resolution: Mapper instances resolved on first access

Fail-Fast Pattern

  • Return null on validation failures: Let repository handle errors
  • Type Safety: Convert to strong types before entity construction
  • Clear Error Messages: Factory provides guidance when mappers not found

Discovery & Registration

  • Convention over Configuration: Automatic discovery eliminates manual registration
  • Assembly Scanning: Uses ABP's IAssemblyFinder for type discovery
  • Flexible Type Matching: Support multiple naming conventions for RDF types

Usage Examples

Mapper Registration (Automatic)

Mappers are automatically discovered via assembly scanning - no manual registration needed:

// RdfEntityMapperFactory discovers mappers automatically
// because they implement IRdfEntityMapper<TRdfDefinition, TEntity> 
// or IRdfEntityMapper<TRdfDefinition, TEntity, TKey>

Using Mappers via Repository (Read)

// Get repository with graph context
var repository = serviceProvider.GetRequiredService<IRdfGraphRepository<MyEntity, Guid>>();

// Find by ID - uses Map() internally
var entity = repository.Find(Guid.Parse("c24a1f3e-..."));

// Get list - uses Map() for each definition
var entities = repository.GetList();

Using Mappers via Repository (Write)

// Create new entity
var newEntity = new MyEntity(Guid.NewGuid(), "Name", 42.0f);

// Insert uses Unmap() internally
repository.Insert(newEntity);

// Update uses Unmap() internally  
entity.Name = "Updated Name";
repository.Update(entity);

// Upsert uses Unmap() internally
repository.Upsert(newEntity);

Direct Mapper Usage (Testing)

var mapper = serviceProvider.GetRequiredService<IRdfEntityMapper<MyRdfDefinition, MyEntity, Guid>>();

// Forward mapping (RDF → Entity)
var entity = mapper.Map(rdfDefinition);

// Reverse mapping (Entity → RDF)
if (mapper.CanUnmap)
{
    var definition = mapper.Unmap(entity);
    // definition can be used with IRdfInstanceWriter
}

Project Structure

src/GridLab.Abp.Rdf/GridLab/Abp/
├── Cim/
   ├── Parsers/
      ├── ICimIdentifierParser.cs       # CIM ID parsing contract
      ├── CimIdentifierParser.cs        # CIM ID parsing implementation
      ├── ICimValueParser.cs            # Value parsing contract
      ├── CimValueParser.cs             # Value parsing implementation
      └── README.md                     # Parsers documentation
   ├── Resolvers/
      ├── ICimNodeResolver.cs           # Node resolution contract
      └── CimNodeResolver.cs            # Node resolution implementation
   └── Factories/
       ├── ICimNodeFactory.cs            # Node factory contract
       └── CimNodeFactory.cs             # Node factory implementation
├── Rdf/
   ├── Core/
      ├── IRdfDefinition.cs             # Resource definition contract
      ├── IRdfDescription.cs            # Difference model description contract
      └── IRdfProperty.cs               # Property contract
   ├── Mappers/
      ├── IRdfEntityMapper.cs           # Base and keyed interfaces
      ├── IRdfEntityMapperFactory.cs    # Factory interface
      ├── RdfEntityMapperFactory.cs     # Factory implementation
      └── README.md                     # This documentation
   ├── Readers/
      └── IRdfInstanceReader.cs         # Read definitions from graph
   ├── Writers/
      └── IRdfInstanceWriter.cs         # Write definitions to graph
   └── Repositories/
       └── Graph/
           ├── IRdfGraphRepository.cs    # Repository interface
           └── RdfGraphRepository.cs     # Repository implementation

Value Parser Details

The ICimValueParser interface provides type-safe parsing for RDF literal values:

Supported Parse Methods

Method Input Formats Output Type Notes
TryParseFloat Numeric strings float Invariant culture, supports thousands separator
TryParseInt Integer strings int Invariant culture
TryParseBool "true"/"false", "1"/"0", "yes"/"no" bool Case-insensitive
TryParseEnum<T> Enum member names TEnum Case-insensitive matching

Example Usage

// Float parsing
if (_valueParser.TryParseFloat("110.5", out var voltage))
    Console.WriteLine($"Voltage: {voltage}kV");  // 110.5kV

// Integer parsing
if (_valueParser.TryParseInt("42", out var count))
    Console.WriteLine($"Count: {count}");  // 42

// Boolean parsing (multiple formats)
_valueParser.TryParseBool("true", out var b1);  // true
_valueParser.TryParseBool("1", out var b2);     // true
_valueParser.TryParseBool("yes", out var b3);   // true
_valueParser.TryParseBool("0", out var b4);     // false

// Enum parsing (case-insensitive)
if (_valueParser.TryParseEnum<UnitMultiplier>("k", out var multiplier))
    Console.WriteLine($"Multiplier: {multiplier}");  // UnitMultiplier.k

CIM Identifier Parser Details

The ICimIdentifierParser interface provides CIM-specific identifier handling according to IEC 61970-552:

Supported URI Formats

Format Example Description
URN UUID urn:uuid:26cc8d71-3b7e-4cf8-8c93-8d9d557a4846 Preferred in Edition 2
Hash form #_26cc8d71-3b7e-4cf8-8c93-8d9d557a4846 Fragment reference
rdf:ID form _26cc8d71-3b7e-4cf8-8c93-8d9d557a4846 Direct ID

Available Methods

public interface ICimIdentifierParser
{
    // Parse methods (throw on failure)
    string ParseId(string uri);
    Guid ParseGuid(string uri);

    // TryParse methods (return false on failure)
    bool TryParseId(string uri, out string result);
    bool TryParseGuid(string uri, out Guid result);

    // Validation methods
    bool IsValidCimUri(string uri);
    bool IsUrnUuidFormat(string uri);
    bool IsHashFormat(string uri);
}

See Also: - Repository Documentation - How repositories use mappers for CRUD operations - Graph Context Documentation - RDF graph lifecycle management - CIM Identifier Parsing - CIM-specific identifier parsing