MapsterMapper / Mapster

A fast, fun and stimulating object to object Mapper
MIT License
4.3k stars 328 forks source link

ConstructUsing for generic types doesn't work #507

Open omerercelik opened 1 year ago

omerercelik commented 1 year ago

TypeAdapterConfig<PagedList<ExampleEntity>, PagedList<ExampleDto>>.ForType().ConstructUsing(dest => new PagedList<ExampleDto>(new List<ExampleDto>(), 1, 1)); = > its work

TypeAdapterConfig<PagedList<Type>, PagedList<Type>>.ForType().ConstructUsing(dest => new PagedList<Type>(new List<Type>(), 1, 1)); it doesn't work and throw Exception

Exception => System.InvalidOperationException : No default constructor for type 'PagedList`1', please use 'ConstructUsing' or 'MapWith'

pagedlist constructors;

    `public PagedList(IEnumerable<T> items, int page, int size)
        : this(items.AsQueryable(), page, size)
    {
    }

    public PagedList(IEnumerable<T> items, int page, int size, int itemCount)
    {
        BindItems(page, size, itemCount);
        Items = items;
    }

    public PagedList(IQueryable<T> items, int page, int size)
    {
        var itemCount = items.Count();
        BindItems(page, size, itemCount);

        Items = items.PagedBy(page, size).ToList();
    } `
andrerav commented 1 year ago

@omerercelik Which version of Mapster are you using?

omerercelik commented 1 year ago

@andrerav I am using 7.3.0

DocSvartz commented 10 months ago

@omerercelik You can set Full sample of this case. propably i found the source of this problem )

DocSvartz commented 10 months ago

@andrerav This seems to be related to the reason why the tests I commented out in this do not work #646 . the same error is generated there. I made a simplified version of the bypass previously installed in the class adapter and then there was no error.

DocSvartz commented 10 months ago

TypeAdapterConfig<PagedList<Type>, PagedList<Type>>.ForType().ConstructUsing(dest => new PagedList<Type>(new List<Type>(), 1, 1)); it doesn't work and throw Exception

If Type is System.Type? then as far as I understand it cannot be created. This can only be obtained. typeof(), .Gettype()

valid Generic definition possible must look like as typeof(PagedList<>), and mapping from this and mapping for this should be supported endofpage

TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>))
    .Map("value", "Value");
omerercelik commented 10 months ago

TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>)) Does not have ConstructUsing definition. @DocSvartz When I use this syntax i can not use ConstructUsing method.

How can I use contructusing method in generic classes? Or How can i skip this error when generic class have one more constructor?

Exception => System.InvalidOperationException : No default constructor for type 'PagedList`1', please use 'ConstructUsing' or 'MapWith'

DocSvartz commented 10 months ago

Even if you bring it to a case that in theory should work.

You get IQueryable from the conversion function and there will always be an error Instead of the data you need

[TestClass]
public class WhenGenericMapping
{

    [TestMethod]
    public void TestMappingGeneric()
    {
        TypeAdapterConfig
            .GlobalSettings
            .ForDestinationType(typeof(SamplePageList<>))
            .MapToConstructor(true);

        SamplePageList<string> _SourcePageList = new(new[] { "1234", "222" }.ToList(), 2, 2);

         var _DeconstructPageList = _SourcePageList.Adapt<PocoActivatorPageList<string>>();
         var _result = _DeconstructPageList.Adapt<SamplePageList<string>>();

        _DeconstructPageList.Items.ToArray()[0].ShouldBe("1234"); 
        _DeconstructPageList.Items.ToArray()[1].ShouldBe("222"); 

        _result.Items.ToArray()[0].ShouldBe("1234"); // Error 
        _result.Items.ToArray()[1].ShouldBe("222"); //  Error 

    }

}

    class PocoActivatorPageList<T>
    {
        public  IEnumerable<T> Items { get; set; }
        public int Size { get; set; }
        public int Page { get; set; }

    }

    class SamplePageList<T>
    {

        public IQueryable<T> Items { get;  private set; }

        public int Size {  get; private set; }
        public int Page {  get; private set; }

        public int ItemCount { get; private set; }

        public SamplePageList(IEnumerable<T> items, int page, int size)
        : this(items.AsQueryable(), page, size)
        {
            // this target
        }

        public SamplePageList(IEnumerable<T> items, int page, int size, int itemCount)
        {
           /// Not used
        }

        public SamplePageList(IQueryable<T> items, int page, int size)
        {
            Items = items;
            ItemCount = items.Count();
            Page = page;
            Size = size;
        } 
    }
Error Message:
 System.NotImplementedException: The method or operation is not implemented.
Trace Stack:
  GeneratedType_1.GetEnumerator()
  LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
  EnumerableHelpers.ToArray[T](IEnumerable`1 source)
  Enumerable.ToArray[TSource](IEnumerable`1 source)
  WhenGenericMapping.TestMappingGeneric() 

From this File Mapster.Utils.DynamicTypeGenerator
DocSvartz commented 10 months ago

@andrerav For some reason, RecordType is involved there again))) for interface IQueryable

DocSvartz commented 10 months ago

@andrerav Apparently it is necessary to allocate a separate InterfaceAdapter (Base logic work from all Interface) handler with private setters (uses from RecordTypeAdapter). I'll try this as step 3 of fix #537. In continuation of this PR #646.

DocSvartz commented 10 months ago

@omerercelik This working From This

@andrerav I managed to separate the processing of interfaces without public setters from the processing of RecordTypes)

But IQueryable now not supported

[TestClass]
public class WhenGenericMapping
{

    [TestMethod]
    public void TestMappingGeneric()
    {

        TypeAdapterConfig
            .GlobalSettings
            .ForType(typeof(SamplePageList<>), typeof(SamplePageList<>))
            .ShallowCopyForSameType(true);

        SamplePageList<string> _SourcePageList = new(new[] { "1234", "222" }.ToList(), 2, 2);
        SamplePageList<int> _SourcePageList2 = new(new[] { 555, 333 }.ToList(), 2, 2);

        var c = _SourcePageList.Adapt(_SourcePageList2);

        c.Items.ToArray()[0].ShouldBe(1234);
        c.Items.ToArray()[1].ShouldBe(222);

    }

}

class PocoActivatorPageList<T>
{
    public  IEnumerable<T> Items { get; set; }
    public int Size { get; set; }
    public int Page { get; set; }

}

class SamplePageList<T>
{

    public IEnumerable<T> Items { get;  private set; }

    public int Size {  get; private set; }
    public int Page {  get; private set; }

    public int ItemCount { get; private set; }

    public SamplePageList(IEnumerable<T> items, int page, int size)

    {
        Items = items;
        ItemCount = items.Count();
        Page = page;
        Size = size;
    }

    public SamplePageList(IEnumerable<T> items, int page, int size, int itemCount)
    {
       /// Not used
    }

    public SamplePageList(IQueryable<T> items, int page, int size)
    {
        Items = items;
        ItemCount = items.Count();
        Page = page;
        Size = size;
    } 
}