Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The navigation properties of IQueryable ProjectTo do not require null checks. #1651

Open
ldqk opened this issue Dec 21, 2024 · 0 comments
Open
Labels
enhancement New feature or request

Comments

@ldqk
Copy link

ldqk commented Dec 21, 2024

Is your feature request related to a problem? Please describe.
When Project disable nullable reference type,The navigation properties of IQueryable ProjectTo do not require null checks.

Describe the solution you'd like
Source:

[Mapper]
public static partial class PostMapper
{
    [MapProperty("Category.Name", nameof(PostDto.CategoryName))]
    [MapProperty("Comment.Count", nameof(PostDto.CommentsCount))]
    public static partial PostDto ToDto(this Post car);

    public static partial IQueryable<PostDto> Project(this IQueryable<Post> q);
}

public record Category(int Id, string Name);
public record Post(int Id, string Title, string Content, int CategoryId)
{
    public virtual Category Category { get; set; }
    public virtual ICollection<Comment> Comments { get; set; }
}

public record Comment(int Id, string Content, int UserId, int PostId)
{
    public virtual Post Post { get; set; }
}

public record PostDto(int Id, string Title, string Content, int CategoryId)
{
    public string CategoryName { get; set; }
    public int CommentsCount { get; set; }
}

var dtos = dbContext.Posts.Project().ToList();

Actual:

    public static partial class MapperConfigs
    {
        [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.1.1.0")]
        public static partial global::ConsoleApp1.PostDto? ToDto(this global::ConsoleApp1.Post? car)
        {
            if (car == null)
                return default;
            var target = new global::ConsoleApp1.PostDto(car.Id, car.Title, car.Content, car.CategoryId);
            target.CategoryName = car.Category?.Name;
            if (car.Comments != null)
            {
                target.CommentsCount = car.Comments.Count;
            }
            return target;
        }

        [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.1.1.0")]
        public static partial global::System.Linq.IQueryable<global::ConsoleApp1.PostDto?>? Project(this global::System.Linq.IQueryable<global::ConsoleApp1.Post?>? q)
        {
            if (q == null)
                return default;
#nullable disable
            return System.Linq.Queryable.Select(
                q,
                x => new global::ConsoleApp1.PostDto(x.Id, x.Title, x.Content, x.CategoryId)
                {
                    CategoryName = x.Category != null && x.Category.Name != null ? x.Category.Name : default,
                    CommentsCount = x.Comments != null ? x.Comments.Count : default,
                }
            );
#nullable enable
        }
    }

The generated SQL:

SELECT p."Title",p."Content", p."CategoryId", p."Id", c."Id", c0."Id", c0."Content", c0."UserId", c0."PostId", (
          SELECT count(*)::int
          FROM "Comment" AS c1
          WHERE p."Id" = c1."PostId"), CASE
          WHEN c."Id" IS NOT NULL AND c."Name" IS NOT NULL THEN c."Name"
      END
      FROM "Post" AS p
      LEFT JOIN "Category" AS c ON p."CategoryId" = c."Id"
      LEFT JOIN "Comment" AS c0 ON p."Id" = c0."PostId"
      ORDER BY p."Id", c."Id"

Expect:

    public static partial class MapperConfigs
    {
        [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.1.1.0")]
        public static partial global::ConsoleApp1.PostDto? ToDto(this global::ConsoleApp1.Post? car)
        {
            if (car == null)
                return default;
            var target = new global::ConsoleApp1.PostDto(car.Id, car.Title, car.Content, car.CategoryId);
            target.CategoryName = car.Category?.Name;
            if (car.Comments != null)
            {
                target.CommentsCount = car.Comments.Count;
            }
            return target;
        }

        [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.1.1.0")]
        public static partial global::System.Linq.IQueryable<global::ConsoleApp1.PostDto?>? Project(this global::System.Linq.IQueryable<global::ConsoleApp1.Post?>? q)
        {
            if (q == null)
                return default;
            return System.Linq.Queryable.Select(
                q,
                x => new global::ConsoleApp1.PostDto(x.Id, x.Title, x.Content, x.CategoryId)
                {
                    CategoryName = x.Category.Name,
                    CommentsCount = x.Comments.Count,
                }
            );
        }
    }

Expect generated SQL:

SELECT p."Title", p."Content", p."CategoryId", (
          SELECT count(*)::int
          FROM "Comment" AS c0
          WHERE p."Id" = c0."PostId") AS "CommentCount", c."Name" AS "CategoryName", 
      FROM "Post" AS p
      LEFT JOIN "Category" AS c ON p."CategoryId" = c."Id"
@ldqk ldqk added the enhancement New feature or request label Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant