Git Product home page Git Product logo

Comments (8)

promontis avatar promontis commented on May 23, 2024 1

@dstelljes good questions!

After giving it some more thought, I don't think I'm going the polymorphism route.
It would work for in C#, but it would be really hard to get this going in say KSQL.

I'm now going to split up every concrete class in its own event. Much simpler and more supported. Feel free to continue on my fork if you want.

from dotnet-avro.

dstelljes avatar dstelljes commented on May 23, 2024

We’ve discussed this internally, and it’s definitely something we eventually want to support.

One option we considered was the “official” KnownType attribute; the data contract resolver already analyzes DataContract and DataMember. My sense is that KnownType may not be a good fit, but I haven’t dug into it enough to know for sure. I’ll try to carve out some time next week to put together a solid proposal.

from dotnet-avro.

promontis avatar promontis commented on May 23, 2024

The 'official' KnownType attribute is ok, but it doesn't allow interfaces. I've currently added a new Attribute which does allow it.

I've also added an InterfaceResolution for interfaces together with an InterfaceResolverCase. The InterfaceResolution has a KnownTypes of Type[].

Furthermore, I've added an InterfaceSchemaBuilderCase which does something like this:

            var innerSchemas = interfaceResolution.KnownTypes
             .Select(x => SchemaBuilder.BuildSchema(x, cache))
             .ToArray();

            var schema = new UnionSchema(innerSchemas);

The schema building seem to work!

I'm now in the process of adding two SerializerBuilderCases. One for the interface and one for the concrete classes.

The interface one has an InterfaceResolution and an UnionSchema. The concrete class one also has an InterfaceResolution, but a RecordSchema instead of UnionSchema.

The biggest challenge is creating the expression, that should dynamically switch to the correct type.

from dotnet-avro.

promontis avatar promontis commented on May 23, 2024

@dstelljes see #19 for initial wip

from dotnet-avro.

dstelljes avatar dstelljes commented on May 23, 2024

Thanks for posting your WIP; that’s helpful. Still mulling over a few questions:

  • How would Chr.Avro determine which record in a union is mapped to which known type? Given a schema [null, "Cat", "Dog"] where Cat and Dog are records, and an interface IAnimal with known implementing classes Cat and Dog, the serde builders have to make some kind of judgment—matching name? largest percentage of matching fields? function provided by some attribute? System.Runtime.Serialization seems to use equivalence.

  • Would we ever want to support interfaces without known concrete classes? For instance, deserializing a record to some interface IAnimal, could the deserializer builder create and map to some runtime-generated class that implemented IAnimal? This seems possible; not sure whether it‘s wise.

from dotnet-avro.

dstelljes avatar dstelljes commented on May 23, 2024

After talking it over with some people internally, we’re going to try to add support for KnownTypeAttribute as it’s described in the WCF docs.

Here’s what I’m thinking: Given this type EventRecord with some KnownType attributes...

[KnownType(typeof(OrderCreation))]
[KnownType(typeof(OrderLineItemModification))]
[KnownType(typeof(OrderCancellation))]
class EventRecord
{
    IEvent Event { get; set; }
    DateTime Timestamp { get; set; }
}

interface IEvent
{
    object Metadata { get; set; }
}

class OrderCreation : IEvent
{
    public IEnumerable<OrderLineItem> LineItems { get; set; }
}

class OrderLineItemModification : IEvent
{
    public int Index { get; set; }
    public OrderLineItem LineItem { get; set; }
}

class OrderCancellation : IEvent
{
    
}

class OrderLineItem
{
    Guid ProductId { get; set; }
    int Quantity { get; set; }
}
  1. Any known types on EventRecord that implement IEvent will be included in the resolution for the Event field.

  2. Known types will cascade, so all of the known types on EventRecord will propagate as its fields are resolved. (All of the known types on EventRecord will be included in the resolution for the Metadata field since they extend object.)

  3. When a resolution contains multiple known types, the binary serde builders will attempt to build serdes for each known type and throw unless exactly one matches. A serializer for EventRecord will build successfully, but building a deserializer will throw—an OrderCancellation record, since it has no fields, could be mapped to any of the known types.

Obviously, this has some drawbacks, but hopefully it’s enough to start collecting feedback.

from dotnet-avro.

dstelljes avatar dstelljes commented on May 23, 2024

One other thought: We could also demonstrate how to make this work by customizing the serde builders. For example, a custom case could resolve the ambiguity above:

public EventDeserializerBuilderCase : BinaryDeserializerBuilderCase
{
    public override Delegate BuildDelegate(TypeResolution resolution, Schema schema, ConcurrentDictionary<(Type, Schema), Delegate> cache)
    {
        if (!(resolution is RecordResolution recordResolution) || recordResolution.Type !== typeof(IEvent))
        {
            throw new UnsupportedTypeException(type);
        }

        if (!(schema is UnionSchema unionSchema))
        {
            throw new UnsupportedSchemaException(schema);
        }

        // map each schema in the union to a concrete type and build a deserializer
    }
}

We could maybe even provide some higher-level cases to streamline this a little.

from dotnet-avro.

dstelljes avatar dstelljes commented on May 23, 2024

Closing for #43, which is how I think we’re going to want to implement this.

from dotnet-avro.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.