Mapster icon indicating copy to clipboard operation
Mapster copied to clipboard

AOT issue for Xamarin/MAUI

Open NeilN1 opened this issue 2 years ago • 4 comments

Not sure if this can be fixed in Mapster but I'm getting this crash when running a MAUI app on a physical iPhone (running in a simulator is fine). Mapster 7.4.0 and .NET 7.

Attempting to JIT compile method '(wrapper delegate-invoke) int <Module>:invoke_callvirt_int_List1<PromoItemDistributionDTO/DistributionDetail> (System.Collections.Generic.List1<METIS.DTOs.PromoItemDTOs.PromoItemDistributionDTO/DistributionDetail>)' while running in aot-only mode. See https://docs.microsoft.com/xamarin/ios/internals/limitations for more information.

Calling Adapt on a simple class is fine. The crash occurs when the class contains a subclass. Example:

                    //Convert list of PromoItemDTOs to PromoItems
                    SelectedDistributionDetail = new PromoItemDistribution(promoDistributionDto.Result);
    public partial class PromoItemDistributionDTO
    {
        public Guid PromoItemDistributionId { get; set; } // PromoItemDistributionId (Primary key)
        public int ReceiptNo { get; set; } // ReceiptNo
        public Guid ContactId { get; set; } // ContactId
        public int PromoItemDistributionTypeId { get; set; } // PromoItemDistributionTypeId
        ...
        public List<DistributionDetail>? DistributionDetails { get; set; }

        public partial class DistributionDetail
        {
            public long PromoItemDistributionDetailId { get; set; } // PromoItemDistributionDetailId (Primary key)
            public Guid PromoItemVariationId { get; set; } // PromoItemVariationId
            public string ItemName { get; set; } // ItemName (length: 255)
           ...
        }
    }
    public class PromoItemDistribution : PromoItemDistributionDTO, INotifyPropertyChanged
    {
        public new List<ExtendedDistributionDetail>? DistributionDetails { get; set; }

        public PromoItemDistribution()
        {
            DistributionDetails = new List<ExtendedDistributionDetail>();
        }

        public PromoItemDistribution(PromoItemDistributionDTO promoItemDistributionDTO)
        {
            promoItemDistributionDTO.Adapt(this);
            DistributionDetails = promoItemDistributionDTO.DistributionDetails?
           .Select(detail => detail.Adapt<ExtendedDistributionDetail>())
           .ToList();
        }

        public partial class DistributionDetail
        {
        }
        public string DistributionType
        {
            get
            {
                if (PromoItemDistributionTypeId == 1)
                    return "Pickup";
                else
                {
                    if (string.IsNullOrEmpty(DeliveredBy))
                        return "Delivery";
                    else
                        return "Delivery (" + DeliveredBy + ")";
                }
            }

        }
        public string CreatedOnFormatted => CreatedOn.ToString().FormatToDateTimeString("ddMMMyyyy").ToUpper();


        public event PropertyChangedEventHandler? PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    public class ExtendedDistributionDetail : PromoItemDistributionDTO.DistributionDetail, INotifyPropertyChanged
    {
        public string CreatedOnFormatted { get; set; }
        public string ItemDescriptionShort => QtyDistributed.ToString() + " x " + ClothesSize + " - " + ItemDetail;

        public event PropertyChangedEventHandler? PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

    }

NeilN1 avatar Nov 01 '23 16:11 NeilN1

Hello @NeilN1

On real hardware, you can adapt Collections. In a simple case?

var result = DistributionDetails.Adapt<List<ExtendedDistributionDetail>>()

if Yes, Then this is the reason for the error in this code


> public PromoItemDistribution(PromoItemDistributionDTO promoItemDistributionDTO)
>         
{
>             promoItemDistributionDTO.Adapt(this);
>             DistributionDetails = promoItemDistributionDTO.DistributionDetails? // this transform
>            .Select(detail => detail.Adapt<ExtendedDistributionDetail>())
>            .ToList();
>         }


DocSvartz avatar Nov 03 '23 14:11 DocSvartz

On real hardware I can adapt simple classes with a property defined as a subclass. However, a crash occurs when that property is defined as a List.

This works:

    public partial class TestClass1DTO
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Phone? PrimaryPhone { get; set; }

        public partial class Phone
        {
            public string PhoneType { get; set; }
            public string PhoneNumber { get; set; }
        }
    }
    public partial class TestClass : TestClass1DTO, INotifyPropertyChanged
    {
        public TestClass(TestClass1DTO testClass1DTO)
        {
            testClass1DTO.Adapt(this);
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
            List<TestClass1DTO> testClass1DTOs = new List<TestClass1DTO>();
            testClass1DTOs.Add(new TestClass1DTO()
            {
                FirstName = "John",
                LastName = "Doe",
                PrimaryPhone = new TestClass1DTO.Phone()
                {
                    PhoneType = "Home",
                    PhoneNumber = "123-456-7890"
                } 
            });

            testClass1DTOs.Add(new TestClass1DTO()
            {
                FirstName = "Jane",
                LastName = "Smint",
                PrimaryPhone = new TestClass1DTO.Phone()
                {
                    PhoneType = "Home",
                    PhoneNumber = "111-456-7890"
                }           
            });

            List<TestClass> testClasses = testClass1DTOs.Select(x => new TestClass(x)).ToList();

This does not:

    public partial class TestClass1DTO
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public List<Phone>? PhoneNumbers { get; set; }

        public partial class Phone
        {
            public string PhoneType { get; set; }
            public string PhoneNumber { get; set; }
        }
    }
    public partial class TestClass : TestClass1DTO, INotifyPropertyChanged
    {
        public TestClass(TestClass1DTO testClass1DTO)
        {
            testClass1DTO.Adapt(this);
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
            List<TestClass1DTO> testClass1DTOs = new List<TestClass1DTO>();
            testClass1DTOs.Add(new TestClass1DTO()
            {
                FirstName = "John",
                LastName = "Doe",
                PhoneNumbers = new List<TestClass1DTO.Phone>()
                {
                    new TestClass1DTO.Phone()
                    {
                        PhoneType = "Home",
                        PhoneNumber = "123-456-7890"
                    },
                    new TestClass1DTO.Phone()
                    {
                        PhoneType = "Work",
                        PhoneNumber = "987-654-3210"
                    }
                }
            });

            testClass1DTOs.Add(new TestClass1DTO()
            {
                FirstName = "Jane",
                LastName = "Smint",
                PhoneNumbers = new List<TestClass1DTO.Phone>()
                {
                    new TestClass1DTO.Phone()
                    {
                        PhoneType = "Home",
                        PhoneNumber = "111-456-7890"
                    },
                    new TestClass1DTO.Phone()
                    {
                        PhoneType = "Work",
                        PhoneNumber = "222-654-3210"
                    }
                }
            });

            List<TestClass> testClasses = testClass1DTOs.Select(x => new TestClass(x)).ToList();

NeilN1 avatar Nov 05 '23 21:11 NeilN1

This seems to be due to the inability to dynamically create a specific implementation of a generic class in AOT, including generic collections.

DocSvartz avatar Nov 06 '23 02:11 DocSvartz

Yes, the link in my first post referred to that. Is this something that can be fixed in Mapster or should we avoid using it in iOS apps for classes that have List properties?

NeilN1 avatar Nov 06 '23 18:11 NeilN1