upvote
This is my experience too, I've worked on code bases which made heavy use of attributes, and they work really well to provide static per-type information, but if you see them as a universal problem solver and try to go too fancy, you'll find yourself in trouble.

Fluent builders are nicer to work with than attributes, although it sometimes feels weird if the defaults are nearly fine but not quite, and you wish you could just reach for a single attribute rather than having to traverse down 3 layers of builders to change a single property.

reply
The only case I've used them was to mark classes that I need to find via reflection and do something with them. For example a migration system where you want to load all migrations that are defined and check if you need to run them. Of course you don't really need an attribute for that, but I find it helpful to leaven a marker on the class that there's something else going on there.
reply
To solve this problem I have seen the following pattern:

1. Create an abstract base class named MigrationBaseClass 2. Have all migrations classes inherit from MigrationBaseClass 3. Use .Net Reflection to get all types that inherit from MigrationBaseClass 4. Do something with these types.

reply
Doesn't even need to be an abstract base class. It's just as easy to use reflection to find all implementations in an assembly of an IMigration interface.

(ETA: Though my favorite pattern here became using DI for this instead of reflection. For every IMigration have a `services.AddTransient<IMigration, SomeMigrationImplementationClass>()` somewhere and then your service to run all migrations can just request from DI `IEnumerable<IMigration>`. I can then put the Reflection into a unit test to make sure everything that implements IMigration is registered in the DI container. But using DI in the main assembly to register all the migrations rather than Reflection leaves more room to try to AOT compile the assembly in production builds.)

reply
Assuming you have all the code in your solution, you could do this with a source generator instead and have no need of reflection and are AOT compatible
reply
yeah, there's plenty of ways to do that. I like having the attribute there so that there is a strong hint that some magic is happening somewhere with that class.
reply
FastEndpoints does this for you with Validators as an example... It can be abstracted cleanly.
reply