Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapping Optional<T> to T #561

Open
furier opened this issue Mar 9, 2023 · 8 comments
Open

Mapping Optional<T> to T #561

furier opened this issue Mar 9, 2023 · 8 comments

Comments

@furier
Copy link

furier commented Mar 9, 2023

I have a situation where I only want the Source.Name property to be mapped to the Target.Name property if the HasValue flag of the Source.Name property is true.

struct Optional<T> {
    public bool HasValue { get; }
    public T? Value { get; }
}

class Source { 
    public Optional<string?> Name { get; set; }
}

class Target { 
    public string Name { get; set; }
}

I've tried to create a custom mapping using the TypeAdapterConfig object as shown below, but the Target.Name property never gets a value.

public void Register(TypeAdapterConfig config)
{
    config
        .NewConfig<Optional<string?>, string>()
        .MapToTargetWith((source, target) => source.HasValue ? source.Value : target);
}

Here's the code I'm using to adapt the source object to a target object:

var target = new Source { Name = new Optional<string?>("John") }.Adapt<Target>();

I have so incredibly many source and target types that I dont want to explicitly create manual mapping for all the types, but in stead tell Mapster that when it encounters a property on a source class with the type of Optional<T>, only map it if the HasValue flag is true.

I am doing this a part of my HotChocolate GraphQL API, here is a link to the documentation regarding Optional properties.

@larsbloch
Copy link

larsbloch commented Mar 11, 2023

I have asked a similar question on hotchocolates slack channel the other day. I have yet to receive something usefull. If we can figure this out i will share it with the rest of the project. I think many people could take advantage of this usecase !

@furier
Copy link
Author

furier commented Mar 13, 2023

With AutoMapper you can do the following to add a type conversion, and it works great. However I was hoping to use Mapster as it is significantly faster.

config
    .CreateMap<Optional<T>, T>()
    .ConvertUsing((s, d) => s.HasValue ? s.Value : d)

@furier
Copy link
Author

furier commented Mar 14, 2023

The problem seems to be related to Nullable<T> as it works perfectly fine when none of the source or target T is Nullable<T> eg: .NewConfig<Optional<string>, string>()

@DocSvartz
Copy link

DocSvartz commented Sep 30, 2023

Hello, String is the same Class, what member do you want to replace with a string or this just stated as an example?

@DocSvartz
Copy link

DocSvartz commented Oct 3, 2023

Sorry, I misunderstood the question. I thought you were looking for a solution to the fact that the update TDistination has null When null was not transmitted TSource.

@DocSvartz
Copy link

DocSvartz commented Oct 4, 2023

Hello, problem in this. In this example, the conversion to a primitive. The primitive adapter does not support custom mapping processing.
In this sample in to destination set value from method .ToString() Source object, not from source.Value.
MapToTargetWith() doesn't work.

You @furier were right if Tsource == null && map != Projection working this section and return default(TDistination)

If this not work from not primitive class please add a sample.

@DocSvartz
Copy link

Hello, probably it you want
IgnorIF

@DocSvartz
Copy link

DocSvartz commented Oct 24, 2023

Hello @furier,
The underlying problem here is that .MapToTargetWith() only works when you are updating existing instances of your objects:

_Optional<string> _Optional = new();
Target _target = new();
_Optional.adapt(_target);

in this case .MapToTargetWith() not work:

var target = new Source { Name = new Optional<string?>("John") }.Adapt<Target>();

I get behavior you want from primitive type, you wanted to get this for all types ?

[TestMethod]
public void OptionalT()
{

    TypeAdapterConfig<Optional561<string>, string>
        .NewConfig()
        .MapToTargetWith((source, target) => source.HasValue ? source.Value : target)
        .MapToTargetPrimitive(true) // wip func
        .IgnoreNullValues(true);

   

    var sourceNull = new Source561 { Name = new Optional561<string?>(null) };

    var target = new Source561 { Name = new Optional561<string>("John") }.Adapt<Target561>();

   
    var TargetDestinationFromNull = new Target561() { Name = "Me" };

    var NullOptionalUpdateTarget= sourceNull.Adapt(TargetDestinationFromNull); //   Target.Name = "ME"

    var _result = sourceNull.Adapt(target);
    target.Name.ShouldBe("John");


}

class Optional561<T>
{
    public Optional561(T? value) 
    {
        if (value != null)
            HasValue = true;

        Value = value;

      
    }

    public bool HasValue { get; }
    public T? Value { get; }
}

class Source561
{
    public Optional561<string?> Name { get; set; }
}


class Target561
{
    public string? Name { get; set; }
}


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants