Code Monkey home page Code Monkey logo

aqua-core's Introduction

aqua-core

GitHub license Github Workflow

branch AppVeyor Travis CI Codecov.io Codacy CodeFactor
main AppVeyor Build Status Travis Build Status codecov Codacy Badge CodeFactor
package nuget myget
aqua-core NuGet Badge MyGet Pre Release
aqua-core-newtonsoft-json NuGet Badge MyGet Pre Release
aqua-core-protobuf-net NuGet Badge MyGet Pre Release
aqua-core-text-json NuGet Badge MyGet Pre Release

Transform any object-graph into a dynamic, composed dictionaries like structure, holding serializable values and type information.

Aqua-core provides a bunch of serializable classes:

  • DynamicObject
  • TypeInfo
  • FieldInfo
  • PropertyInfo
  • MethodInfo
  • ConstructorInfo

Any object graph may be translated into a DynamicObject structure and back to it's original type using DynamicObjectMapper.

Sample

Mapping an object graph into a DynamicObject and then back to it's original type

Blog blog = new Blog
{
    Title = ".NET Blog",
    Description = "A first-hand look from the .NET engineering teams",
    Posts = new[]
    {
        new Post
        {
            Title = "Announcing .NET Core 1.0",
            Date = new DateTime(2016, 6, 27),
            Author = "rlander"
            Text = "We are excited to announce the release of .NET Core 1.0, ASP.NET Core 1.0 and " +
               "Entity Framework Core 1.0, available on Windows, OS X and Linux! " +
               ".NET Core is a cross-platform, open source, and modular .NET platform [...]",
        },
        new Post
        {
            Title = "Happy 15th Birthday .NET!",
            Date = new DateTime(2017, 2, 13),
            Author = "bmassi",
            Text = "Today marks the 15th anniversary since .NET debuted to the world [...]",
        }
    }
}

DynamicObject dynamicObject = new DynamicObjectMapper().MapObject(blog);

Blog restoredBlog = new DynamicObjectMapper().Map(dynamicObject) as Blog;

aqua-core's People

Contributors

6bee avatar azabluda avatar dependabot[bot] avatar karlox2 avatar mircotamburini avatar thompson-tomo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

aqua-core's Issues

Mapping of Nullable<Enum>

If I change the type of EnumProperty in Aqua.Tests.Dynamic.DynamicObject.When_converting_to_object_with_enum.ClassWithEnum to CustomEnum? then the tests would no longer pass. There seems to be a problem with enum values stored as string and int.

VB Anonymous types not supported

The anonymous types are identified using the class name, which always starts with "<>Anonymous" in C#, but in VB.Net the name starts with "VB$Anonymous".

DateTimeOffset milliseconds are swallowed with FormatPrimitiveTypesAsString

var mapperSettings = new DynamicObjectMapperSettings { FormatPrimitiveTypesAsString = true };
var mapper = new DynamicObjectMapper(mapperSettings);

var dt1 = new DateTime(2, 1, 2, 10, 0, 0, 300);
var dtDynamic = mapper.MapObject(dt1);
var dt2 = mapper.Map<DateTime>(dtDynamic);
dt2.ShouldBe(dt1); // [PASS]

var dto1 = new DateTimeOffset(2, 1, 2, 10, 0, 0, 300, new TimeSpan(1, 30, 0));
var dtoDynamic = mapper.MapObject(dto1);
var dto2 = mapper.Map<DateTimeOffset>(dtoDynamic);
dto2.ShouldBe(dto1); // [FAIL] "0002-01-02T10:00:00.0000000+01:30" != "0002-01-02T10:00:00.3000000+01:30"

Culture mismatch: FormatNativeTypeAsString vs Serializers.

After fca4dda FormatNativeTypeAsString uses InvariantCulture (which is good), yet the serializers still seem to be implicitly using the CurrentCulture. This causes a problem when the current culture defines a decimal separator different from the invariant one.

public abstract class When_serializing_dynamic_object
{
    // ...

    [Fact]
    public void Float_should_serialize()
    {
        CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de");
        var result = Serialize<float, float>(0.1F, formatValuesAsStrings: true);
        result.ShouldBe(0.1F);
    }
}

Aqua.Tests.Serialization.Dynamic.DynamicObject.When_serializing_dynamic_object+JsonSerializer.Float_should_serialize [FAIL]

Shouldly.ShouldAssertException : result
    should be
0,1f
    but was
1E+08f

Announcement: Newtonsoft.Json (Json.NET) Configuration for aqua-core

Dependency on Newtonsoft.Json moved from aqua-core to package aqua-core-newtonsoft-json as of version 4.0.0-alpha-041.

To enable JSON serialization for aqua-core using Newtonsoft.Json please include aqua-core-newtonsoft-json in your project and used the provided extension method to configure JsonSerializerSettings:

using Newtonsoft.Json;
using Aqua;
…
JsonSerializerSettings serializerSettings = new JsonSerializerSettings().ConfigureAqua();

Problems with System.Text.Json on .NET8

Hey,
we are using Remote.Linq and Aqua.DynamicObject for a long time and really like it.
It crashes on .NET 8 with the following message:

    System.NotSupportedException : The converter for derived type 'Aqua.Dynamic.DynamicObject' does not support metadata writes or reads. The unsupported member type is located on type 'System.Dynamic.IDynamicMetaObjectProvider'. Path: $.
    ---- System.NotSupportedException : The converter for derived type 'Aqua.Dynamic.DynamicObject' does not support metadata writes or reads.

Have these configuration flags set:

         MaxDepth = int.MaxValue,
         ReferenceHandler = SystemTextReferenceHandler.Preserve,
         NumberHandling = SystemTextJsonNumberHandling.AllowNamedFloatingPointLiterals | SystemTextJsonNumberHandling.AllowReadingFromString,
         WriteIndented = true,
         DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,

Can you point me into the right direction? Maybe we can fix it and contribute as PR ;-)

Best Regards,
Alex

Empty DynamicObject with protobuf-net serializer throws exception

Hi,

I faced with a serialization problem of a DynamicObject. Empty DynamicObject is incorrectly deserialized by protobuf. I get the last sources to investigate the problem.
To reproduce exception you need to open the "29_RemoteQueryable_UsingProtobufNetSerializationOverTcp" sample, and at first query change
foreach (var item in await repo.Products.ToArrayAsync().ConfigureAwait(false))
to
foreach (var item in await repo.Products.Where(x => x.Id == 0).ToArrayAsync().ConfigureAwait(false))
Filter can be anything, we need to get an empty result.

As far as I know, problem is at empty array (value of dictionary) of SurrogateDynamicObject. ProtoWriter treat field as a string and deserializer throws an exception. If add check for an empty array and set field to null, everything works, but I prefer to get an empty array as a result.

Why it happens I didn't found yet.

Exception:
System.AggregateException: One or more errors occurred. (Invalid wire-type; this usually means you have over-written a file without truncating or setting the length; see https://stackoverflow.com/q/2152978/23354)
---> ProtoBuf.ProtoException: Invalid wire-type; this usually means you have over-written a file without truncating or setting the length; see https://stackoverflow.com/q/2152978/23354
at ProtoBuf.ProtoReader.SkipField() in c:\Projects\Remote.Linq-main\protobuf\src\protobuf-net\ProtoReader.cs:line 796
at proto_2(Object , ProtoReader )
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source)
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source)
at ProtoBuf.ProtoReader.ReadTypedObject(Object value, Int32 key, ProtoReader reader, Type type)
at ProtoBuf.ProtoReader.ReadObject(Object value, Int32 key, ProtoReader reader)
at proto_244(Object , ProtoReader )
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source)
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source)
at ProtoBuf.Meta.TypeModel.DeserializeCore(ProtoReader reader, Type type, Object value, Boolean noAutoCreate)
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type, SerializationContext context)
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type)
at Common.ProtobufNetFormatter.ReadInternalAsync[T](Stream stream, Type type, CancellationToken cancellation)
at Common.ProtobufNetFormatter.ReadAsync[T](Stream stream, CancellationToken cancellation)
at Client.RemoteRepository.<.ctor>b__2_0(Expression expression, CancellationToken cancellation)
at Remote.Linq.DynamicQuery.AsyncRemoteQueryProvider1.ExecuteAsync[TResult](Expression expression, CancellationToken cancellation) at Remote.Linq.Async.AsyncQueryableExtensions.ExecuteAsync[TResult](IQueryable source, CancellationToken cancellation) at Remote.Linq.Async.AsyncQueryableExtensions.ToArrayAsync[TSource](IQueryable1 source, CancellationToken cancellation)
at Client.AsyncDemo.RunAsync()
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at IAsyncDemoExtensions.RunAsyncDemo(IAsyncDemo demo) in c:\Projects\Remote.Linq-main\samples\SharedCode\Client.IAsyncDemo.cs:line 24

Support Universal Windows Platform

Since UWP10 support was withdrawn from netstandard1.6 we should aim to add support for UWP explicitely: target platform uap10.0

see .NET Standard Versions table

Using code with NET6 fails on some method resolving

My nuget referenced code stopped working once I upgraded my solution to NET 6. I copied the lates RemoteLinq and Aqua sources to my solution and referenced it directly. This made my error go away but a new one appeared,

Below code does not work on for example OfType method. Binding flags come back as Default, so method is not marked as static.

Adding below code after DecalringType line fixes the issue:

if(member is System.Reflection.MethodInfo method)
{
IsStatic = method.IsStatic;
return;
}

image

Typo in DynamicObjectMapper.ParseToNativeType

There seems to be a question mark ? missing after the second short

if (targetType == typeof(short) || targetType == typeof(short))
{
    return short.Parse(value);
}

This causes System.NotImplementedException: string parser for type System.Nullable`1[System.Int16] is not implemented.

How to customize mapping of types implementing IEnumerable to DynamicObject?

Sometimes we need to map types which implement IEnumerable but also contain other properties. The standard example is IGrouping<TKey, TElement> which implements IEnumerable<TElement> but also contains the Key property.

public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable
{
    TKey Key { get; }
}

Class DynamicObject provides a virtual method MapToDynamicObjectGraph which I think I am supposed to override, f.ex. the following way

class CustomDynamicObjectMapper : DynamicObjectMapper
{
    protected override DynamicObject MapToDynamicObjectGraph(object obj, Func<Type, bool> setTypeInformation)
    {
        if (obj != null && obj.GetType().Implements(typeof(IGrouping<,>)))
        {
            object mappedGrouping = typeof(CustomDynamicObjectMapper)
                .GetTypeInfo()
                .GetDeclaredMethod(nameof(MapGrouping))
                .MakeGenericMethod(obj.GetType().GenericTypeArguments)
                .Invoke(this, new[] { obj, setTypeInformation });
            return (DynamicObject)mappedGrouping;
        }

        return base.MapToDynamicObjectGraph(obj, setTypeInformation);
    }

    private DynamicObject MapGrouping<TKey, TElement>(IGrouping<TKey, TElement> grouping, Func<Type, bool> setTypeInformation)
    {
        var mappedGrouping = new DynamicObject(typeof(IGrouping<TKey, TElement>));
        mappedGrouping.Add("Key", this.MapToDynamicObjectGraph(grouping.Key, setTypeInformation));
        mappedGrouping.Add("Elements", this.MapCollection(grouping, setTypeInformation).ToList());
        return mappedGrouping;
    }
}

and it seems to return me exactly what I want

IGrouping<string, int> grouping = Enumerable.Range(1, 5).GroupBy(x => "Hello", x => x).Single();
DynamicObject dynamicGrouping = new CustomDynamicObjectMapper().MapObject(grouping);

Unfortunately the overridden method is NOT called when mapping an object containing an IGrouping property, which makes me believe I chose the wrong way of custom type mapping. What could you advise instead?

My example

public class When_mapping_object_from_object_with_igrouping_members
{
    class CustomClass
    {
        public IGrouping<string, int> Grouping { get; set; }
    }

    class CustomDynamicObjectMapper : DynamicObjectMapper
    {
        protected override DynamicObject MapToDynamicObjectGraph(object obj, Func<Type, bool> setTypeInformation)
        {
            if (obj != null && obj.GetType().Implements(typeof(IGrouping<,>)))
            {
                object mappedGrouping = typeof(CustomDynamicObjectMapper)
                    .GetTypeInfo()
                    .GetDeclaredMethod(nameof(MapGrouping))
                    .MakeGenericMethod(obj.GetType().GenericTypeArguments)
                    .Invoke(this, new[] { obj, setTypeInformation });
                return (DynamicObject)mappedGrouping;
            }

            return base.MapToDynamicObjectGraph(obj, setTypeInformation);
        }

        private DynamicObject MapGrouping<TKey, TElement>(IGrouping<TKey, TElement> grouping, Func<Type, bool> setTypeInformation)
        {
            var mappedGrouping = new DynamicObject(typeof(IGrouping<TKey, TElement>));
            mappedGrouping.Add("Key", this.MapToDynamicObjectGraph(grouping.Key, setTypeInformation));
            mappedGrouping.Add("Elements", this.MapCollection(grouping, setTypeInformation).ToList());
            return mappedGrouping;
        }
    }

    DynamicObject dynamicGrouping;
    DynamicObject dynamicObject;

    public When_mapping_object_from_object_with_igrouping_members()
    {
        IGrouping<string, int> grouping = Enumerable.Range(1, 5).GroupBy(x => "Hello", x => x).Single();
        var source = new CustomClass
        {
            Grouping = grouping
        };
        dynamicGrouping = new CustomDynamicObjectMapper().MapObject(grouping);
        dynamicObject = new CustomDynamicObjectMapper().MapObject(source);
    }

    [Fact]
    public void Dynamic_grouping_type_should_be_igrouping()
    {
        dynamicGrouping.Type.Type.Implements(typeof(IGrouping<,>)).ShouldBeTrue();
    }

    [Fact]
    public void Dynamic_object_grouping_should_be_mapped()
    {
        dynamicObject["Grouping"].ShouldBeOfType<DynamicObject>();
    }

    [Fact]
    public void Dynamic_object_grouping_type_should_be_igrouping()
    {
        ((DynamicObject)dynamicObject["Grouping"]).Type.Type.Implements(typeof(IGrouping<,>)).ShouldBeTrue();
    }
}

Both asserts on dynamicObject fail.

ResolveMethod fails on explicit cast from decimal? to double?

[Fact]
public void ResolveMethod_ExplicitCast_should_return_original_method_info()
{
    var typeResolver = new TypeResolver();

    Expression<Func<decimal?, double?>> expr = x => (double?)x;
    System.Reflection.MethodInfo methodInfo = ((UnaryExpression)expr.Body).Method;
    Aqua.TypeSystem.MethodInfo mappedMethod = new Aqua.TypeSystem.MethodInfo(methodInfo);
    System.Reflection.MethodInfo resolvedMethod = mappedMethod.ResolveMethod(typeResolver);

    resolvedMethod.ShouldBe(methodInfo);
}
Shouldly.ShouldAssertException : resolvedMethod
    should be
Double op_Explicit(System.Decimal)
    but was
Byte op_Explicit(System.Decimal)

Optimise dependencies for net 6

Is your feature request related to a problem? Please describe.
I want to minimise dependencies in my project by utilising framework dependencies wherever possible

Describe the solution you'd like
I want the package to not have an explicit dependency on System.Text.Json as it can be provided by the framework when targeting net 6.

Describe alternatives you've considered
Accept the additional dependency

Additional context
n/a

When_resolving_hidden_property.ResolveProperty_should_return_original_property_info

Version 4.5.0.

The following test fails with System.Reflection.AmbiguousMatchException

public class When_resolving_hidden_property
{
    private class TypeHidingBase
    {
        public string Property { get; }
    }

    private class TypeHiding : TypeHidingBase
    {
        public new int Property { get; }
    }

    [Fact]
    public void ResolveProperty_should_return_original_property_info()
    {
        var typeResolver = new TypeResolver();

        System.Reflection.TypeInfo type = typeof(TypeHiding).GetTypeInfo();
        System.Reflection.PropertyInfo propertyInfo = type.GetDeclaredProperty(nameof(TypeHiding.Property));
        Aqua.TypeSystem.PropertyInfo mappedProperty = new Aqua.TypeSystem.PropertyInfo(propertyInfo);
        System.Reflection.PropertyInfo resolvedProperty = mappedProperty.ResolveProperty(typeResolver);

        resolvedProperty.ShouldBe(propertyInfo);
    }
}

Attaching an entity of type 'my sub-entity type' failed because another entity of the same type already has the same primary key value.

Good morning,
First off, thanks so much for creating this aqua-core package. It has enabled me to pass entire graphs of data returned by Entity Framework from a WCF Service successfully to the client!
I'm working on the part of our application that makes changes to the graph of data and returns it to the WCF Service to handle the updates. I convert the updated graph object back to DynamicObject and pass it as a parameter to my WCF Service method UpdateXYZ. The service is successfully receiving the DynamicObject as a parameter, and successfully converting it back to an object graph of my type in the first line below. However, I am receiving the error above when the second line runs to attach the entity to the service's context. It isn't the top-level MyType that it's failing on, but rather one of the nested types in the graph.
Am I going about this correctly? I don't want to have to perform a field-by-field copy of the received data onto a newly retrieved entity from my service's DbContext.

        MyType app = new DynamicObjectMapper().Map(obj) as MyType;
        context.MyTypeCollection.Attach(app);
        context.SaveChanges();

Edit: If I originally query the data as "AsNoTracking", this Attach error on the return trip goes away, but I have to manually set the EF record state to Modified (code below) to get it to save the changes. However, without the change tracking enabled while work is being done on the client side, I'll have no idea which nested entities in the graph were added, modified, or possibly deleted, so this doesn't really fix my issue.
context.Entry(app).State = EntityState.Modified;

Thanks!

When_resolving_hidden_property.ResolveMemberInfo_should_return_original_property_info

Version 4.6.3.

The following test fails with System.Reflection.AmbiguousMatchException

[Fact]
public void ResolveMemberInfo_should_return_original_property_info()
{
    var typeResolver = new TypeResolver();

    System.Reflection.TypeInfo type = typeof(TypeHiding).GetTypeInfo();
    System.Reflection.PropertyInfo propertyInfo = type.GetDeclaredProperty(nameof(TypeHiding.Property));
    Aqua.TypeSystem.PropertyInfo mappedProperty = new Aqua.TypeSystem.PropertyInfo(propertyInfo);
    System.Reflection.MemberInfo resolvedMemberInfo = mappedProperty.ResolveMemberInfo(typeResolver);

    resolvedMemberInfo.ShouldBe(propertyInfo);
}

Related to

[Bug] Serializers fail deserialization of dynamic object for empty type

Description

Serializers (system.text.json, newtonsoft, and protobuf-net) fail deserialize json for dynamic object representing empty type (i.e. type without any data members).

ACC

  • Aqua.Newtonsoft.Json allows deserialization of dynamic object with empty property set
  • Aqua.Text.Json allows deserialization of dynamic object with empty property set
  • Aqua.protobuf-net allows deserialization of dynamic object with empty property set

netstandard1.6 version of aqua-core is not fully compatible with its netstandard1.3 counterpart

I have a library referencing aqua-core and targeting netstandard1.3. It contains the following line of code

... = new TypeResolver(); // resolves into new TypeResolver(null, false)

The library compiles just fine, but its consuming application (in my case netcoreapp1.0 xunit test project) seems to be pulling the netstandard1.6 version of aqua-core. It doesn't look too wrong to me and I actually don't mind if it happens, but apparently aqua-core/ns1.6 does no longer expose the 2-arg ctor of class TypeResolver which leads to a runtime error

System.MissingMethodException : Method not found: 'Void Aqua.TypeSystem.TypeResolver..ctor(System.Func`2<Aqua.TypeSystem.TypeInfo,System.Type>, Boolean)'.

After some more reflection I tend to see here a contradiction with the general statement of Microsoft

.NET Standard versions are backward compatible. That means that netstandard1.0 libraries run on netstandard1.1 platforms and higher, and so on...

If we apply this statement not only to .NET Standard itself but also to libraries targeting netstandardX_Y then it explains my MissingMethodException.

Can you bring the following signature to aqua-core/ns1.6 for compatibility with aqua-core/ns1.3?

public TypeResolver(Func<TypeInfo, Type> typeEmitter, bool validateIncludingPropertyInfos)

or do you have any better ideas/suggestions?

Serialize DynamicObject containing both 'double' and 'decimal' properties with Json.NET

Hello,

We seem to have stuck at the problem serializing/deserializing DynamicObject's with properties of types 'double' and 'decimal' in the same object (or graph) using Json.NET serializer. The easiest way to reproduce the problem for you would be to change the datatype 'double' to 'decimal' in this field and rerun the tests.

Test Name:  Aqua.Tests.Serialization.Dynamic.DynamicObject.When_using_dynamic_object_for_complex_object_tree.Clone_should_contain_simple_decimal_property
Test FullName:  Aqua.Tests.Serialization.Dynamic.DynamicObject.When_using_dynamic_object_for_complex_object_tree.Clone_should_contain_simple_decimal_property
Test Source:    \aqua-core\src\Aqua\Aqua.Tests\Serialization\Dynamic\DynamicObject\When_using_dynamic_object_for_complex_object_tree.cs : line 43
Test Outcome:   Failed
Test Duration:  0:00:00,014

Result StackTrace:  at Aqua.Tests.Serialization.Dynamic.DynamicObject.When_using_dynamic_object_for_complex_object_tree.Clone_should_contain_simple_decimal_property() in \aqua-core\src\Aqua\Aqua.Tests\Serialization\Dynamic\DynamicObject\When_using_dynamic_object_for_complex_object_tree.cs:line 44
Result Message: 
Assert.Equal() Failure
Expected: 123 (System.Decimal)
Actual:   123 (System.Double)

One may configure Json.NET to parse numeric literals as either 'double' or 'decimal' using FloatParseHandling option, but there is no option to preserve the original CLR type. That means DynamicObject must somehow supply it explicitly.

How would you recommend us to proceed? Thank you in advance!

[Question] Ability to recycle type cache at runtime

Hello @6bee ,

First of all, thank you very much for the remote.linq and aqua-core libraries. It has allowed me to apply metaprogramming techniques.

I'm currently playing around with the new AssemblyLoadContext from dotnet core 3.0, which allows me to dynamically compile ASTs and load the binary which plays quite nicely with remote.linq.

The problem I'm facing now is that whenever I want to unload the context and recompile I fail to do that due to (I presume) the type cache which prevents GC to collect the assembly.

Before I go any deeper, given that you know the inner workings of this library the best, is a complete recycle of the emitted types feasible? Or a possible workaround as a hint?

The main outcome of this would be to be able to reuse remote.linq against the newly built types.

Thanks,
Cosmin

Exception for anonymous type in select containing int property

Hi,

I am currently getting "Failed to pick matching constructor for type" exception for any int property in anonymous type in the following expression: "Select(x => new { x.Id})". I am using the latest Entity Framework on server side.

It looks strange, since the following combinations work well:

  1. Select(x => x.Id)
  2. Select(x => new {Id = (long)x.Id })

Any ideas?

Thanks!

TypeResolver.ResolveType getting slow with nested anonymous types

As I was playing with EntityFrameworkCore 2.0.0-preview1-final I ran into rather severe performance issues. Some of my tests would suddenly take a few minutes and even hours to execute. A bit deeper analysis hints at their new IncludeCompiler which tends to expand included navigation properties into subsequent SelectMany/GroupJoin more aggressively, generating a fairly large number of nested anonymous types on the way. One could certainly find their approach questionable, but I still think there exists a performance problem in aqua-core which deserves its own attention.

Apparently TypeResolver.ResolveType doesn't scale too well with the length of the chain of nested anonymous types. I wrote a little program which generates such types and measures the performance of TypeResolver

[Fact]
public void ResolveTypePerformanceTest()
{
    TypeInfo GenerateAnonymousType<T>(uint nestingCount, T value)
    {
        if (nestingCount == 0)
            return null;

        var newValue = new { Prop = value };
        return GenerateAnonymousType(nestingCount - 1, newValue) ?? new TypeInfo(newValue.GetType());
    }

    for (uint i = 15; i <= 30; ++i)
    {
        TypeInfo type = GenerateAnonymousType(i, "hello");
        var typeResolver = new TypeResolver();

        var watch = Stopwatch.StartNew();
        typeResolver.ResolveType(type);
        watch.Stop();

        Debug.WriteLine($"{i} | {watch.ElapsedMilliseconds}");
    }
}

As one can see the cost of resolving only one type is growing quite rapidly.

nestingCount execution time (ms)
15 55
16 56
17 88
18 117
19 199
20 319
21 525
22 792
23 1309
24 1963
25 3109
26 4943
27 8065
28 12996
29 21031
30 33822

The debugger suggests that it's mainly spending time computing collection hashes for nested anonymous types (TypeResolver.EqualityComparer.GetHashCode). In real world scenarios you usually have more than one type cached in a TypeResolver and you resolve types hundreds of times (e.g. when visiting Remote.Linq expression trees). Then the performance starts to suffer with much shorter chains of nested types, e.g. my EFC2 test never returned with type nesting depth being only 16.

Could you please take another look at TypeResolver.EqualityComparer from the performance point of view?

JsonSerializer and XmlSerializer don't serialize DynamicObject.Type property

I extended When_using_dynamic_object_for_complex_object_tree class with one more test

public class When_using_dynamic_object_for_complex_object_tree
{
    // ...

    public When_using_dynamic_object_for_complex_object_tree()
    {
        var originalObject = new DynamicObject()
        {
            { "DoubleValue", DoubleValue },
            { 
                "Reference", new DynamicObject(typeof(string))
                {
                    { "StringValue", StringValue },
                }
            },
        };

        serializedObject = originalObject.Serialize();
    }

    [Fact]
    public void Clone_should_contain_type_information()
    {
        var nestedObject = serializedObject["Reference"] as DynamicObject;

        (nestedObject.Type?.Type).ShouldBe(typeof(string));
    }
    
    // ...
}

It passes with BinaryFormatter, DataContractSerializer and NetDataContractSerializer, but fails with JsonSerializer and XmlSerializer.

[Question] How to map custom structure?

I am trying to overcome serialization problems with DynamicObjectMapper and NodaTime.LocalTime. I have a simple code:

mapper = new DynamicObjectMapper();
dyO = mapper.MapObject(new C());
mapper.Map<C>(dyO);

public class C
{
      public LocalDate Date { get; set; } = new LocalDate(2020, 10, 10);
 }

And can't replace default DynamicObject creation, I want to serialize LocalDate as string but overriding MapToDynamicObjectGraph and IDynamicObjectFactory do not help.

Could you please provide the correct way to LocalDate serialization?

Mapping of objects with collection members containing null elements

The following tests

public class When_mapping_from_object_with_collection_member
{
    class CustomClass
    {
        public IEnumerable<object> Items { get; set; }
    }

    CustomClass source;
    DynamicObject dynamicObject;

    public When_mapping_from_object_with_collection_member()
    {
        source = new CustomClass { Items = new List<object> { 1, null, "hello"} };
        dynamicObject = new DynamicObjectMapper().MapObject(source);
    }

    [Fact]
    public void Dynamic_object_items_count_should_be_three()
    {
        ((IEnumerable<object>)dynamicObject["Items"]).Count().ShouldBe(3);
    }

    [Fact]
    public void Object_items_count_should_be_three()
    {
        var obj = new DynamicObjectMapper().Map<CustomClass>(dynamicObject);
        obj.Items.Count().ShouldBe(3);
    }
}

are failing with

Aqua.Tests.Dynamic.DynamicObjectMapper.When_mapping_from_object_with_collection_member.Dynamic_object_items_count_should_be_three [FAIL]
      Shouldly.ShouldAssertException : (IEnumerable<object>)dynamicObject["Items"]).Count(
    should be
3
    but was
      2

    Aqua.Tests.Dynamic.DynamicObjectMapper.When_mapping_from_object_with_collection_member.Object_items_count_should_be_three [FAIL]
      Shouldly.ShouldAssertException : obj.Items.Count()
    should be
3
    but was
      2

The reason is possibly attributed to the misuse of .OfType instead of .Cast in DynamicObjectMapper class. Please revisit all three occurrences. I suspect that my tests only cover two of them. Thanks!

How to import and gen from Excel

Hello this a nice place to keep the meta data of my projects. I used to have an excel spreadsheet with the table names & two Cols for the name and type, I would like to import that into your project - so I can generate the model objects from from here.

Excel Col - ClassName 
Excel Col - AtrributeName
Excel Col - Type

How can I template this excel sheet to a simple C# POCO model class with the name and type

TypeResolver returns wrong anonymous type because of caching

We observe one peculiar and fairly unstable bug in TypeResolver for which I think I found an explanation.

Apparently under certain conditions the runtime generates anonymous types with the same name <>f__AnonymousType0´2 but different DeclaringAssembly and different sets of properties. Exactly for such cases the TypeResolver.IsValid method contains the intelligent distinction logic based on matching property names.

The problem occurs when the first candidate is successfully resolved and cached in TypeResolver._typeCache. Then the second candidate is fetched from the cache bypassing the TypeResolver.IsValid method simply because the class names are identical.

It's a bit problematic to provide simple test, but I hope my explanation is clear.

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.