m31coding / M31.FluentAPI

Generate fluent builders for your C# classes with ease.
MIT License
94 stars 3 forks source link

Support C# records #27

Closed Sam13 closed 1 month ago

Sam13 commented 1 month ago

Would it be possible to support fluent API generation for C# records?

[FluentApi]
public record Student(
  [FluentMember(0, "Named", 0)] string FirstName,
  [FluentMember(0, "Named", 1)] string LastName,
  [FluentMember(1, "OfAge")] int Age);
m31coding commented 1 month ago

Hi @Sam13,

Thank you for this request.

Records are already supported. Your example does not work because the attributes are specified for the primary constructor parameters. Instead, you should declare the attributes on the properties. Here’s how you can do it:

[FluentApi]
public record Student(
    [property: FluentMember(0, "Named", 0)] string FirstName,
    [property: FluentMember(0, "Named", 1)] string LastName,
    [property: FluentMember(1, "OfAge")] int Age)
{
    private Student() : this(default!, default!, default)
    {
    }
}

For more details, see records - Positional syntax for property definition.

Additionally, I have added a default constructor, which is required by the fluent API. Based on this working example, I might consider loosening the requirement of a default constructor.

Best regards, Kevin

Sam13 commented 1 month ago

Hi @m31coding

Thanks for your help. This works!

I tried to use inheritance of records (https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record#inheritance) - that doesn't work Example:

public abstract record Person(
    string Name);

[FluentApi]
public sealed record Student(
    [property: FluentMember(0)] string Name,
    [property: FluentMember(1)] string Subject)
    : Person(Name)
{
    private Student()
        : this(
            default!,
            default!)
    {
    }
}

I get the compiler error Error CS0657 : 'property' is not a valid attribute location for this declaration. for the inherited properties.

m31coding commented 1 month ago

You probably get this error because the compiler generates the Name property on the Person class and not on the Student class. Unfortunately, inheritance is not (yet?) supported by the fluent API library, which is a larger, separate issue.

In the meantime, I have released version v1.8.0, which allows you to omit the default constructor. If there is a default constructor, the instance will be created with it. Otherwise, the instance will be created with the explicitly defined constructor that has the fewest parameters. In the latter case, the constructor will be invoked with default values. This is now possible:

[FluentApi]
public record Student(
    [property: FluentMember(0, "Named", 0)] string FirstName,
    [property: FluentMember(0, "Named", 1)] string LastName,
    [property: FluentMember(1, "OfAge")] int Age);
Sam13 commented 1 month ago

You probably get this error because the compiler generates the Name property on the Person class and not on the Student class. Unfortunately, inheritance is not (yet?) supported by the fluent API library, which is a larger, separate issue.

I think that's what happens. If I create e.g. a Name2 property on the Student class the fluent code generation works. Downside is the additional duplicated property.

m31coding commented 1 month ago

As records are already supported, I will close this issue. Inheritance is being tracked under issue #29.

Best regards, Kevin

Sam13 commented 1 month ago

Inheritance is being tracked under issue #29.

Looking forward to this new feature - can provide test feedback if necessary.