I am using a data annotation like so:
[RequiresAnyRole("Admin", "Member")]
I don't like passing strings as it can be a pain refactoring later but If I try to make an enum like so:
public enum Roles
{
Admin,
Member
}
And then try to cast the enum to string like so:
[RequiresAnyRole(Roles.Admin.ToString(), Roles.Member.ToString())]
Then I get the error:
Error CS0182 An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
My understanding of this error is that it has to be a string at compile time. I have tried to make a static dictionary with the enum as the key but that didn't work either.
What is the best way to pass these values in a way that can be refactored later i.e. not passing in a string but passing in a reference of some sort that can be updated?
This is a great opportunity to use nameof. You can keep your enum, and do:
[RequiresAnyRole(nameof(Roles.Admin), nameof(Roles.Member))]
nameof expressions are constant expressions.
Of course, if you can change the attribute's declaration, you should change it to accept your Roles enum.
Related
This question already has answers here:
An attribute argument must be a constant expression, ...- Create an attribute of type array
(3 answers)
Closed 3 years ago.
I am trying to set the TableAttribute for my view like so:
[Table(LSODatabase.databaseString)]
However the following error is thrown:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.
A workaround I am trying to avoid is making another view and manually setting the strings, however I have a lot of views and would like to avoid this as much as possible.
The error is somewhat self explaining. The arguments you put into your attribute constructors have to be a constant expression, typeof expression or array creation expression. The databaseString field of your LSODatabase isn't constant unless you add the const modifier. Constant means that you can say at compile time what the value of the property will be and that it won't be able to change at runtime.
As the error says, you must use either a constant, a typeof expression or an array creation.
One possible solution would be:
public class LSODatabase
{
public const string databaseString = "myDatabase";
}
Now you can use that as an argument for your attribute.
I've a Custom Attribute which I'm using to authorize the request before it hits the Controller Action, in ASP.NET Core Web API. In the image below, "SecureMethod" is the custom attribute.
I want to add a property in the Custom Attribute, which I want to bind to one of the Route Values, please refer to the image below:
I want to bind "userIDs" route-value to one of the Custom Attribute's property ("UserIDs", this is an example attribute).
Is there any way I can bind the route-value to a custom-attribute's property?
TIA
One way you can do this is by passing the name of the route parameter to the attribute, and examining the route data in the attribute itself. For example:
[SecureMethod(PermissionRequired = AccessPermissionEnum.DeleteUsers, UserIdsKey = "userIds")]
Then something like:
public AccessPermissionEnum PermissionRequired { get; set; }
public string UserIdsKey { get; set; }
public override void OnActionExecuting(ActionExecutingContext context)
{
// Depending on how you pass your userIds to the action, you
// may need to use something else from context to get the data,
// but it's all available in there.
var userIds = context.ActionArguments[UserIdsKey];
// Do something with the ids.
base.OnActionExecuting(context);
}
Whilst this works, and in certain places it works really well, you should be aware that the filter now has intimate knowledge of the parameter being passed in. For the most part, this usually doesn't matter so much, but it's an approach you shouldn't use everywhere because it will end up adding a lot of coupling if not used well. It's a useful tool to use every now and then.
No, it is not possible.
Attribute parameters are restricted to constant values of the following types:
Simple types (bool, byte, char, short, int, long, float, and double)
string
System.Type
enums
object (The argument to an attribute parameter of type object must be
a constant value of one of the above types.)
One-dimensional arrays of any of the above types
You cannot nest attributes and you cannot pass non-constant values to attribute parameter. Even though you can declare an attribute parameter as type object, the actual argument you pass in must still be a constant string, bool, etc (declaring it as an object just means you can use any of these types each time you declare the attribute).
As anyone answering this already knows, parameters of attributes require constant expressions. Optional parameters (for anything, not just attributes) also require constant expressions for their default values.
The (albeit minor) inconvenience I'm having is with RegularExpressionAttribute's pattern parameter. I have dozens of properties in my data-model that use this attribute (found in System.ComponentModel.DataAnnotations), and whenever I make a change to the validation pattern, I have to go back and make that change everryyywherrreee . . . it's really quite annoying.
My question . . .
Is there a .net structure that can be declared, recognized as a constant expression, and then be usable where constant expressions are normally required?
It would be great if I could just declare a RegexPatternForNameProperty = "^[a-zA-Z0-9,.# ]{1,150}$" property somewhere, then just change that one value as needed.
Anything that can be defined as a const can be used in an attribute. So you are still limited to compile-time constants, but you do not have to use string or numeric values directly.
public const string RegexPatternForNameProperty = "^[a-zA-Z0-9,.# ]{1,150}$";
[RegularExpression(RegexPatternForNameProperty)]
public string Name {get; set;}
I'd like to set a DataAnnotation on a view model to a dynamic value that is configurable via the web.config. In the following example I get this error "An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type". Anyone know if this is possible? Thanks
[DataType(DataType.Password)]
[RegularExpression(Properties.Settings.Default.PasswordExpression)]
public string Password { get; set; }
Attribute parameters must be constants, i.e. something whose value can be resolved at compile time. But you could write your own simple Attribute class that took the name of the the item in the appSettings, got the underlying value, and passed that on to the normal RegularExpression processing. Then your attribute would look like this:
[ConfigedRegularExpression("PasswordExpression")]
where PasswordExpression was the name of the app setting containing the actual regular expression string.
and, after writing this and doing a search (I should have done that first), I see someone's worked it out for you here:
How to write custom RegularExpressionValidator which takes the values from the config file?
Using the C# object initializer syntax I can instantiate an anonymous object like this:
object empData = new { name = "bob", age = 30, salary = 100000 };
But what if I have the initializer stored in a string, e.g.:
string init = "{ name = \"bob\", age = 30, salary = 100000 }";
Whats the best way of converting this string into an instance of the object?
Anonymous classes are C# syntactic sugar (see Remarks section here). csc.exe creates a class with private fields and a read/write property with the type inferred from context. All uses of the object are, again, inferred.
What this means is you cannot create an anonymous class at run time because the CLR sees them no differently than any other class (again, because it is C# syntactic sugar).
So instead:
Use a Dictionary<string,object>
Use JSON.NET, XML or something like it that has some already-defined scheme for parsing a string to get an object. This requires the properties be well-defined, however.
Use System.Reflection.Emit to create the type at run-time, but I see no real benefit to this over just a Dictionary<string,object>
I also have concerns of what you're doing because this as a string very likely means to me that you are accepting user input of some kind. Be wary of the security issues in whatever you do.
It's not possible using anonymous types, however you can do it with Reflection Emit using the TypeBuilder class, specifically TypeBuilder.Create(..).
http://msdn.microsoft.com/en-us/library/system.reflection.emit.typebuilder.createtype.aspx
The best way to do it is to use serialization. But of course, that doesn't use the same string format that you described.
Your object initializer does not have to contain constant values.
string myName="bob";
int myAge=30;
double mySalary=100000;
object empData = new { name = myName, age = myAge, salary = mySalary };
So in your scenario you would have to parse out the individual elements from your string, and perform some conversions on them to coerce them into the types you want.
If you are not married to this particular string format, you can serialize and deserialize your objects and accomplish the same thing using XML much more easily.
There's no direct easy way to do so. Basically, you have these options:
Parse the string manually.
Use the C# compiler to compile that object as a part of an assembly and use reflection to figure out the contents.
(not sure if it's possible, C# 4.0 only): Use C# 4.0 managed compiler classes to parse the expression and infer the stuff.
None of which is ideal in my opinion. I'd consider QueryString like pairs, XML or JSON or some other format that has parsers available out there instead if you have the option and consider storing data in a dictionary instance instead of creating an object for every expression.
I don't think this question answers yours per se, but it will tell you why it's not possible to create anonymous instances of objects using strings in a manner such as you suggest:
How do I create and access a new instance of an Anonymous Class passed as a parameter in C#?
Anonymous types don't have any public fields, consequently while you can do this for a named object, it's not so simple to do it for an anonymous type. That said, there's nothing to stop you [as suggested by BFree] using reflection to emit the MSIL for the anonymous type - which isn't exactly straightforward, but isn't impossible either.