Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alias does not seem to work during serialization after moving code - still referring types using full name #8922

Closed
SoucianceEqdamRashti opened this issue Mar 23, 2024 · 9 comments

Comments

@SoucianceEqdamRashti
Copy link

I have records defining my types in Orleans 7.2.1 like this. As you can see I have an Alias attribute to use that instead of the full name.

namespace TIP.AutomationCenter.Orleans.Business.Grains.Contracts.Country;

[Alias("CountryTriggerConditionRecordType")]
[GenerateSerializer]
public sealed record CountryTriggerConditionRecord : TriggerConditionBaseRecord
{ 
....
}

Now, before my change the namespace was TIP.AutomationCenter.Orleans.Api.Grains.Contracts.Country and now it is TIP.AutomationCenter.Orleans.Business.Grains.Contracts.Country and the types are moved to a library project.

I deployed the code and tried it out but when looking at the error I get, it seems to use the full name to reference types? Shouldn't it be using the alias which I include in the record?

This is the actual state data from Azure table storage and as you can see the full namespace is used:

{"$id":"1","$type":"TIP.AutomationCenter.Orleans.Api.Grains.Contracts.ActionReason.ActionReasonTriggerConditionRecord, TIP.AutomationCenter.Orleans.Api","ActionReasonType":1,"EntityId":"6d76f079-c30e-4ea1-b347-e0e0578953e3","IsActive":true,"AutomationRuleId":"8376d57d-f20f-4813-b0d2-08db46395112","CustomerId":"6d76f079-c30e-4ea1-b347-e0e0578953e3","ActionType":{"$id":"2","$type":"TIP.AutomationCenter.Orleans.Api.Grains.Contracts.Conditions.DefaultConditionRecord, TIP.AutomationCenter.Orleans.Api"}}

And here is the error itself.

Exc level 0: System.AggregateException: Unable to convert from storage format GrainStateEntity.Data=System.Byte[] (Error resolving type specified in JSON 'TIP.AutomationCenter.Orleans.Api.Grains.Contracts.Status.StatusTriggerConditionRecord, TIP.AutomationCenter.Orleans.Api'. Path '$type', line 1, position 140.) at Orleans.Storage.AzureTableGrainStorage.ConvertFromStorageFormat[T](TableEntity entity) in /_/src/Azure/Orleans.Persistence.AzureStorage/Providers/Storage/AzureTableStorage.cs:line 370 at Orleans.Storage.AzureTableGrainStorage.ReadStateAsync[T](String grainType, GrainId grainId, IGrainState1 grainState)
at Orleans.Core.StateStorageBridge1.ReadStateAsync() in /_/src/Orleans.Runtime/Storage/StateStorageBridge.cs:line 72 Exc level 1: Newtonsoft.Json.JsonSerializationException: Error resolving type specified in JSON 'TIP.AutomationCenter.Orleans.Api.Grains.Contracts.Status.StatusTriggerConditionRecord, TIP.AutomationCenter.Orleans.Api'. Path '$type', line 1, position 140. at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String qualifiedTypeName) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) at Orleans.Storage.JsonGrainStorageSerializer.Deserialize[T](BinaryData input) in /_/src/Orleans.Core/Providers/StorageSerializer/JsonGrainStorageSerializer.cs:line 35 at Orleans.Storage.AzureTableGrainStorage.ConvertFromStorageFormat[T](TableEntity entity) in /_/src/Azure/Orleans.Persistence.AzureStorage/Providers/Storage/AzureTableStorage.cs:line 341 Exc level 2: Newtonsoft.Json.JsonSerializationException: Could not find type 'TIP.AutomationCenter.Orleans.Api.Grains.Contracts.Status.StatusTriggerConditionRecord' in assembly 'TIP.AutomationCenter.Orleans.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. at Newtonsoft.Json.Serialization.DefaultSerializationBinder.GetTypeFromTypeNameKey(StructMultiKey2 typeNameKey)
at Orleans.Serialization.OrleansJsonSerializationBinder.BindToType(String assemblyName, String typeName) in /_/src/Orleans.Core/Serialization/OrleansJsonSerializationBinder.cs:line 29
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String qualifiedTypeName) Unable to convert from storage format GrainStateEntity.Data=System.Byte[] (Error resolving type specified in JSON 'TIP.AutomationCenter.Orleans.Api.Grains.Contracts.Status.StatusTriggerConditionRecord, TIP.AutomationCenter.Orleans.Api'. Path '$type', line 1, position 140.) Error resolving type specified in JSON 'TIP.AutomationCenter.Orleans.Api.Grains.Contracts.Status.StatusTriggerConditionRecord, TIP.AutomationCenter.Orleans.Api'. Path '$type', line 1, position 140. Could not find type 'TIP.AutomationCenter.Orleans.Api.Grains.Contracts.Status.StatusTriggerConditionRecord' in assembly 'TIP.AutomationCenter.Orleans.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. `

I don't understand why it should use the full name when alias are put on top of the records. Or have I missunderstood or use this in the wrong way?

Thanks

@SoucianceEqdamRashti
Copy link
Author

@ReubenBond , I believe the issue here is that we have moved our types to a class library. Now Orleans cannot find the types in the assembly anymore even though the main project references the class library. Do we need to explicitly add the class library assembly similar to what has been mentioned in other issues like [assembly: GenerateCodeForDeclaringAssembly(typeof(SomeTypeInMyOtherAssembly))] . We are on version 7.2.1 and the only change we have done in our code is move the grain types (records) from the main project to a class library. All the types of have generate serializer and alias attributes.

@ledjon-behluli
Copy link
Contributor

@SoucianceEqdamRashti the issue is not moving types to a class lib, the issue is that Alias doesn't apply to the OrleansJsonSerializer

@SoucianceEqdamRashti
Copy link
Author

@ledjon-behluli , can you elaborate on that? Are you saying I need to explicitly add the new NewtonSoft.Json package to the class library?

To give more context, in the initial setup, everything existed in one project and this worked fine. We created creates in Azure table storage and no issues there. Then we decided to move the models to a class library and that's when we got the issue of the types not being found and also codec not found for a json serializer exception.

@ledjon-behluli
Copy link
Contributor

@SoucianceEqdamRashti maybe we are talking about two different things, but can you tell me if in the "$type" property, stored in table storage, you had the fully qualified name or not, i mean in the previous setup you had.

@ledjon-behluli
Copy link
Contributor

Ah, i looked at your initial comment and as you can see the FQN is stored in $type. See: $type":"TIP.AutomationCenter.Orleans.**Api**.Grains.Contracts.ActionReason.ActionReasonTriggerConditionRecord.

Now the namespace changed to: TIP.AutomationCenter.Orleans.**Business**.Grains.Contracts.Country

@SoucianceEqdamRashti
Copy link
Author

@ledjon-behluli , yeah the namespace changed initially.

But I also tried to keep the same namespace in the new class library but that didn't make any difference. Got the same error as above. My assumption is that with Alias it shouldn't use the full name to access the types?

@ledjon-behluli
Copy link
Contributor

@SoucianceEqdamRashti I dont know about keeping the namespace the same, i would assume it would work (regardless of Alias or not), in the otherhand i dont know if 2 types with the same namespace can be deserialized if assembly info is taking into account or not. You could try this with a POC project and just use Newtonsoft.Json to try it out.

Regarding Alias in the context of storage: no it doesnt work! Kinda...See the Alias attribute is used with the Orleans serializer (the default RPC is done with), not with others! And the json serializer (deafult one for storage) doesnt take that into account (so far). You could use the orleans serializer for storage too, and it should work then. I am not sure if you want to do that, since you'll gain this ability but loose readability, and in-place modification...potentially other benefits.

Hope this helps.

@SoucianceEqdamRashti
Copy link
Author

Hmm, thanks for the clarification. Perhaps we will just keep the code in one project for now until some of these serializer parts become easier. Thanks again. I will though try with a POC and if anything new comes up I will post it here. Can close this issue for now.

@ledjon-behluli
Copy link
Contributor

I will though try with a POC and if anything new comes up I will post it here

Please, and @ me if you dont mind

@github-actions github-actions bot locked and limited conversation to collaborators Apr 27, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants