Open mrudat opened 6 months ago
The proposed change lives in @dotnet/area-system-componentmodel-dataannotations , though we may need to expose the timeout field on the Regex object.
Attribute parameters can only be compile-time constants, and limited to primitive types only.
The proposed change won't work at all.
Ah, so the only way to apply compile-time generation to RegularExpressionAttribute is to build a new source code generator for this specific case.
That sounds like it's entirely possible, but a significant amount of work for not quite as much performance improvement as the original Regex compile-time generator, not least of which is designing how to attach the generated code.
That said, without looking at the relevant code, I imagine that extending compile-time generation to RegularExpressionAttribute should allow for the reuse of much of the work that went into Regex support.
On the other hand, https://learn.microsoft.com/en-us/dotnet/core/extensions/options-validation-generator may emit code that pre-compiles any required Regex objects, which would make pre-compiling the Regex used in the RegularExpressionAttribute but not being used for options validation even more of a niche case.
I imagine that extending compile-time generation to RegularExpressionAttribute should allow for the reuse of much of the work that went into Regex support.
Can you elaborate on how it would work? I don't see a good way currently with the capabilities of source generators today.
It would be really useful if at least Delegate/Action/Func could be used with Attributes, would that be possible ro implement? Than the RegexArtribute could have a Func
It would be really useful if at least Delegate/Action/Func could be used with Attributes, would that be possible ro implement?
Barely. Attributes can only reference method names. Delegates are runtime concepts. Attribute data is purely compile time, so it should be method instead of delegate.
Would running the source generator manually then pasting the output in help?
Strictly, I don't know whether we guarantee that such code will not break in an upgrade (@stephentoub?)
public partial record ARecord { [RegularExpressionAttribute(TheRegex())]
Attributes used at design-time can't have non-trivial classes.
After some thought, if you're willing to use a subclass of RegularExpressionAttribute to have a named regex constraint (I can't see why not, as it should be a net improvement to actually name the purpose of the constraint), you could perhaps do something like:
public class RegularExpressionAttribute
{
// Not ideal because it exposes a requirement for a specific factory method rather than the method group as a whole.
private readonly Func<..., Regex> regexFactory;
public RegularExpressionAttribute([StringSyntax("Regex")] string pattern)
: this((...) => new Regex(..., pattern: pattern))
{
}
public RegularExpressionAttribute(Func<..., Regex> regexFactory)
{
this.regexFactory = regexFactory;
}
private Regex MethodThatBuildsTheRequiredRegex(...)
{
// instead of new Regex(pattern, ...)
var regex = this.regexFactory(...);
}
}
For example, this looks like it would work:
/// <summary>
/// Specifies that a data field must be a valid MQTT identifier.<br />
/// Must consist of characters from the character class [a-zA-Z0-9_-] (alphanumerics, underscore and hyphen).
/// </summary>
public partial class MqttIdentifierAttribute() : RegularExpressionAttribute(MqttIdentifierRegex)
{
[GeneratedRegex(@"^[a-zA-Z0-9_-]+$")]
private static partial Regex MqttIdentifierRegex();
}
Tagging subscribers to this area: @dotnet/area-system-componentmodel-dataannotations See info in area-owners.md if you want to be subscribed.
Wouldn't it be feasible to add an empty constructor and a string Method
property to the attribute (since a string parameter would clash with the already defined pattern constructor)? This would then invoke the method in the same Type.
We could also add a Type parameter to specify the type where to look for the method to invoke (this could also be a constructor of its own).
This way we could do something like:
public partial record ARecord {
[RegularExpressionAttribute(Method = nameof(TheRegex))]
public string NeedsComplexValidation1;
[RegularExpressionAttribute(typeof(AnotherClass), nameof(AnotherClass.AnotherRegexMethod))]
public string NeedsComplexValidation2;
[GeneratedRegex(@"<Insert some complicated regular expression here>")]
private static partial Regex TheRegex();
}
Background and motivation
It would be useful to supply a Regex object to a RegularExpressionAttribute so that you can use a compile-time Regex for data validation.
API Proposal
API Usage
Alternative Designs
Perhaps there would be extra performance gains from generating a RegularExpressionAttribute's Validation method directly?
Risks
No API (that I can spot) on Regex allows specifying matchTimeout for an existing Regex object.