Skip to content

A project which uses Source Generation to create a FluentBuilder for a specified model or DTO

License

Notifications You must be signed in to change notification settings

StefH/FluentBuilder

Repository files navigation

Projects

See also these blogs:

CSharp.SourceGenerators.Extensions

See this page.

FluentBuilder

A project which uses Source Generation to create a FluentBuilder for a specified model or DTO.

This project is based on Tom Phan : "auto-generate-builders-using-source-generator-in-net-5".

Install

NuGet Badge

You can install from NuGet using the following command in the package manager window:

Install-Package FluentBuilder

Or via the Visual Studio NuGet package manager or if you use the dotnet command:

dotnet add package FluentBuilder

1️⃣ Usage on a existing class

Annotate a class

Annotate an existing class with [FluentBuilder.AutoGenerateBuilder] to indicate that a FluentBuilder should be generated for this class:

using FluentBuilder;

[AutoGenerateBuilder]
public class User
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public DateTime? Date { get; set; }

    [FluentBuilderIgnore] // Add this attribute to ignore this property when generating a FluentBuilder.
    public int Age { get; set; }

    public int Answer { get; set; } = 42; // When a default value is set, this value is also set as default in the FluentBuilder.
}

AutoGenerateBuilder - attribute

This attribute has 4 arguments:

  • type (Type): The type for which to create the builder, see 'Define a class which needs to act as a builder'
  • handleBaseClasses (bool): Handle also base-classes. Default value is true.
  • accessibility (enum FluentBuilderAccessibility): Generate builder methods for Public or PublicAndPrivate. Default value when not provided is Public.
  • methods (enum FluentBuilderMethods): Generate With*** methods or also Without*** methods. Default value when not provided is WithOnly. See also Notes

Use FluentBuilder

using System;
using FluentBuilder;

namespace Test;

class Program
{
    static void Main(string[] args)
    {
        var user = new UserBuilder()
            .WithFirstName("Test")
            .WithLastName("User")
            .Build();

        Console.WriteLine($"{user.FirstName} {user.LastName}");
    }
}

Use FluentBuilder when the class has a default (parameter-less) constructor

using FluentBuilder;

[AutoGenerateBuilder]
public class User
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public DateTime? Date { get; set; }

    public User()
    {
        FirstName = "test";
    }
}
using System;
using FluentBuilder;

namespace Test;

class Program
{
    static void Main(string[] args)
    {
        var user = new UserBuilder()
            .WithLastName("User")
            .Build(false); // ⭐ Provide `false` for `useObjectInitializer` here.

        Console.WriteLine($"{user.FirstName} {user.LastName}");
    }
}

Use FluentBuilder when the class has a constructor with parameters

using FluentBuilder;

[AutoGenerateBuilder]
public class User
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public DateTime? Date { get; set; }

    public User(string first)
    {
        FirstName = first;
    }
}
using System;
using FluentBuilder;

namespace Test;

class Program
{
    static void Main(string[] args)
    {
        var user = new UserBuilder()
            .UsingConstructor("First")  // ⭐ Use `UsingConstructor` here.
            .WithLastName("User")
            .Build();

        Console.WriteLine($"{user.FirstName} {user.LastName}");
    }
}

Using FluentBuilder when a class has an Array or IEnumerable<T> property

using FluentBuilder;

[AutoGenerateBuilder]
public class UserDto
{
    public IEnumerable<EmailDto> SecondaryEmails { get; set; }

    public int[] IntArray { get; set; }
}
var user = new UserDtoBuilder()
    .WithIntArray(ib => ib         // πŸ‘ˆ Use a Integer Array Builder
        .Add(1)                    // Add a normal integer

        .Add(() => 2)              // Add an integer with a Func<>
        .Build()
    )
    .WithSecondaryEmails(sb => sb  // πŸ‘ˆ Use a EmailDto IEnumerable Builder
        .Add(new EmailDto())       // Add a normal EmailDto using new() constructor

        .Add(() => new EmailDto()) // Add an EmailDto using Func<>

        .Add(eb => eb              // πŸ‘ˆ Use a EmailDto IEnumerable Builder to add an EmailDto
            .WithPrimary(true)
            .Build()
        )
        .Build()
    )
    .Build();

Using FluentBuilder when a class has an IDictionary<TKey, TValue> property

using FluentBuilder;

[AutoGenerateBuilder]
public class UserDto
{
    public IDictionary<string, int> Tags { get; set; }
}
var user = new UserDtoBuilder()
    .WithTags(db => db      // πŸ‘ˆ Use a Dictionary<TKey, TValue> Builder
        .Add("test", 123)   // Add a key with value

        .Add(() => new KeyValuePair<string, int>("x", 42)) // Add a KeyValuePair with a Func<>
        .Build()
    )
    .Build();

2️⃣ Define a class which needs to act as a builder

This scenario is very usefull when you cannot modify the class to annotate it.

Create a public and partial builder class

And annotate this class with [AutoGenerateBuilder(typeof(XXX))] where XXX is the type for which you want to generate a FluentBuilder.

using FluentBuilder;

[AutoGenerateBuilder(typeof(UserDto))]
public partial class MyUserDtoBuilder
{
}

Use FluentBuilder

using System;
using FluentBuilder;

namespace Test;

class Program
{
    static void Main(string[] args)
    {
        var user = new MyUserDtoBuilder() // πŸ‘ˆ Just use your own Builder
            .WithFirstName("Test")
            .WithLastName("User")
            .Build();

        Console.WriteLine($"{user.FirstName} {user.LastName}");
    }
}

Extension Method

By default, the AsBuilder extension method is also generated which allows you to change an existing instance using the With-methods from the builder.

Example:

var user = await dbContext.Users.FirstAsync();

user = user.AsBuilder() // Lifts the user into a builder instance
    .WithLastName("Different LastName") // Updates "LastName" while keeping existing properties
    .Build(); // Changes are applied

await dbContext.SaveChangesAsync(); // User's LastName property is updated.

Notes

Since version 0.8.0, this FluentBuilder will only generate the With*** methods. If you want the builder to also generate the Without*** methods, add the enum FluentBuilderMethods.WithAndWithout to the attribute.

using FluentBuilder;

[AutoGenerateBuilder(typeof(UserDto), FluentBuilderMethods.WithAndWithout)]
public partial class MyUserDtoBuilder
{
}

About

A project which uses Source Generation to create a FluentBuilder for a specified model or DTO

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •