Skip to content
This repository has been archived by the owner on Jan 29, 2023. It is now read-only.

Latest commit

 

History

History
52 lines (35 loc) · 3.29 KB

AnonymousTypes.md

File metadata and controls

52 lines (35 loc) · 3.29 KB

Anonymous Types

Anonymous types like var x = new { Test = "Cat" } end up being types generated by the compiler. They're also used a lot in LINQ (both written by developers and generated by compilers when using the query syntax). However these types will only exist in the client assembly, the server will not understand them so we need a way of transmitting the same data structure to the server.

Our approach is to convert hidden anonymous types to tuples. See implementation in AnonymousTypeRewriter.cs.

  • Find any anonymous type
  • Select an appropriate tuple
  • Get a constructor, build an anonymous type to tuple delegate
  • Visit NewExpr, ConstantExpr, LambdaExpr, MethodCallExpr, MemberExpr, ParameterExpr

🤔 And to do something with visible anonymous types.

Limitations

  • Anonymous types with more than seven properties are not yet supported. (I need to support using the 'Rest' parameter.)
  • Empty anonymous types are not yet supported. (I need to support some kind of unit type.)
  • Outer results can't contain anonymous types right now. (I need to retain visible anonymous types, somehow.)

Investigation

Dealing with visible anonymous types

Some anonymous types used in the query might actually be visible, as in the following query:

var query = client.GetEnumerable<SharePrice>("SharePrices")
    .Where(sp => sp.Symbol == "MSFT")


    .SelectMany(sp => client.GetEnumerable<SharesOutstanding>("SharesOutstanding")
        .Where(so => so.Symbol == sp.Symbol)
        .Select(so => new { so.Symbol, MarketCap = sp.Price * so.Count })) // hidden

    .SelectMany(mc => client.GetEnumerable<Listing>("Listings")
        .Where(ls => ls.Symbol == mc.Symbol)
        .Select(ls => new { mc.Symbol, ls.Name, mc.MarketCap } )); // visible

// IAsyncQueryable<'a> query
// 'a is new { string Symbol, string Name, decimal  MarketCap }

While we can get rid of hidden anonymous types by converting them to tuples, we really need to preserve the structure of the visible anonymous types as that's what the client is expecting.

Options:

  • Add some converter during de/serialization which takes these tuples and jams them back in to the appropriate anonymous type.

    There's a task to replace the existing de/serialization stuff anyway, so we could add a converter which does it. I think the main issue will be that the actual type information is dropped during rewriting with the current design. (We at least need the property names to be retained in order to identify the correct anonymous type, ideally the full type name.)

  • Transmit the actual structure of the anonymous type in the query and (runtime) generate the type on the server.

    I think it'd have to involve extending all of the Expression types (like Serialize.Linq does with ExpressionNode) to include an extension of the Type type which can actually hold the structure (i.e. the names and types of the properties) of an anonymous type. The server could then TypeBuilder that structure into types we can use at runtime? I don't even know if this is possible/practical, particularly with .NET Core and no AppDomains. (Maybe it can just be in the same AppDomain as they're just types with read-only properties?) We'd still probably keep the existing anonymous type rewriting into tuples to reduce the number of types we'd have to generate.