Attributes confusion
hey! I'm having trouble choosing an approach when using attributes.
I'm building a wrapper around
System.Threading.RateLimiting
. I have an attribute RateLimitAttribute
which is responsible for the basic logic and creation of PartitionedRateLimiter
.
here’s the problem: I want to be able to create custom ignore policies (to have no limit in custom conditions) and pass them directly to the RateLimitAttribute
, but I don’t know how to do this correctly and what approach to choose.
initially I wanted to make a base class RateLimitPolicyAttribute
for all policies and later use it like this:
but the policies here can exist without the main RateLimitAttribute
also, I thought about passing the policies directly to the main attribute, but this approach is not possible because the values in attribute parameters must be constant, so I can't just pass a list of policies. additionally, it'll be a very long line in length if I have many policies
how can I implement this most conveniently?12 Replies
Option 1:
If these are user defined (not constants), then they have to pass in a list of Types most likely
Option 2:
If these are specific constants that you define and are an immutable list, I would just use a flagged Enum
these are user defined (not constants)
as for the list of types, how do you type them correctly? for example, just
Type[] policies
? but how do you get the needed method to execute without hard-coding its name in a string? I was trying to find something like "generic type" so I could use it like that: Type<BaseType>[] policies
or should I just do it like that:
You have to use reflection, it's why you'll notice many attributes take in Types as their params for basically this exact problem space
thank you!! I'll look into it :)
Attributes can be generic these days. So you can do
[RateLimit<SomeCustomPolicy>]
, with associated generic type constraintsI thought about this too, but what if you need multiple policies? also, you won't be able to use the
[RateLimit]
without the generic typeYeah, you can apply it multiple times, or have multiple versions which take different numbers of types, but it gets a bit messy
You can have both
[RateLimit]
and [RateLimit<Foo>]
easily thoughif I allow to use multiple
[RateLimit]
attributes, then there will be multiple PartitionedRateLimiter
instances, so the logic might be duplicated
however, maybe I still need to rework the core attribute logic to make this possible
if there's no policy, then the rate limit should be applied to every request (which is the default policy)
maybe I can do a DefaultPolicy
and use it like that [RateLimit<DefaultPolicy>]
well, it seems to be more customizable
speaking globally, what would you prefer as a user of it? pass policies into one attribute as the argument, thereby making the string infinitely long and unreadable, or make several such attributes that accept the policy as a generic argument creating a long chain of attributes on several lines?Personally I don't like using attributes for that sort of stuff, as it's awkward to customise them. If I need to instantiate my policy with a particular parameter, say, that gets awkward. Particularly if that parameter is only known at runtime
also, all of these attributes will always be executed before processing the request until there is a negative result or all attributes check the conditions (this is because of the framework's nature for which I am trying to implement it). because of this, there may be conflicts: for example, I have this method:
although
AdminPolicy
skips all requests, the following attribute is always executed after it, which will be valid for all requestsI mean, everything you're saying here isn't set in stone. It's up to you to define how the attributes interact
yeah, I'm of the same opinion, but the framework which I use decides the attributes interaction for me
anyway, thank you so much for the suggestion! I'll try to implement something similar and compare it with other ideas :)