Mapster icon indicating copy to clipboard operation
Mapster copied to clipboard

Properties do not map properly

Open kzeratal opened this issue 3 years ago • 11 comments

I have a class A that has a DateTime property, and class B that has a long property. Both properties have the same name. I've setup the rule for type DateTime converting to long in a config C1 which implements IRegister interface. And of course a config C2 for A converting to B But when I map from A to B, that DateTime property will always 0, unless I set the mapping rule in config C2.

kzeratal avatar Feb 24 '22 04:02 kzeratal

@kzeratal Can you share the mapping config and your custom type conversion code? Or alternatively set up a minimal test case if that's easier for you.

andrerav avatar Feb 24 '22 07:02 andrerav

The following are the config and classes.

public class DateTimeConfig : IRegister
{
    public void Register(TypeAdapterConfig config)
    {
        config.ForType<DateTime, long>()
            .Map(dest => dest, src => src.ToLong());
        config.ForType<long, DateTime>()
            .Map(dest => dest, src => src.ToDateTime());
    }
}

Extensions look like

public long ToLong(DateTime time)
{
    return new DateTimeOffset(time).ToUnixTimeSeconds();
}

public DateTime ToDateTime(long value)
{
    return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(source);
}
public class C1
{
    public DateTime Time { get; set; }
}

public class C2
{
    public long Time { get; set; }
}

and the usage

var c1 = new C1 { Time = DateTime.UtcNow };
var c2Test1 = dbCotext.C1.ProjectToType<C2>().ToList().First(); // failed using EF 6
var c2Test2 = c1.Adapt<C2>(); // also failed at simple mapping, both two c2 objects' Time are 0

kzeratal avatar Feb 24 '22 09:02 kzeratal

Have you added breakpoints to the ToLong() and ToDateTime() methods and verified that they are being called as expected?

andrerav avatar Feb 24 '22 10:02 andrerav

Well, they are not being called.

kzeratal avatar Feb 25 '22 02:02 kzeratal

I see. You have also checked that the DateTimeConfig.Register() method is called?

andrerav avatar Feb 25 '22 11:02 andrerav

Yes, the register was executed. If there is no config, an System.InvalidCastException: 'Invalid cast from 'DateTime' to 'Int64'.' will occur.

kzeratal avatar Feb 25 '22 15:02 kzeratal

Hello @andrerav I think I know what this is about. Primitives do not support custom mapping. I described it in this #561. I even developed a specific solution) I'll test it on this case and report the result

DocSvartz avatar Oct 27 '23 12:10 DocSvartz

It needs some work, but it definitely works as I planned when the mapping To primitive.

 [TestMethod]
 public void MappingDatetimeToLong()
 {
    
     TypeAdapterConfig.GlobalSettings.ForType(typeof(Source407), typeof(Destination407))
        .MapToTargetPrimitive(true);
     
     TypeAdapterConfig<DateTime, long>
        .NewConfig()
        .MapToTargetWith((source, target) => true ? new DateTimeOffset(source).ToUnixTimeSeconds() : target);

     TypeAdapterConfig<long, DateTime>
        .NewConfig()
        .MapToTargetWith((source, target) => true ? new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(source) : target);


     var fromC1 = new DateTime(2023, 10, 27);
     var fromC2 = new DateTimeOffset(new DateTime(2025, 11, 23)).ToUnixTimeSeconds();

     var c1 = new Source407 { Time = fromC1 };
     var c2 = new Destination407 { Time = fromC2 };

     var _result = c1.Adapt<Destination407>(); // Work 
     var _resultLongtoDateTime = c2.Adapt<Source407>();

   
     _result.Time.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27)).ToUnixTimeSeconds()); // Work                  
    // _resultLongtoDateTime.Time.ShouldBe(new DateTime(2025, 11, 23)); // ClassAdapter - not work; 
 }


    public class Source407
    {
        public DateTime Time { get; set; }
    }

    public class Destination407
    {
        public long Time { get; set; }
    }


update must work because Datetime not primitive. I just set the settings for the update script

DocSvartz avatar Oct 27 '23 14:10 DocSvartz

Work all

[TestMethod]
public void MappingDatetimeToLong()
{
   
    TypeAdapterConfig.GlobalSettings.ForType(typeof(Source407), typeof(Destination407))
       .MapToTargetPrimitive(true);

    
    TypeAdapterConfig<DateTime, long>
       .NewConfig()
       .MapToTargetWith((source, target) => true ? new DateTimeOffset(source).ToUnixTimeSeconds() : target);

    TypeAdapterConfig<long, DateTime>
       .NewConfig()
       .MapToTargetWith((source, target) => true ? new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(source).Date : target);


    var emptySource = new Source407() { Time = DateTime.UtcNow.Date };

    var fromC1 = new DateTime(2023, 10, 27);
    var fromC2 = new DateTimeOffset(new DateTime(2025, 11, 23)).ToUnixTimeSeconds();

    var c1 = new Source407 { Time = fromC1 };
    var c2 = new Destination407 { Time = fromC2 };

   var _result = c1.Adapt<Destination407>(); // Work 
    var _resultLongtoDateTime = c2.Adapt(emptySource); // work

  
    _result.Time.ShouldBe(new DateTimeOffset(new DateTime(2023, 10, 27)).ToUnixTimeSeconds()); // Work                  
    _resultLongtoDateTime.Time.ShouldBe(new DateTime(2025, 11, 22).Date); // work but it turns out to be a day less. Perhaps this is how it was intended
}

DocSvartz avatar Oct 27 '23 14:10 DocSvartz

@andrerav Yes, this is simply due to the lack of support for custom mapping processing from primitive type

DocSvartz avatar Oct 27 '23 14:10 DocSvartz

@andrerav in #650 i send this function.

DocSvartz avatar Oct 28 '23 03:10 DocSvartz