Tips for working with EFCore | Vogen (original) (raw)
Identifying types that are generated by Vogen
Thank you to @jeffward01 for this item.
The goal of this is to identify types that are generated by Vogen.
Use Case
I use Vogen alongside EfCore, I like to programmatically add ValueConverter
s by convention, I need to identity which properties on my entities are generated value objects.
Solution
Vogen decorates the source it generates with the GeneratedCodeAttribute
. This provides metadata about the tool which generated the code, this is what we'll use as an identifier.
Note: the code snippets use:
- CSharpFunctionalExtensions - to use
Maybe<T>
BTW - if you're reading this, and have not checked out this library, I highly recommend. You don't need to adopt the pattern 100%, treat it as a buffet and take / use what you want - FluentAssertions—in the unit tests, just because
- XUnit, because it's better than NUnit and MSTest 🙃
Code Snippet
// Helper class internal static class AttributeHelper { public static bool IsVogenValueObject(this Type targetType) { Maybe generatedCodeAttribute = targetType.GetClassAttribute(); return generatedCodeAttribute.HasValue && generatedCodeAttribute.Value.Tool == "Vogen"; } private static Maybe GetClassAttribute( this Type targetType) where TAttribute : Attribute { return targetType.GetAttribute(); } }
Usage Example (From EfCore)
foreach (IMutableEntityType et in builder.Model.GetEntityTypes()) { PropertyInfo[] properties = et.ClrType.GetProperties(); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.PropertyType.IsVogenValueObject()) { // Huzzah! // Do something with the property that is a value // object generated by Vogen.... } } }
Testing
Data for unit test
[ValueObject] // ReSharper disable once PartialTypeWithSinglePart public readonly partial struct VogenStronglyTypedId {} Unit Test ```c# public class VogenStronglyTypedIdTests { [Fact] public void ShouldIdentityVogenAttributeByHelperMethod() { Type vogenType = typeof(VogenStronglyTypedId); Maybe generatedCodeAttribute = vogenType.GetClassAttribute(); Assert.True(generatedCodeAttribute.HasValue); GeneratedCodeAttribute? valueOfAttribute = generatedCodeAttribute.Value; valueOfAttribute.Tool.Should().Be("Vogen"); } [Fact] public void ShouldIdentityVogenAttribute() { Type vogenType = typeof(VogenStronglyTypedId); vogenType.IsVogenValueObject() .Should() .Be(true); } }
Summary
Jeff says:
Adding all value objects to the ModelConfigurationBuilder
Thank you to CheloXL for this tip
Just to add another snippet that I'm using in my solutions: Add all VOs that have EfCoreValueConverter to the ModelConfigurationBuilder
:
internal static class VogenExtensions { public static void ApplyVogenEfConvertersFromAssembly( this ModelConfigurationBuilder configurationBuilder, Assembly assembly) { var types = assembly.GetTypes(); foreach (var type in types) { if (IsVogenValueObject(type) && TryGetEfValueConverter( type, out var efCoreConverterType)) { configurationBuilder .Properties(type) .HaveConversion(efCoreConverterType); } } } private static bool TryGetEfValueConverter( Type type, [NotNullWhen(true)]out Type? efCoreConverterType) { var inner = type.GetNestedTypes(); foreach (var innerType in inner) { if (!typeof(ValueConverter).IsAssignableFrom(innerType) || !"EfCoreValueConverter".Equals( innerType.Name, StringComparison.Ordinal)) { continue; } efCoreConverterType = innerType; return true; } efCoreConverterType = null; return false; } private static bool IsVogenValueObject(MemberInfo targetType) { var generatedCodeAttribute = targetType.GetCustomAttribute(); return "Vogen".Equals( generatedCodeAttribute?.Tool, StringComparison.Ordinal); } }
Usage: In your dbcontext
:
protected override void ConfigureConventions( ModelConfigurationBuilder configurationBuilder) { configurationBuilder.ApplyVogenEfConvertersFromAssembly( typeof(YOURDBCONTEXT).Assembly); }
Last modified: 06 June 2025