Shapely 0.0.3
dotnet add package Shapely --version 0.0.3
NuGet\Install-Package Shapely -Version 0.0.3
<PackageReference Include="Shapely" Version="0.0.3" />
<PackageVersion Include="Shapely" Version="0.0.3" />
<PackageReference Include="Shapely" />
paket add Shapely --version 0.0.3
#r "nuget: Shapely, 0.0.3"
#:package Shapely@0.0.3
#addin nuget:?package=Shapely&version=0.0.3
#tool nuget:?package=Shapely&version=0.0.3
Shapely
A C# source generator inspired by TypeScript's utility types (Pick, Omit) that simplifies creating DTOs and other derived types. Instead of hand-writing classes that are mostly copies of your domain models, you describe the shape you want and Shapely generates it.
Why Shapely?
When building APIs you often need types that are slight variations of your domain models — a response DTO that omits an internal field, a command object that picks only a few properties, or a composed type that pulls properties from two different services. In TypeScript you'd reach for Pick<T, K> or Omit<T, K>. Shapely brings that pattern to C#, with additional ideas suited to the language.
A common scenario: one service returns a type where a property contains just IDs. You need to enrich it with full objects from a second service. With Shapely you can model that as a shaped class that picks the bulk of its properties from the first type and replaces the ID property's type with the full type from the second service — all without manually writing out every property.
Installation
dotnet add package Shapely
Getting Started
Decorate a partial class with one or more shaping attributes:
using Shapely.Abstractions;
[All(typeof(Product))]
public partial class ProductDto { }
Shapely generates all public properties from Product into ProductDto at compile time. No mapping code, no property duplication.
Shaping Attributes
All — copy every property
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string InternalCode { get; set; }
}
[All(typeof(Product))]
public partial class ProductDto { }
// Generated:
// public partial class ProductDto
// {
// public int Id { get; set; }
// public string Name { get; set; }
// public decimal Price { get; set; }
// public string InternalCode { get; set; }
// }
Pick — include only named properties
[Pick(typeof(Product), nameof(Product.Id), nameof(Product.Name))]
public partial class ProductSummaryDto { }
// Generated: Id and Name only
Omit — exclude named properties
[Omit(typeof(Product), nameof(Product.InternalCode))]
public partial class ProductPublicDto { }
// Generated: Id, Name, Price — InternalCode dropped
Composing from Multiple Sources
Stack multiple attributes to pull properties from more than one type. This is especially useful when you're composing a response from multiple services:
// Service A returns orders with a list of product IDs
public class Order
{
public int OrderId { get; set; }
public DateTime PlacedAt { get; set; }
public IEnumerable<int> ProductIds { get; set; }
}
// Service B returns full product data
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
// Composed response: everything from Order except the IDs,
// plus the resolved products from Service B
[Omit(typeof(Order), nameof(Order.ProductIds))]
[Pick(typeof(Product), nameof(Product.Name))]
public partial class OrderWithProductsDto { }
Shapely raises a compile-time error (SHAPELY002) if two sources contribute a property with the same name.
Type Transformations
After shaping, you can transform property types without changing the property selection.
TransformAllTypes — replace a type everywhere
[All(typeof(Product))]
[TransformAllTypes(typeof(decimal), typeof(double))]
public partial class ProductApiDto { }
// Price becomes double instead of decimal
TransformType — replace a type on specific properties
[All(typeof(Order))]
[TransformType(typeof(IEnumerable<int>), typeof(IEnumerable<Product>), nameof(Order.ProductIds))]
public partial class EnrichedOrderDto { }
// ProductIds becomes IEnumerable<Product>
Accessor Transformations
Control whether generated properties use set or init accessors.
TransformAllSetAccessors — change all setters
[All(typeof(Product))]
[TransformAllSetAccessors(SetAccessor.Init)]
public partial class ImmutableProductDto { }
// All properties become init-only
TransformSetAccessor — change specific setters
[All(typeof(Product))]
[TransformSetAccessor(SetAccessor.Init, nameof(Product.Id))]
public partial class ProductDto { }
// Only Id becomes init-only; others stay as set
Analyzer Diagnostics
Shapely ships Roslyn analyzers that give you compile-time feedback:
| ID | Description |
|---|---|
SHAPELY001 |
Class with Shapely attributes must be partial |
SHAPELY002 |
Two sources produce a property with the same name |
SHAPELY003 |
Property named in a type transformation does not exist on the class |
SHAPELY004 |
A property appears in duplicate type transformation rules |
SHAPELY005 |
Property named in Pick/Omit does not exist on the source type |
SHAPELY006 |
A property appears in duplicate accessor transformation rules |
SHAPELY007 |
Property named in an accessor transformation does not exist on the generated class |
Working with Mapperly
Shapely works well alongside Mapperly for object mapping, but there is an ordering constraint: Roslyn source generators run in parallel, so Shapely-generated types are not visible to the Mapperly generator in the same project. The workaround is to keep your Shapely-shaped types in a separate project from your Mapperly mappers. That way the shaped types are compiled before Mapperly's generator runs, and Mapperly can see them normally.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.