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

[feature request] Orleans integration #385

Closed
newbe36524 opened this issue Apr 6, 2023 · 7 comments · Fixed by #653
Closed

[feature request] Orleans integration #385

newbe36524 opened this issue Apr 6, 2023 · 7 comments · Fixed by #653
Labels
enhancement New feature or request

Comments

@newbe36524
Copy link

newbe36524 commented Apr 6, 2023

go to: #385 (comment)


Describe the feature

Support to be serialize with Microsoft/orleans

see: dotnet/orleans#8374

Option 1

Support to declare custom constructor, then GenerateSerializer can works well in orleans.

[ValueObject(typeof(ulong))]
[GenerateSerializer]
public partial struct Nonce
{
    public Nonce(ulong value)
    {
        this._value = value;
    }
    private static Validation Validate(ulong input)
    {
        return Validation.Ok;
    }
}

Option 2

if

[ValueObject(conversions: Conversions.NewtonsoftJson | Conversions.OrleansGenerateSerializer, underlyingType: typeof(ulong))]
[GenerateSerializer]
public partial struct Nonce
{
    private static Validation Validate(ulong input)
    {
        return Validation.Ok;
    }
}

then generate

public partial struct Nonce
{
    [Orleans.Id(0)]
    private readonly ulong _value
}

Option 3

surrogates-for-serializing-foreign-types
<https://learn.microsoft.com/en-us/dotnet/orleans/host/configuration-guide/serialization?pivots=orleans-7-0#surrogates-for-serializing-foreign-types >

if

[ValueObject(conversions: Conversions.NewtonsoftJson | Conversions.OrleansGenerateSerializer, underlyingType: typeof(ulong))]
public partial struct Nonce
{
    private static Validation Validate(ulong input)
    {
        return Validation.Ok;
    }
}

then generate

public partial struct Nonce
{
    [GenerateSerializer]
    public struct NonceSurrogate
    {
        [Id(0)]
        public ulong Value;
    }
    
    // This is a converter that converts between the surrogate and the foreign type.
    [RegisterConverter]
    public sealed class NonceSurrogateConverter :
        IConverter<Nonce, NonceSurrogate>
    {
        public Nonce ConvertFromSurrogate(
            in NonceSurrogate surrogate) =>
            new(surrogate.Value);
    
        public NonceSurrogate ConvertToSurrogate(
            in Nonce value) =>
            new()
            {
                Value = value. Value,
            };
    }
}
@newbe36524 newbe36524 added the enhancement New feature or request label Apr 6, 2023
@newbe36524 newbe36524 changed the title Orleans integration [feature request] Orleans integration Apr 6, 2023
@cmeyertons
Copy link

Is this one up for grabs?

I really like what this library is doing but am missing this piece and the Mongo BSON conversion.

Happy to contribute!

@newbe36524
Copy link
Author

As a result, I choose to clone source code Vogen and choose

option 4:

remove fields from Vogen SG code like _value and _init and type them in my source code as below:

[ValueObject<ulong>(conversions: Conversions.SystemTextJson)]
[GenerateSerializer]
[Immutable]
public readonly partial struct UserId
{
    [Id(0)] private readonly bool _isInitialized;
    [Id(1)] private readonly ulong _value;

    private static Validation Validate(ulong input)
    {
        return Validation.Ok;
    }
}

Reason:

Orleans.Sdk cannot generate serializer code from Vogen SG code, so option 2 & 3 do not work.
And option 1 is actually option 4 now.

So the best way I think to integrate Vogen and Orleans like projects, is to support for developer to type fields in their source code instead of generate code in SG. It need some detection to know there is or not self-type fields, if so, do not generate code in Vogen

@newbe36524 newbe36524 changed the title [feature request] Orleans integration [feature request] Support to detect self-type fields in user-end May 4, 2023
@cmeyertons
Copy link

cmeyertons commented May 4, 2023

I think we should rename this issue back to Orleans support rather than the new name?

I think instead of using GenerateSerializer and exposing the Vogen private fields etc, we should follow the current pattern of Vogen owning the serialization logic. Because Vogen applies many constraints on the ValueObject, we can safely assume that there is one and only one field on the object that needs serialization and can make the value object itself a serializer.

Here's a string example, but you can see this could be compiled to support the other primitive types Vogen supports.

[ValueObject<string>]
[Immutable, RegisterSerializer]
public readonly partial record struct ValueObjectTest : IValueSerializer<ValueObjectTest>, IFieldCodec<ValueObjectTest>
{
    [GenerateSerializer]
    public readonly record struct Surrogate(string Value);

    public void WriteField<TBufferWriter>(ref Writer<TBufferWriter> writer, uint fieldIdDelta, Type expectedType, ValueObjectTest value)
        where TBufferWriter : IBufferWriter<byte>
    {
        StringCodec.WriteField(ref writer, fieldIdDelta, value.Value);
    }

    public ValueObjectTest ReadValue<TInput>(ref Reader<TInput> reader, Field field)
    {
        var value = StringCodec.ReadValue(ref reader, field);
        return From(value);
    }

    public void Serialize<TBufferWriter>(ref Writer<TBufferWriter> writer, scoped ref ValueObjectTest value) where TBufferWriter : IBufferWriter<byte>
    {
        StringCodec.WriteField(ref writer, 0, value.Value);
    }

    public void Deserialize<TInput>(ref Reader<TInput> reader, scoped ref ValueObjectTest value)
    {
        var header = reader.ReadFieldHeader();
        var stringValue = StringCodec.ReadValue(ref reader, header);
        value = From(stringValue);
    }
}

I think Vogen could support decorating the value object with RegisterSerializer and Immutable attribute and the IValueSerializer interface along w/ its code get automatically added.

@newbe36524 newbe36524 changed the title [feature request] Support to detect self-type fields in user-end [feature request] Orleans integration May 5, 2023
@newbe36524
Copy link
Author

I think we should rename this issue back to Orleans support rather than the new name?

I think instead of using GenerateSerializer and exposing the Vogen private fields etc, we should follow the current pattern of Vogen owning the serialization logic. Because Vogen applies many constraints on the ValueObject, we can safely assume that there is one and only one field on the object that needs serialization and can make the value object itself a serializer.

Here's a string example, but you can see this could be compiled to support the other primitive types Vogen supports.

[ValueObject<string>]
[Immutable, RegisterSerializer]
public readonly partial record struct ValueObjectTest : IValueSerializer<ValueObjectTest>, IFieldCodec<ValueObjectTest>
{
    [GenerateSerializer]
    public readonly record struct Surrogate(string Value);

    public void WriteField<TBufferWriter>(ref Writer<TBufferWriter> writer, uint fieldIdDelta, Type expectedType, ValueObjectTest value)
        where TBufferWriter : IBufferWriter<byte>
    {
        StringCodec.WriteField(ref writer, fieldIdDelta, value.Value);
    }

    public ValueObjectTest ReadValue<TInput>(ref Reader<TInput> reader, Field field)
    {
        var value = StringCodec.ReadValue(ref reader, field);
        return From(value);
    }

    public void Serialize<TBufferWriter>(ref Writer<TBufferWriter> writer, scoped ref ValueObjectTest value) where TBufferWriter : IBufferWriter<byte>
    {
        StringCodec.WriteField(ref writer, 0, value.Value);
    }

    public void Deserialize<TInput>(ref Reader<TInput> reader, scoped ref ValueObjectTest value)
    {
        var header = reader.ReadFieldHeader();
        var stringValue = StringCodec.ReadValue(ref reader, header);
        value = From(stringValue);
    }
}

I think Vogen could support decorating the value object with RegisterSerializer and Immutable attribute and the IValueSerializer interface along w/ its code get automatically added.

  1. It is a good idea.
  2. I have no time to take care of this issue, I believe you can make a PR about this.
  3. Actually, I think the better idea is to support expose field by developer because there should be some lib that only support to mark attribute onto fields, e.g. messagepack serializer, cbor serializer and etc. It could be more flexible, if developers can mark attributes onto fields.

@cmeyertons
Copy link

It sounds like we have two separate issues to track here then:

  1. Enhance source generator to not generate fields if they are in user-land (this will help Orleans but is not necessarily Orleans specific)
  2. Add Orleans support for [RegisterSerializer] above to generate IValueSerializer (for structs) and IFieldCodec implementations

Strictly for the Orleans use case, I prefer option 2, it results in cleaner, less duplicative code that doesn't "know" the internals of the ValueObject.

@cmeyertons
Copy link

Also, I can take this one on!

@SteveDunn
Copy link
Owner

Hi - sorry for the delay in replying. Very happy to accept any contributions. It would be great to enable everyone to use Vogen in Orleans while still having all of the current constraints that ensure safety.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants