英文:
Constant Expression Required for Attribute Constructor
问题
I'm attempting to implement encrypted JSON properties using code from the question Using Json.NET, how can I encrypt selected properties of any type when serializing my objects?. I had originally set up a few "keys" as constants (Const
) in my application, but I recently had a thought of using another, more unique value to apply this encryption - a token that the user will be required to enter during the initial setup of the application. However, when I tried to change to using that token instead of my original keys, I'm encountering the Constant expression is required
message.
The original, working code for flagging the JSON properties for encryption looks like this:
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyEndpoint
<JsonProperty("endpoint")> <JsonEncrypt(EndpointKey)>
Public Property Endpoint As Endpoint
End Class
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyConnectionString
<JsonProperty("connection_string")> <JsonEncrypt(ConnectionStringKey)>
Public Property ConnectionString As ConnectionString
End Class
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyUserCredential
<JsonProperty("credentials")> <JsonEncrypt(CredentialKey)>
Public Property Credential As Credential
End Class
I have the JsonEncryptAttribute
class like this:
<AttributeUsage(AttributeTargets.[Property] Or AttributeTargets.Field, AllowMultiple:=False)>
Public NotInheritable Class JsonEncryptAttribute
Inherits Attribute
Public Property EncryptionKey As Byte()
Public Sub New()
Me.EncryptionKey = GenerateKey(DefaultKey)
End Sub
Public Sub New(ByVal Password As String)
Me.EncryptionKey = GenerateKey(Password)
End Sub
Private Function GenerateKey(ByVal Password As String) As Byte()
Return SHA256.HashData(Encoding.UTF8.GetBytes(Password))
End Function
End Class
However, when I change the MyEndpoint
class definition to look like this:
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyEndpoint
<JsonProperty("endpoint")> <JsonEncrypt(Settings.AppTokens("myapp"))>
Public Property Endpoint As Endpoint
End Class
...where AppTokens
is a property in the Settings
class that will be saved and read separately, the IDE balks and gives me the Constant expression is required
message. Based on the constructor for the JsonEncryptAttribute
class, I'm completely confused about why it would require the value to be a Const
in the first place.
However, looking at the documentation, I figure the error has to do with the fact that the AppTokens
(which is defined as a Dictionary(Of String, String)
) is the most likely culprit:
> A Const
statement does not properly initialize a constant, or an array declaration uses a variable to specify the number of elements.
>
> Error ID: BC30059
>
> ## To correct this error
> If the declaration is a Const
statement, check to make sure the constant is initialized with a literal, a previously declared constant, an enumeration member, or a combination of literals, constants, and enumeration members combined with operators.
>
> If the declaration specifies an array, check to see if a variable is being used to specify the number of elements. If so, replace the variable with a constant expression.
I looked at the self-accepted answer to another question - Variable in enum attribute : constant expression is required - and tried to implement something similar but, unless I'm just doing it wrong (a definite possibility), it doesn't seem to work with class properties.
<JsonEncrypt(GetType(Settings), NameOf(Settings.AppTokens("myapp")))>
So, now I'm left with the following question(s):
-
Can anyone explain to me why this error is occurring here? Does it have something to do with the
JsonEncryptAttribute
's inheritance of the baseSystem.Attribute
? -
Is there a way to work around this by using a different object type or something? The
AppTokens
is read from a "base" JSON configuration file for the application, so whatever alternative would have to be compatible with that.
英文:
I'm attempting to implement encrypted JSON properties using code from the question Using Json.NET, how can I encrypt selected properties of any type when serializing my objects?. I had originally set up a few "keys" as constants (Const
) in my application, but I recently had a thought of using another, more unique value to apply this encryption - a token that the user will be required to enter during the initial setup of the application. However, when I tried to change to using that token instead of my original keys, I'm encountering the Constant expression is required
message.
The original, working code for flagging the JSON properties for encryption looks like this:
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyEndpoint
<JsonProperty("endpoint")> <JsonEncrypt(EndpointKey)>
Public Property Endpoint As Endpoint
End Class
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyConnectionString
<JsonProperty("connection_string")> <JsonEncrypt(ConnectionStringKey)>
Public Property ConnectionString As ConnectionString
End Class
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyUserCredential
<JsonProperty("credentials")> <JsonEncrypt(CredentialKey)>
Public Property Credential As Credential
End Class
I have the JsonEncryptAttribute
class like this:
<AttributeUsage(AttributeTargets.[Property] Or AttributeTargets.Field, AllowMultiple:=False)>
Public NotInheritable Class JsonEncryptAttribute
Inherits Attribute
Public Property EncryptionKey As Byte()
Public Sub New()
Me.EncryptionKey = GenerateKey(DefaultKey)
End Sub
Public Sub New(ByVal Password As String)
Me.EncryptionKey = GenerateKey(Password)
End Sub
Private Function GenerateKey(ByVal Password As String) As Byte()
Return SHA256.HashData(Encoding.UTF8.GetBytes(Password))
End Function
End Class
However, when I change the MyEndpoint
class definition to look like this:
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyEndpoint
<JsonProperty("endpoint")> <JsonEncrypt(Settings.AppTokens("myapp"))>
Public Property Endpoint As Endpoint
End Class
...where AppTokens
is a property in the Settings
class that will be saved and read separately, the IDE balks and gives me the Constant expression is required
message. Based on the constructor for the JsonEncryptAttribute
class, I'm completely confused about why it would require the value to be a Const
in the first place.
However, looking at the documentation, I figure the error has to do with the fact that the AppTokens
(which is defined as a Dictionary(Of String, String)
) is the most likely culprit:
> A Const
statement does not properly initialize a constant, or an array declaration uses a variable to specify the number of elements.
>
> Error ID: BC30059
>
> ## To correct this error
> If the declaration is a Const
statement, check to make sure the constant is initialized with a literal, a previously declared constant, an enumeration member, or a combination of literals, constants, and enumeration members combined with operators.
>
> If the declaration specifies an array, check to see if a variable is being used to specify the number of elements. If so, replace the variable with a constant expression.
I looked at the self-accepted answer to another question - Variable in enum attribute : constant expression is required - and tried to implement something similar but, unless I'm just doing it wrong (a definite possibility), it doesn't seem to work with class properties.
<JsonEncrypt(GetType(Settings), NameOf(Settings.AppTokens("myapp")))>
So, now I'm left with the following question(s):
-
Can anyone explain to me why this error is occurring here? Does it have something to do with the
JsonEncryptAttribute
's inheritance of the baseSystem.Attribute
? -
Is there a way to work around this by using a different object type or something? The
AppTokens
is read from a "base" JSON configuration file for the application, so whatever alternative would have to be compatible with that.
答案1
得分: 0
我相信我已经找到了一个可行的解决方案,其中我可以使用令牌作为加密密钥。只需要进行一些小的重构和重新组织我之前的内容。在阅读如何在我的属性中设置动态值之后,我将先前定义的EndpointKey
、ConnectionStringKey
和CredentialKey
值作为独立的Const
字段转换为枚举:
Public Enum ApplicationKeys
UserToken
<Description("Default Encryption Key")> DefaultKey
<Description("Encrypted Setting Key")> Setting
<Description("Encrypted ConnectionString Key")> ConnectionString
<Description("Encrypted Endpoint Key")> Endpoint
<Description("Encrypted Credential Key")> Credential
End Enum
然后,我为JsonEncryptAttribute
设置了一个新的构造函数,该构造函数接受此枚举作为参数:
Public Sub New(ByVal Key As ApplicationKeys)
If Key = ApplicationKeys.UserToken Then
Me.EncryptionKey = GenerateKey(Settings.AppTokens("myapp"))
Else
Me.EncryptionKey = GenerateKey(DirectCast([Enum].Parse(GetType(ApplicationKeys), Key), ApplicationKeys).GetDescription)
End If
End Sub
这使用了GetDescription
扩展方法†来获取“预定义”键值,以便在任何我可能想要使用它们的地方,但也使我能够在选择该枚举时引入动态令牌。
显然,我需要在那里添加一些额外的错误处理,以处理令牌可能为Nothing
或空字符串的情况,或者如果传递给构造函数的值未定义为ApplicationKeys
枚举的一部分,但我相信这将提供我真正想要实现的解决方案。
† 仅为帮助将来可能查看此内容的其他人,这是GetDescription
扩展方法:
''' <summary>
''' Gets the Description attribute of an Enum value
''' </summary>
''' <param name="CurrentEnum">The specific Enum value for which to get the Description attribute</param>
''' <returns>String value representing the Description attribute of the specified Enum</returns>
''' <remarks>Requires System.Reflection, System.ComponentModel and System.Runtime.CompilerServices namespaces to be imported.
''' The Description attribute of an Enum is set in the Enum declaration.</remarks>
<DebuggerStepThrough>
<Extension()>
Public Function GetDescription(ByVal CurrentEnum As [Enum]) As String
Dim DescAttribute As DescriptionAttribute
Dim Field As FieldInfo = CurrentEnum.GetType.GetField(CurrentEnum.ToString)
DescAttribute = DirectCast(Attribute.GetCustomAttribute(Field, GetType(DescriptionAttribute)), DescriptionAttribute)
If DescAttribute IsNot Nothing Then
Return DescAttribute.Description
Else
Return CurrentEnum.ToString
End If
End Function
英文:
I believe I've found a workable solution where I can use the token as the encryption key. It just requires a small bit of refactoring and restructuring what I had. After reading How to set dynamic value in my Attribute, I took the EndpointKey
, ConnectionStringKey
, and CredentialKey
values I had previously defined as individual Const
fields and converted them to an enum:
Public Enum ApplicationKeys
UserToken
<Description("Default Encryption Key")> DefaultKey
<Description("Encrypted Setting Key")> Setting
<Description("Encrypted ConnectionString Key")> ConnectionString
<Description("Encrypted Endpoint Key")> Endpoint
<Description("Encrypted Credential Key")> Credential
End Enum
Then, I set up a new constructor for the JsonEncryptAttribute
that accepts this enum as a parameter:
Public Sub New(ByVal Key As ApplicationKeys)
If Key = ApplicationKeys.UserToken Then
Me.EncryptionKey = GenerateKey(Settings.AppTokens("myapp"))
Else
Me.EncryptionKey = GenerateKey(DirectCast([Enum].Parse(GetType(ApplicationKeys), Key), ApplicationKeys).GetDescription)
End If
End Sub
This uses the GetDescription
extension method† to pull the "pre-defined" key values for anywhere I might want to use them, but also enables me to draw in the dynamic token if that enum is selected.
Obviously, I'll need to put some additional error handling in there for the possibility that the token is Nothing
or an empty string, or if the value passed to the constructor isn't defined as a part of the ApplicationKeys
enumeration, but I believe this will provide the solution I was really trying to achieve.
† Just to help out anyone else who might be looking at this in the future, here's the GetDescription
extension method:
''' <summary>
''' Gets the Description attribute of an Enum value
''' </summary>
''' <param name="CurrentEnum">The specific Enum value for which to get the Description attribute</param>
''' <returns>String value representing the Description attribute of the specified Enum</returns>
''' <remarks>Requires System.Reflection, System.ComponentModel and System.Runtime.CompilerServices namespaces to be imported.
''' The Description attribute of an Enum is set in the Enum declaration.'</remarks>
<DebuggerStepThrough>
<Extension()>
Public Function GetDescription(ByVal CurrentEnum As [Enum]) As String
Dim DescAttribute As DescriptionAttribute
Dim Field As FieldInfo = CurrentEnum.GetType.GetField(CurrentEnum.ToString)
DescAttribute = DirectCast(Attribute.GetCustomAttribute(Field, GetType(DescriptionAttribute)), DescriptionAttribute)
If DescAttribute IsNot Nothing Then
Return DescAttribute.Description
Else
Return CurrentEnum.ToString
End If
End Function
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论