Skip to content

Fix exceptions with generic source types #91

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

Merged
merged 3 commits into from
Oct 15, 2020

Conversation

mycroes
Copy link

@mycroes mycroes commented Oct 14, 2020

Today I ran into issues when mapping generic source types with UseAsDataSource(). I noticed ExpressionMapper.VisitAllParametersExpression was substituting the source type for it's generic argument when trying to find a match, which of course breaks the matching.

I changed the method to only take the generic type argument if the type implements IEnumerable<>, because I'm assuming what this logic is for. All tests still pass, so I'm assuming all is still fine. I also added a test which breaks without this change.

@mycroes
Copy link
Author

mycroes commented Oct 14, 2020

Seems to me CI build is failing due to misconfiguration, hope you can look into this PR shortly.

Copy link
Member

@BlaiseD BlaiseD left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested a couple of changes. Thanks.

@@ -171,5 +171,14 @@ public static bool IsQueryableType(this Type type)

public static Type GetGenericElementType(this Type type)
=> type.HasElementType ? type.GetElementType() : type.GetTypeInfo().GenericTypeArguments[0];

public static bool IsEnumerableType(this Type type, out Type itemType)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

public static bool IsEnumerableType(this Type type)
            => type.IsGenericType && typeof(System.Collections.IEnumerable).IsAssignableFrom(type);

I think your method is doing more than it need to.

@@ -173,7 +173,7 @@ private Expression VisitAllParametersExpression<T>(Expression<T> expression)
from t in expression.Parameters
let sourceParamType = t.Type
from destParamType in _destSubTypes.Where(dt => dt != sourceParamType)
let a = destParamType.IsGenericType() ? destParamType.GetTypeInfo().GenericTypeArguments[0] : destParamType
let a = destParamType.IsEnumerableType(out var itemType) ? itemType : destParamType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the recommendation in TypeExtensions.cs:

let a = destParamType.IsEnumerableType() ? destParamType.GetGenericElementType() : destParamType

@BlaiseD
Copy link
Member

BlaiseD commented Oct 14, 2020

Never mind the build failure - probably because the myget keys are only accessible the AutoMapper repositories.

@mycroes
Copy link
Author

mycroes commented Oct 14, 2020

Hi @BlaiseD, I must admit I didn't check your suggestion before I implemented mine (which basically ensures that the generic argument of the IEnumerable<> implementation is used), but following your suggestions results in 27 failed tests (all across the board).

@mycroes
Copy link
Author

mycroes commented Oct 14, 2020

Sorry, that's incorrect, not sure what I messed up, but 3 other tests fail with your suggestion:

  • SourceInjectedQuery
    • Map_select_method
    • Map_select_method_toList
  • ExpressionMapping
    • GrandParent_Mapping_To_Sub_Sub_Property_Condition

@BlaiseD
Copy link
Member

BlaiseD commented Oct 15, 2020

Worked for me. With the code from your branch, the only changes were:

        public static bool IsEnumerableType(this Type type)
            => type.IsGenericType && typeof(System.Collections.IEnumerable).IsAssignableFrom(type);

        //public static bool IsEnumerableType(this Type type, out Type itemType)
        //{
        //    itemType = type.GetInterfaces()
        //        .FirstOrDefault(t => t.GetGenericTypeDefinitionIfGeneric() == typeof(IEnumerable<>))
        //        ?.GetGenericArguments()[0];

        //    return itemType != null;
        //}

and

                    let a = destParamType.IsEnumerableType() ? destParamType.GetGenericElementType() : destParamType
                    //let a = destParamType.IsEnumerableType(out var itemType) ? itemType : destParamType

Or what am I missing.

@mycroes
Copy link
Author

mycroes commented Oct 15, 2020

My bad, messed up big time 😉 I used GetElementType() instead of GetGenericElementType(), which then results in the 3 failed cases... Applied suggested changes.

@BlaiseD BlaiseD merged commit 17d5804 into AutoMapper:master Oct 15, 2020
@mycroes mycroes deleted the generic-source-type branch October 15, 2020 22:15
@mycroes
Copy link
Author

mycroes commented Oct 21, 2020

Hi @BlaiseD, is there any timeline on a next release? We're currently using a local build to work around the issue fixed with this PR, but we'd prefer to use the upstream release again.

@BlaiseD
Copy link
Member

BlaiseD commented Oct 21, 2020

I'm sure it's part of 4.0.2-preview-2..

You can compare releases here.

@mycroes
Copy link
Author

mycroes commented Oct 21, 2020

Thanks, I didn't notice there was a preview release. I'm really leaving a good impression of my analytic skills here here 😉

@BlaiseD
Copy link
Member

BlaiseD commented Oct 21, 2020

I'd usually leave a note in the issue or PR - didn't this time. BTW there's also a MyGet package which gets the CI build.

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

Successfully merging this pull request may close these issues.

2 participants