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

Dictionary projection by key not working #263

Closed
YeskaNova opened this issue Aug 17, 2020 · 5 comments
Closed

Dictionary projection by key not working #263

YeskaNova opened this issue Aug 17, 2020 · 5 comments

Comments

@YeskaNova
Copy link

Hi,
I need to select only some values to be copied from a dictionary but it doesn't work. You can reproduce it with this code:

Version: 6.1.1

Exception:
Mapster.CompileException
ArgumentException: 'Name' is not a member of type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]' (Parameter 'propertyOrFieldName')
System.Linq.Expressions.Expression.PropertyOrField(System.Linq.Expressions.Expression, string)
Mapster.Utils.ExpressionEx.PropertyOrField(System.Linq.Expressions.Expression, string)
System.Linq.Enumerable.Aggregate<TSource, TAccumulate>(System.Collections.Generic.IEnumerable, TAccumulate, System.Func<TAccumulate, TSource, TAccumulate>)
Mapster.Utils.ExpressionEx.PropertyOrFieldPath(System.Linq.Expressions.Expression, string)
Mapster.Models.InvokerModel.GetInvokingExpression(System.Linq.Expressions.Expression, Mapster.MapType)
Mapster.Models.InvokerModel.Next(System.Linq.Expressions.ParameterExpression, string)
Mapster.ReflectionUtils.Next.AnonymousMethod__1(Mapster.Models.InvokerModel)
System.Linq.Enumerable.WhereSelectListIterator<TSource, TResult>.MoveNext()
System.Linq.Enumerable.WhereEnumerableIterator.ToList()
System.Linq.Enumerable.ToList(System.Collections.Generic.IEnumerable)

using Mapster;
using MapsterMapper;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApp22
{
    public class Post
    {
        public IDictionary<string, object> Dic { get; set; }
    }
    class Program
    {

        static void Main(string[] args)
        {
            var config = TypeAdapterConfig<Post, Post>
                .NewConfig()
                .Map(nameof(Post.Dic) + ".Name", nameof(Post.Dic) + ".Name");
            config.Compile();
            var mapper = new Mapper(config.Config);

            var p1 = new Post{ Dic = new Dictionary<string, object>() { { "Name", "test"}, {"Secret" , "password" }} };
            var p2 = new Post();
            mapper.From(p1).AdaptTo(p2);
            Debug.Assert(p1.Dic["Name"] == p2.Dic["Name"]);
            Debug.Assert(! p2.Dic.ContainsKey("Secret"));

        }
    }
}
@YeskaNova
Copy link
Author

@chaowlert , this specific exception is not there anymore, but still the projection is not working as expected, the whole dictionary is copied, and if the dictionary is null, there is an exception ( I think that we can consider a null dictionary like an empty one ):

using Mapster;
using MapsterMapper;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApp22
{
    public class Post
    {
        public IDictionary<string, object> Dic { get; set; }
    }
    class Program
    {

        static void Main(string[] args)
        {
            var config = TypeAdapterConfig<Post, Post>
                .NewConfig()
                .Map(nameof(Post.Dic) + ".Name", nameof(Post.Dic) + ".Name");
            config.Compile();
            var mapper = new Mapper(config.Config);

            var p1 = new Post { Dic = new Dictionary<string, object>() { { "Name", "test" }, { "Secret", "password" } } };
            var p2 = new Post();

            mapper.From(p1).AdaptTo(p2);

            Debug.Assert(p1.Dic["Name"] == p2.Dic["Name"]);
            Debug.Assert(!p2.Dic.ContainsKey("Secret"));

            var p3 = new Post();
            mapper.From(p3).AdaptTo(p2);
            Debug.Assert(!p2.Dic.ContainsKey("Name"));
        }
    }
}

@chaowlert chaowlert reopened this Aug 31, 2020
@YeskaNova
Copy link
Author

@chaowlert As a workaround, can we have AfterMapping and BeforeMapping in TypeAdapterSetter too, and not only in TypeAdapterSetter<T,T> ? My types are not known at compile-time and I need to add code to AfterMapping to handle dictionaries projection. Is that possible ?

@chaowlert
Copy link
Collaborator

Can, however what type for the input parameter for AfterMapping? object? Could you also describe your use case, what AfterMapping will do and why you cannot know at compile time?

@YeskaNova
Copy link
Author

Hmm, my current workaround is to have a generic configurator that will use TypeAdapterSetter<T,T> that a construct at runtime :

private static TypeAdapterSetter<T,T> ConfigureMapper<T>(TypeAdapterConfig config, IList<EntityPropertyDescriptorDbo> permittedfields, IList<EntityPropertyDescriptorDbo> allFields, EntityDescriptorDbo ed)
...
private static readonly MethodInfo ConfigureMapperMethod =typeof(Extensions).GetMethod(nameof(ConfigureMapper), BindingFlags.Static | BindingFlags.NonPublic);
...
ConfigureMapperMethod.MakeGenericMethod(t).Invoke(null,new object[]{config, permittedfields, allFields, ed});

I look for the dictionary properties by name ( independently of the type of the object ) so having a simple AfterMapping for object can be helpful too. But anyway, it's just a workaround because the mapping by key for dictionaries is not yet working, so maybe I will have just to wait for that to be fixed, no need to add something if it wasn't needed by someone before me.

@chaowlert
Copy link
Collaborator

I just have chance to debug, in fact it works as expected. Mapping null dictionary will also produce null dictionary. If you would like to create dictionary if source is null, you can use AddDestinationTransform.

example:

config.NewConfig<Post, Post>()
    .Map(nameof(Post.Dic) + ".Name", nameof(Post.Dic) + ".Name")
    .AddDestinationTransform(DestinationTransform.EmptyCollectionIfNull
        .WithCondition(type => type == typeof(IDictionary<string, string>)));

Then your dictionary will be empty instead of null.

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

2 participants