ValueConverterAttribute for Smart Enums and simple Value Objects
See original GitHub issueI work with Smart Enums and Value Objects quite a lot, which have to be persisted in a database. To do so the classes have to provide a ValueConverter
. Currently, the ValueConverters can be registered
- manually via
HasConversion
or inConfigureConventions
. Both options require some typing if the model is using dozens of different Smart Enums and Value Objects. - “automatically” in
OnModelCreating
by iterating over all entities and all properties - “automatically” by passing some conventions (like PropertyAddedConventions and NavigationAddedConventions) to
DbContextOptionsBuilder
An ideal solution would be if the developer doesn’t have to do anything besides implementing the Smart Enum or Value Object - similar as with JsonConverterAttribute
or MessagePackFormatterAttribute
.
The current work flow for JSON is as following:
- The developer installs a nuget package and implements a Smart Enum or a Value Object.
public partial class ProductType : IEnum<string>
{
public static readonly ProductType Groceries = new("Groceries");
public static readonly ProductType Housewares = new("Housewares");
}
A roslyn source generator kicks in and generates a lot of boilerplate code.
- The developer installs a JSON-specific nuget package, which comes with another roslyn source generator. The source generator applies the
JsonConverterAttribute
to the Smart Enum/Value Object - the type is json-serializable without further configuration.
[JsonConverterAttribute(typeof(ValueObjectJsonConverterFactory))]
partial class ProductType
{
}
With a ValueConverterAttribute
it would be the same as with JSON. A source generator could render a few lines of code making the type convertable.
[ValueConverterAttribute(typeof(MyProductTypeValueConverter))]
partial class ProductType
{
}
A more powerfull solution could be an attribute, which provides not just the ValueConverter
but some other configuration settings as well, like max-length, is-unicode and precision.
[ScalarValueAttribute(ValueConverter = typeof(MyProductTypeValueConverter), MaxLength = 20)]
partial class ProductType
{
}
Issue Analytics
- State:
- Created 2 years ago
- Comments:9 (5 by maintainers)
@PawelGerr if you’re simply looking for a way to easily bulk-configure a value converter for all properties of a given type, then you’d probably be interested in the new pre-convention model configuration in EF Core 6.0.
Aside from this, you seem to be asking to configure value converters via an attribute (Data Annotation). This approach would be generally limited, since attributes are very constrained in the parameters they allow (essentially only constants); we tend to provide Data Annotations for the simple/restricted cases (e.g. specifying a string column name), but value converters are quite an open case which may not be a good fit.
Given the new pre-convention model configuration which can be used to do the same thing, do you still think an attribute-based approach is important, especially given the added complexity of the source generator to augment the type with it?
As an aside, I’m curious about the pattern of defining a “smart enum” in this way… I understand the desire to pack additional state with the enum (not possible with built-in .NET enum), but this precludes using the enum in standard C# constructs (e.g. switches, pattern matching) and is quite inefficient (heap allocation for each enum instance, could be mitigated by making it a struct).
Ok, let’s wait for the value objects.