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
Value transformers in global, profiles, type maps, and members #2261
Conversation
cc/ @asherber |
@@ -522,6 +522,10 @@ private Expression BuildValueResolverFunc(PropertyMap propertyMap, Expression de | |||
); | |||
} | |||
|
|||
valueResolverFunc = _typeMap.Profile.ValueTransformers | |||
.Where(vt => vt.IsMatch(propertyMap)) | |||
.Aggregate(valueResolverFunc, (current, vtConfig) => ToType(ReplaceParameters(vtConfig.TransformerExpression, ToType(current, vtConfig.ValueType)), propertyMap.DestinationPropertyType)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this change to having ValueTransformerConfiguration have function likeMapExpression? Kinda like what IObjectMapper does now where it's an interface like IValueTransformerConfiguration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's a further task on the list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't going to make it like IObjectMapper though, more like IValueResolver and ITypeConverter. You can go the expression route, which can be used in LINQ etc. or the runtime route that can get access to ResolutionContext. That would also allow a DI-able version.
This one's ready to review. |
What do you think about making The only issue with this if you want to keep the same public function calls Unless this is meant for another PR in the future, then can discuss it there. |
Hrm that's a big departure from the rest of the configuration API. I think you already have the ability to do similar though with ForAllTypeMaps? |
But for all type maps doesn't apply to individual property maps and can't be profile specific. That's the whole reason for transforms. Also your current implementation is only for property maps of same type. What about different types being mapped what about comparing by some kind of naming convention. If you keep the API as it is for each one of them you'll have to keep adding more and more functions to the interface, as well as their implementation in multiple places away from the base class. Where as if you use extension methods they can be confined to their own classes near their respected implementations of
Yes it's a big change in how the API is structured, but one thing I don't like about working with the current one is that the MapperConfiguration, Profile, and everything else have interfaces that have like 20-30 properties/functions that do almost the exact same things using a class and keep adding functionality with more and more function calls is going to make navigating them even worse to wade through. I think this makes the code more easily expandable, without having to hack around in private properties or submit PR's and wait for the next version if you want to do something extra. But then it exposes things that you might not want the user to worry about in configuration. It's a trade off, the big one being change how API is structured, but from the end user experience I don't think it will change, unless there's some weird thing between Extension methods that I'm not aware of. |
PR for a PR?
I can see some benefits to the flexibility for sure, I do get a little wary
of just extension methods since they aren't nearly as discoverable. I would
be interested in a concrete example of what you're suggesting though.
…On Mon, Aug 14, 2017 at 7:06 PM, Tyler Carlson ***@***.***> wrote:
But for all type maps doesn't apply to individual property maps and can't
be profile specific. That's the whole reason for transforms.
Also your current implementation is only for property maps of same type.
What about different types being mapped what about comparing by some kind
of naming convention. If you keep the API as it is for each one of them
you'll have to keep adding more and more functions to the interface, as
well as their implementation in multiple places away from the base class.
Where as if you use extension methods they can be confined to their own
classes near their respected implementations of
IValueTransformerConfiguration for profiles and member configurations.
ValueTransformerConfiguration can have a static extensions class in the
same location as it, maybe even a sub class in the same file, with all the
method calls that use it. Instead of every configuration interface having
an implementation in 2-3 separate files that you have to search for if you
want to change something.
Yes it's a big change in how the API is structured, but one thing I don't
like about working with the current one is that the MapperConfiguration,
Profile, and everything else have interfaces that have like 20-30
properties/functions that do almost the exact same things using a class and
keep adding functionality with more and more function calls is going to
make navigating them even worse to wade through.
I think this makes the code more easily expandable, without having to hack
around in private properties or submit PR's and wait for the next version
if you want to do something extra. But then it exposes things that you
might not want the user to worry about in configuration. It's a trade off,
the big one being change how API is structured, but from the end user
experience I don't think it will change, unless there's some weird thing
between Extension methods that I'm not aware of.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#2261 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAGYMhKtu2iHBRCeigmYVu2hDhv0Z-02ks5sYOETgaJpZM4OyP7O>
.
|
@@ -522,6 +522,12 @@ private Expression BuildValueResolverFunc(PropertyMap propertyMap, Expression de | |||
); | |||
} | |||
|
|||
valueResolverFunc = propertyMap.ValueTransformers | |||
.Concat(_typeMap.ValueTransformers) | |||
.Concat(_typeMap.Profile.ValueTransformers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might be a lot of transformers here, so a cache would help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know much about transformers, but that sounded weird :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few hundred mappings, maybe a few thousand properties, it seems reasonable. For all the existing transformers. This will hurt eventually :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would we cache? The list could be different since you can define at the property map level.
Alternatively we could push the transformers down from the parent levels, kinda like we do for Include.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but not the transformers per member are the problem, all the rest are. And yes, I thought about smth like your second suggestion, but I'm not sure how that would look like, caching by type seemed more obvious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will only be an issue with large amounts of value transformers. And I'm not sure that would really happen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we can wait :)
|
||
public bool IsMatch(PropertyMap propertyMap) | ||
{ | ||
return ValueType.IsAssignableFrom(propertyMap.DestinationPropertyType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the other way around?
/// </summary> | ||
/// <typeparam name="TValue">Value type to match and transform</typeparam> | ||
/// <param name="transformer">Transformation expression</param> | ||
void ApplyTransform<TValue>(Expression<Func<TValue, TValue>> transformer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where TValue : TDestination? Although integer conversions don't fit.
|
||
namespace AutoMapper | ||
{ | ||
public class ValueTransformerConfiguration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this could be a struct.
src/UnitTests/ValueTransformers.cs
Outdated
{ | ||
cfg.CreateMap<Source, Dest>(); | ||
cfg.ApplyTransform<string>(dest => dest + " is straight up dope"); | ||
cfg.ApplyTransform<string>(dest => dest + "! No joke!"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the order matters here. I don't know about that...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing some people expect stacking and some expect overriding :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the wording can be better? But yes, they'd stack is the idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's easy to make a mistake. You have a lot of config code and you have one ApplyTransform and a few pages below, oops, another one. Not exactly the pit of success :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think AddTransform would be better and exposing a list even clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your point, but I don't mind
cfg.ValueTransformers.Add<string>(dest => dest + "!!!");
It is two dots. But it makes more obvious what's going on. I'm not sure if you wanted to do other things with that list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been following this at a little bit of a distance. FWIW, from a functionality standpoint I like the idea of stacking transforms, but it seems like a departure from the rest of the API, which I think just does overrides. On the whole I think I would vote for ApplyTransform()
and overrides, rather than AddTransform()
and stacking, but I don't feel strongly about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lbargaoanu I gotcha, add the extension method to the list of value transformers rather than the root. I don't want to create a custom collection type.
@asherber I think we could add conditions to transforms that would allow for turning things off (or don't apply things globally).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jbogard @lbargaoanu I kinda like extending off the list better. Less mass options from Intelisense and groups functionality by property.
Also can make it fluent where you add and then return list, so you can stack, and know you are stacking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK I made some changes here lemme know what you think. I couldn't go extension methods all the way because it would have required forcing generic arguments or a weird chaining.
/// </summary> | ||
/// <typeparam name="TValue">Value type to match and transform</typeparam> | ||
/// <param name="transformer">Transformation expression</param> | ||
void AddTransform<TValue>(Expression<Func<TValue, TValue>> transformer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a non generic overload that just uses TMember.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if we need an overload as much as just get rid of this version. I don't remember why I put a transform where you could specify a different type. Transformers get applied after resolving/mapping so it's always going to be of the destination member type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After resolving and before mapping. And I think you meant for TValue to be the type of the resolved value. Which doesn't have to be TMember. Right? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah then I wonder if I have value transformations in the right place. I think it's really before you assign it, you transform it but I have it as part of resolving.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the value transformations to after mapping and right before setting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it. I've always thought AfterPropertyMap is a good idea :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ha well maybe this will take care of people's scenarios.
Anything else on this one? |
Thank you! |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
No description provided.