Constant Expression Required for Attribute Constructor

huangapple go评论49阅读模式
英文:

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):

  1. Can anyone explain to me why this error is occurring here? Does it have something to do with the JsonEncryptAttribute's inheritance of the base System.Attribute?

  2. 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):

  1. Can anyone explain to me why this error is occurring here? Does it have something to do with the JsonEncryptAttribute's inheritance of the base System.Attribute?

  2. 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

我相信我已经找到了一个可行的解决方案,其中我可以使用令牌作为加密密钥。只需要进行一些小的重构和重新组织我之前的内容。在阅读如何在我的属性中设置动态值之后,我将先前定义的EndpointKeyConnectionStringKeyCredentialKey值作为独立的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
    &lt;Description(&quot;Default Encryption Key&quot;)&gt; DefaultKey
    &lt;Description(&quot;Encrypted Setting Key&quot;)&gt; Setting
    &lt;Description(&quot;Encrypted ConnectionString Key&quot;)&gt; ConnectionString
    &lt;Description(&quot;Encrypted Endpoint Key&quot;)&gt; Endpoint
    &lt;Description(&quot;Encrypted Credential Key&quot;)&gt; 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(&quot;myapp&quot;))
    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:

&#39;&#39;&#39; &lt;summary&gt;
&#39;&#39;&#39; Gets the Description attribute of an Enum value
&#39;&#39;&#39; &lt;/summary&gt;
&#39;&#39;&#39; &lt;param name=&quot;CurrentEnum&quot;&gt;The specific Enum value for which to get the Description attribute&lt;/param&gt;
&#39;&#39;&#39; &lt;returns&gt;String value representing the Description attribute of the specified Enum&lt;/returns&gt;
&#39;&#39;&#39; &lt;remarks&gt;Requires System.Reflection, System.ComponentModel and System.Runtime.CompilerServices namespaces to be imported.
&#39;&#39;&#39; The Description attribute of an Enum is set in the Enum declaration.&#39;&lt;/remarks&gt;
&lt;DebuggerStepThrough&gt;
&lt;Extension()&gt;
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

huangapple
  • 本文由 发表于 2023年5月7日 13:08:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76192271.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定