Mapster icon indicating copy to clipboard operation
Mapster copied to clipboard

Error with cycle references

Open ComptonAlvaro opened this issue 3 years ago • 3 comments

I have try a simple test with three classes to simulate a bidirectional navigation properties between to classes, using the same instance fot the same entities. The three classes are these:

[DataContract]
public class ClaseTest2
{
    [DataMember(Order = 1)]
    public ClaseTest3 Clase3 {get; set; }
}


[DataContract]
public class ClaseTest3
{
    [DataMember(Order = 1)]
    public ClaseTest2 Clase2 { get; set; }
}



[DataContract]
public class ClaseTest4
{
    public ClaseTest4()
    {
        ClaseTest2 miClase2 = new ClaseTest2();
        ClaseTest3 miClase3 = new ClaseTest3();


        miClase2.Clase3 = miClase3;
        miClase3.Clase2 = miClase2;


        this.Clase2 = miClase2;
    }

    [DataMember(Order = 1)]
    public ClaseTest2 Clase2;
}

I have also create my Class4DTO:

[DataContract] public class ClaseTest4DTO { [DataMember(Order = 1)] public ClaseTest2DTO Clase2; }

In the constructor of the class 4, that is which I want to adapt to the class 4 DTO, I ensure that I have only one instance of Class2 and Class3, and they are referenced one to each other in each case.

But I get an error of stackoverflow.

Stack overflow.
Repeat 19382 times:
--------------------------------
   at DynamicClass.lambda_method7(System.Runtime.CompilerServices.Closure, GTS.Cmms.DemoGrpc.Service.Grpc.Comun.ClaseTest2)
--------------------------------
   at DynamicClass.lambda_method6(System.Runtime.CompilerServices.Closure, System.Object)
   at Mapster.TypeAdapter.Adapt[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Object, Mapster.TypeAdapterConfig)
   at GTS.Cmms.DemoGrpc.Service.Grpc.AspCoreHost.FacturasService+<GetClase4DTOAsync>d__2.MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon ByRef)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Start[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon ByRef)
   at GTS.Cmms.DemoGrpc.Service.Grpc.AspCoreHost.FacturasService.GetClase4DTOAsync(ProtoBuf.Grpc.CallContext)
   at DynamicClass.lambda_method3(System.Runtime.CompilerServices.Closure, GTS.Cmms.DemoGrpc.Service.Grpc.AspCoreHost.FacturasService, ProtoBuf.Grpc.Internal.Empty, Grpc.Core.ServerCallContext)

If for example I don't assign the instance of class 3 to the property of the class2 in the constructor of the class4, it works. But then I lose the informaction that I would like to have.

In the documentation, it is used some example that uses a dbContext, I guess it is Entity Core, to map the result of the query:

using (MyDbContext context = new MyDbContext())
{
    // Build a Select Expression from DTO
    var destinations = context.Sources.ProjectToType<Destination>().ToList();

    // Versus creating by hand:
    var destinations = context.Sources.Select(c => new Destination {
        Id = c.Id,
        Name = c.Name,
        Surname = c.Surname,
        ....
    })
    .ToList();
}

If the entities have bidirectional navigation properties and is not used the option of AsNoTracking() to don't use the same instances for the same entity, that it is the test that I did with the three classes above, then I would have the same problem of stack overflow.

Is it possible to map this kind of bidirectional navigation properties?

Thanks.

ComptonAlvaro avatar Sep 19 '22 09:09 ComptonAlvaro

I have tried to use this code:

                var miConfig = TypeAdapterConfig<ClaseTest4, ClaseTest4DTO>
                    .NewConfig()
                    .PreserveReference(true);

                ClaseTest4 miClaseTest4 = new ClaseTest4();

                ClaseTest4DTO miClase4DTO = miClaseTest4.Adapt<ClaseTest4DTO>();

But I have estill the same problem.

The only way that I don't get the problem is with MaxDepth(), but I have the problem with PreserveReference(), ShallowCopyForSameType() and AvoidInlineMapping().

But I would like to know why only works with MaxDepth and no the other options, because I would like to if there is some option that map all the graph and I don't have to set the max depth, because it would be different in each case (I mean, from different quieries from EF Core).

Thanks.

ComptonAlvaro avatar Sep 19 '22 14:09 ComptonAlvaro

you dont pass config to Adapt method

ParadiseFallen avatar Sep 20 '22 07:09 ParadiseFallen

I have tried too:


var miConfig = TypeAdapterConfig<ClaseTest4, ClaseTest4DTO>
                    .NewConfig()
                    .PreserveReference(true);

ClaseTest4 miClaseTest4 = new ClaseTest4();

 ClaseTest4DTO miClase4DTO = miClaseTest4.Adapt<ClaseTest4DTO>(miConfig);

But I get an error that tells that can't convert miConfig from TypeAdapterSetter<Clase4, Clase4DTO> to TypeAdapterConfig.

How could I pass the config to the Adapt() method? In the documentation I don't see the example, also why it works if I use MaxDepth() but not PreserveReference? In any of both cases I pass the config to the Adapt() method.

Thanks.

ComptonAlvaro avatar Sep 20 '22 07:09 ComptonAlvaro