Gerador/Source Generator para criar automaticamente projeções (Expression<Func<TFrom, TDto>>), métodos auxiliares e propriedades em DTOs, reduzindo drasticamente boilerplate em consultas LINQ / EF Core.
[AutoSelect<TFrom>]: gera expressão de seleção, métodoFrom, extensõesSelect{Dto}/To{Dto}.[AutoProperties]ou[AutoProperties<TFrom>]: gera propriedades simples automaticamente (primitivos, string, bool, DateTime, enum, struct, coleções simplesIEnumerable<T>desses tipos).- Flattening por convenção: nomes concatenados em PascalCase resolvem cadeias aninhadas (ex.:
CustomerAddressCountryRegionName?a.Customer.Address.Country.Region.Name). - Exclusão de propriedades:
Exclude = [ nameof(Entity.Prop) ]. - Diagnósticos de compilação para uso incorreto, tipos incompatíveis e conflitos.
- Instalação
<ItemGroup>
<PackageReference Include="RoyalCode.SmartSelector" Version="x.y.z" />
<PackageReference Include="RoyalCode.SmartSelector.Generators" Version="x.y.z" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>- Anote seu DTO
[AutoSelect<User>, AutoProperties]
public partial class UserDetails { }- Consulte com EF Core
var list = db.Users.SelectUserDetails().ToList();
var dto = UserDetails.From(user);
var expr = UserDetails.SelectUserExpression; // reutilizável / componívelEscopo e foco
- Sem custom resolvers, conditional mapping ou naming policies.
- Foco: projeções traduzíveis por EF Core e mapeamento 1x1 (estilo Adapt do Mapster).
Links úteis
- Documentação completa:
src/docs.md - Projeto Demo:
RoyalCode.SmartSelector.Demo - Benchmarks:
RoyalCode.SmartSelector.Benchmarks
Frameworks e pacotes suportados
- Runtime lib:
RoyalCode.SmartSelector(TFMs: .NET 8, .NET 9, .NET 10) - Generator:
RoyalCode.SmartSelector.Generators(TFM: .NET Standard 2.0, instalado como Analyzer)
[AutoSelect<User>, AutoProperties]
public partial class UserDetails { }
// Uso
var list = db.Users.SelectUserDetails().ToList();
var dto = UserDetails.From(user);
var expr = UserDetails.SelectUserExpression; // reutilizável / componívelCódigo gerado (essencial):
public static Expression<Func<User, UserDetails>> SelectUserExpression => u => new UserDetails
{
Id = u.Id,
Name = u.Name
};
public static UserDetails From(User u) => (selectUserFunc ??= SelectUserExpression.Compile())(u);[AutoSelect<Book>, AutoProperties(Exclude = [ nameof(Book.Sku) ])]
public partial class BookDetails
{
public ShelfDetails Shelf { get; set; }
}
[AutoProperties<Shelf>]
public partial class ShelfDetails { }Trecho gerado:
new BookDetails
{
Shelf = new ShelfDetails
{
Id = a.Shelf.Id,
Location = a.Shelf.Location
},
Price = a.Price,
};
// Sku excluídopublic class Order
{
public Customer Customer { get; set; }
}
// Customer -> Address -> Country -> Region
[AutoSelect<Order>]
public partial class OrderDetails
{
public string CustomerAddressCountryRegionName { get; set; }
}Trecho da expressão:
CustomerAddressCountryRegionName = a.Customer.Address.Country.Region.NameAutoSelect<T>:
[AutoSelect<Product>]
public partial class ProductDetails { }
// Uso:
db.Products.SelectProductDetails().ToList();AutoProperties:
[AutoSelect<Simple>, AutoProperties]
public partial class SimpleDto { /* propriedades simples geradas automaticamente */ }Para usar AutoProperties, o tipo de origem é inferido do AutoSelect<TFrom>.
AutoProperties<TFrom>:
[AutoProperties<User>]
public partial class UserSnapshot { }- DTO aninhado +
Exclude:
[AutoSelect<Order>, AutoProperties<Order>(Exclude = [ nameof(Order.InternalCode) ])]
public partial class OrderDetails
{
public CustomerDetails Customer { get; set; }
}
[AutoProperties<Customer>]
public partial class CustomerDetails { }- Flattening por nome:
[AutoSelect<Order>]
public partial class OrderFlat
{
public string CustomerAddressCity { get; set; }
}
// Gera: CustomerAddressCity = a.Customer.Address.City- Nome da propriedade = concatenação PascalCase dos segmentos do caminho.
- Sem necessidade de atributos extras.
- Primitivos numéricos,
bool,string,char,DateTime/ nullable simples enum,structIEnumerable<T>ondeTé suportado acima / enum / struct
[AutoProperties<Product>(Exclude = [ nameof(Product.InternalCode), nameof(Product.Secret) ])]- Tipos inválidos ou classe não
partial(RCSS000). - Propriedade não encontrada (
RCSS001). - Tipos incompatíveis (
RCSS002). - Uso incorreto de atributos (
RCSS003–RCSS005).
- Sem renome/alias explícito ainda (
MapFrom). - Sem transformações de tipo (formatters / custom converters).
- Desambiguação de flattening limitada em colisões de prefixo.
- Use
nameofemExclude. - Prefira consumir a expressão gerada para reutilização e composição LINQ.
- Para caminhos muito longos, avalie DTOs aninhados por clareza.
| Pergunta | Resposta |
|---|---|
| Preciso configurar algo no runtime? | Não, pura geração de código. |
| Funciona com EF Core? | Sim, a expressão é traduzível. |
| Posso só gerar propriedades? | Sim: [AutoProperties<TFrom>]. |
| Flattening precisa de atributo? | Não, é por nome. |
- Documentação detalhada: ver
docs.mdno repositório. - Projeto Demo: ver
RoyalCode.SmartSelector.Demo. - Benchmarks: ver
RoyalCode.SmartSelector.Benchmarks.
Happy coding!