• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loresoft / FluentCommand / 23278216331

19 Mar 2026 03:19AM UTC coverage: 57.398% (+0.7%) from 56.658%
23278216331

push

github

pwelter34
Enable nullable and improve source generators

1403 of 3069 branches covered (45.72%)

Branch coverage included in aggregate %.

527 of 907 new or added lines in 58 files covered. (58.1%)

22 existing lines in 10 files now uncovered.

4288 of 6846 relevant lines covered (62.64%)

330.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.49
/src/FluentCommand/Query/JoinEntityBuilder.cs
1
using System.Linq.Expressions;
2

3
using FluentCommand.Query.Generators;
4
using FluentCommand.Reflection;
5

6
namespace FluentCommand.Query;
7

8
/// <summary>
9
/// Provides a builder for constructing SQL JOIN clauses between two entity types with fluent, chainable methods.
10
/// </summary>
11
/// <typeparam name="TLeft">The entity type of the left join.</typeparam>
12
/// <typeparam name="TRight">The entity type of the right join.</typeparam>
13
public class JoinEntityBuilder<TLeft, TRight> : JoinBuilder<JoinEntityBuilder<TLeft, TRight>>
14
    where TLeft : class
15
    where TRight : class
16
{
17
    private static readonly TypeAccessor _leftAccessor = TypeAccessor.GetAccessor<TLeft>();
11✔
18
    private static readonly TypeAccessor _rightAccessor = TypeAccessor.GetAccessor<TRight>();
11✔
19

20
    /// <summary>
21
    /// Initializes a new instance of the <see cref="JoinEntityBuilder{TLeft, TRight}"/> class.
22
    /// </summary>
23
    /// <param name="queryGenerator">The <see cref="IQueryGenerator"/> used to generate SQL expressions.</param>
24
    /// <param name="parameters">The list of <see cref="QueryParameter"/> objects for the query.</param>
25
    public JoinEntityBuilder(IQueryGenerator queryGenerator, List<QueryParameter> parameters)
26
        : base(queryGenerator, parameters)
12✔
27
    {
28
    }
12✔
29

30
    /// <summary>
31
    /// Specifies the left property to join on using a strongly-typed property expression.
32
    /// </summary>
33
    /// <typeparam name="TValue">The type of the property value.</typeparam>
34
    /// <param name="property">An expression selecting the property on the left entity to join on.</param>
35
    /// <param name="tableAlias">The alias of the left table.</param>
36
    /// <returns>
37
    /// The same builder instance for method chaining.
38
    /// </returns>
39
    public JoinEntityBuilder<TLeft, TRight> Left<TValue>(
40
        Expression<Func<TLeft, TValue>> property,
41
        string tableAlias)
42
    {
43
        var propertyAccessor = GetPropertyAccessor(_leftAccessor, property);
12✔
44

45
        return Left(propertyAccessor.Column, tableAlias);
12✔
46
    }
47

48
    /// <summary>
49
    /// Specifies the right property to join on using a strongly-typed property expression.
50
    /// The table name and schema are inferred from the right entity type.
51
    /// </summary>
52
    /// <typeparam name="TValue">The type of the property value.</typeparam>
53
    /// <param name="property">An expression selecting the property on the right entity to join on.</param>
54
    /// <param name="tableAlias">The alias of the right table.</param>
55
    /// <returns>
56
    /// The same builder instance for method chaining.
57
    /// </returns>
58
    public JoinEntityBuilder<TLeft, TRight> Right<TValue>(
59
        Expression<Func<TRight, TValue>> property,
60
        string tableAlias)
61
    {
62
        var tableName = _rightAccessor.TableName
12!
63
            ?? throw new InvalidOperationException("The right entity table name is not configured.");
12✔
64

65
        return Right(
12✔
66
            property,
12✔
67
            tableName,
12✔
68
            _rightAccessor.TableSchema,
12✔
69
            tableAlias);
12✔
70
    }
71

72
    /// <summary>
73
    /// Specifies the right property to join on using a strongly-typed property expression and explicit table information.
74
    /// </summary>
75
    /// <typeparam name="TValue">The type of the property value.</typeparam>
76
    /// <param name="property">An expression selecting the property on the right entity to join on.</param>
77
    /// <param name="tableName">The name of the right table. If <c>null</c>, the entity mapping is used.</param>
78
    /// <param name="tableSchema">The schema of the right table. If <c>null</c>, the entity mapping is used.</param>
79
    /// <param name="tableAlias">The alias of the right table. If <c>null</c>, the table name is used as the alias.</param>
80
    /// <returns>
81
    /// The same builder instance for method chaining.
82
    /// </returns>
83
    public JoinEntityBuilder<TLeft, TRight> Right<TValue>(
84
        Expression<Func<TRight, TValue>> property,
85
        string? tableName,
86
        string? tableSchema,
87
        string? tableAlias)
88
    {
89
        var propertyAccessor = GetPropertyAccessor(_rightAccessor, property);
12✔
90
        var resolvedTableName = tableName ?? _rightAccessor.TableName
12!
91
            ?? throw new InvalidOperationException("The right entity table name is not configured.");
12✔
92

93
        return Right(
12!
94
            propertyAccessor.Column,
12✔
95
            resolvedTableName,
12✔
96
            tableSchema ?? _rightAccessor.TableSchema,
12✔
97
            tableAlias ?? resolvedTableName);
12✔
98
    }
99

100
    private static IMemberAccessor GetPropertyAccessor<TModel, TValue>(
101
        TypeAccessor typeAccessor,
102
        Expression<Func<TModel, TValue>> property)
103
    {
104
        if (property is null)
24!
NEW
105
            throw new ArgumentNullException(nameof(property));
×
106

107
        var propertyAccessor = typeAccessor.FindProperty(property);
24✔
108
        if (propertyAccessor is null)
24!
NEW
109
            throw new ArgumentException("The specified property does not exist on the entity.", nameof(property));
×
110

111
        return propertyAccessor;
24✔
112
    }
113
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc