Vogen icon indicating copy to clipboard operation
Vogen copied to clipboard

[Documentation Enhancement] [Code Snippet] -- How to Identity a type that is generated by 'Vogen'

Open jeffward01 opened this issue 1 year ago • 4 comments

Describe the feature

Goal The goal of this is to identify types that are generated by Vogen.

Use Case I use Vogen along side EfCore, I like to programmatically add ValueConverters by convention, I need to identity which properties on my entities are Vogen Generated value objects.

This is one of many use-cases

Shoutout:

  • Big thanks to @SteveDunn for using GeneratedCodeAttribute, this was very helpful, and I have not seen it used before in the numerous other Source Generator libraries I have reviewed.
    • Basically GeneratedCodeAttribute provides metadata about the tool which generated the code, this is what we'll use as an identifier.

Note: My code snippets use:

  • CSharpFunctionalExtensions - to use Maybe<T>
    • btw - if your reading this, and have not checked out this library, I highly recommend.
    • You don't need to adopt the pattern 100%, just treat it as a buffet and take / use what you want
  • FluentAssertions - in the unit tests, just because
  • XUnit, because its better than NUnit and MSTest 🙃

Code Snippet


// Helper class
internal static class AttributeHelper
{
    public static bool IsVogenValueObject(this Type targetType)
    {
        Maybe<GeneratedCodeAttribute> generatedCodeAttribute = targetType.GetClassAttribute<GeneratedCodeAttribute>();
        return generatedCodeAttribute.HasValue && generatedCodeAttribute.Value.Tool == "Vogen";
    }

    private static Maybe<TAttribute> GetClassAttribute<TAttribute>(this Type targetType)
        where TAttribute : Attribute
    {
        return targetType.GetAttribute<TAttribute>();
    }
}

Usage Example (From EfCore)


 foreach (IMutableEntityType entityType in builder.Model.GetEntityTypes())
        {
            PropertyInfo[] properties = entityType.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<Guid>]
// ReSharper disable once PartialTypeWithSinglePart
public readonly partial struct VogenStronglyTypedId {}

Unit Test

public class VogenStronglyTypedIdTests
{
    [Fact]
    public void ShouldIdentityVogenAttributeByHelperMethod()
    {
        Type vogenType = typeof(VogenStronglyTypedId);

        Maybe<GeneratedCodeAttribute> generatedCodeAttribute = vogenType.GetClassAttribute<GeneratedCodeAttribute>();
        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);
    }
}


Note

I think this is outside the scope of the library, but I imagine a large majority of Vogen users have some usecase where this will be helpful.

Add it to the docs, or dont 😄 -- I just wanted to share.


Btw:

  • I think a snippet area where users can share code like this, sorta like Samples but smaller and less overhead would be cool.
  • At the same time, this snippet and snippet samples area might become messy, or is not relevant (out if scope), so perhaps its a reason to not add it.
    • I know that CSharpFunctionalExtensions keeps all of their snippets in the Issues, then you can use GitHub search to find the snippets, it works... but I find myself having to really 'dig' for code and examples

Thanks all

jeffward01 avatar Jan 09 '23 23:01 jeffward01