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 expressions - CreateMappingExpressions<X, Y>() is missing? #1271

Closed
xplatsolutions opened this issue May 12, 2016 · 12 comments
Closed

Comments

@xplatsolutions
Copy link

xplatsolutions commented May 12, 2016

Based on this answer http://stackoverflow.com/a/7425211/122769 and the comment "They moved CreateMapExpression to Mapper.Engine.CreateMapExpression<Cat, Dog>();" in how to map expressions using Automapper and this blog post, http://ahmadreza.com/2014/09/automapper-and-mapping-expressions/ I'm supposed to find the Mapper.Engine.CreateMapExpression<X, Y>()generic method which I can't. I can find the Mapper.Engine.CreateMapExpression(Type, Type)method instead.

Have in mind that I can't update to the latest version of Automapper due to incompatible PCL profile, I'm using Automapper 4.1.1.

Any ideas how to accomplish this?Expression<Func<T, bool>> domainPredicate = Map<Expression<Func<T, bool>>>(predicate);

The exception I'm getting trying the above.

Mapping types:
Expression1 -> Expression1
System.Linq.Expressions.Expression1[[System.Func2[CharacteristicRiskDTO, Mobile.Application, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] -> System.Linq.Expressions.Expression1[[System.Func2[[CharacteristicsRisk, Domain.Client.Sync, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

Destination path:
Expression`1

Source value:
p => (p.ReportId == value(Data.Client.Sync.Services.LocalDataService+<>c__DisplayClass45_0).dto.ReportId)

Configuration.

Mapper.CreateMap<Expression<Func<CharacteristicsRisk, bool>>,
                Expression<Func<CharacteristicRiskDTO, bool>>>()
                .IgnoreAllNonExisting().ReverseMap().IgnoreAllNonExisting();

@TylerCarlson1
Copy link
Member

If you are trying to do Expression<Func<T, bool>> domainPredicate = Map<Expression<Func<T, bool>>>(predicate); that might work as such in 4.1.1 perhapse. I'm not 100% sure when this was implemented of the top of my head.

If it's there, then there's also another extension method called UseAsDataSource<T>() which basicly does your find method, but makes it easier to map back. So instead of ProjectTo you do UseAsDataSource and it will translate the lambdas back to the base type and then call ProjectTo as the very last step.

Also if doing repository thing you might want to look at https://github.com/TylerCarlson1/Automapper.Collection. It does some things to help persist easier.

@xplatsolutions
Copy link
Author

xplatsolutions commented May 12, 2016

@TylerCarlson1 I have a DataService wrapper on the Repository with the following method. TDto is what the consumer classes deals with, T is the local SQLite entities I pass down to the repository.

public async Task<IEnumerable<TDto>> GetAsync<TValue>(long workerId, 
            Expression<Func<TDto, bool>> predicate = null, 
            Expression<Func<TDto, TValue>> orderBy = null)
        {
            Expression<Func<T, bool>> domainPredicate = Map<Expression<Func<T, bool>>>(predicate);
            Expression<Func<T, TValue>> domainOrderBy = Map<Expression<Func<T, TValue>>>(orderBy);
            List<T> domains = await Repository.GetAsync(workerId, domainPredicate, domainOrderBy).ConfigureAwait(false);
            return Map<IEnumerable<TDto>>(domains);
        }

Problem is I can't convert the expressions and receive the above. I'll check the link you sent but I don't think it solves this problem, I don't use Queyrables, instead I have to work with AsyncTableQuery and the SQLiteAsyncCOnnection.

For example my Repository.GetAsync method above is the following.

public async Task<List<T>> GetAsync<TValue>(long workerId, 
            Expression<Func<T, bool>> predicate = null, 
            Expression<Func<T, TValue>> orderBy = null)
        {
            var query = Db.Table<T>();

            if (predicate != null)
            {
                query = query.Where(predicate);
            }
            if (orderBy != null)
            {
                query = query.OrderBy<TValue>(orderBy);
            }

            return await query.ToListAsync().ConfigureAwait(false);
        }

@TylerCarlson1
Copy link
Member

Ok I see yea if you don't have IQueryable then UseAsDataSource isn't gong to help you.
What I was thinking is you do var query = Db.Table<T>().UseAsDataSource<TDto>(); in which case you can use the TDto predicates without having to translate them.

The functionality is there for 4.1.1 for expression mapping and UseAsDataSource. The only thing I can see that's wrong is that if you have a null for predicate or orderBy you need to check if null and then map them. In the code if you try to map expressions and the source is null you will definitely get an ArgumentNullException.

Other than that it should work fine.
something like Expression<Func<T, bool>> domainPredicate = predicate == null ? null : Map<Expression<Func<T, bool>>>(predicate); should solve the problem.

@xplatsolutions
Copy link
Author

Hmmm, predicate is not null, it's a very strange problem. Although orderby is null, I was having in mind to add the null check but I can't get there anyway.

@TylerCarlson1
Copy link
Member

Can you give me a dump of the full exception then?

@xplatsolutions
Copy link
Author

xplatsolutions commented May 12, 2016

@TylerCarlson1 I forgot to post the inner exception here is the complete exception stacktraces and messages.

StackTrace

at Data.Client.Sync.Services.BaseLocalDataService2.Map[TMap](Object source) in C:\APP_DATA\Repositories\ISPFLTKIT-1000-2\SyncAPIv2\Data.Client.Sync\Services\BaseLocalDataService.cs:line 44 at Data.Client.Sync.Services.LocalDataService2.d__5`1.MoveNext() in C:\APP_DATA\Repositories\ISPFLTKIT-1000-2\SyncAPIv2\Data.Client.Sync\Services\LocalDataService.cs:line 41

Message

Mapping types:
Expression1 -> Expression1
System.Linq.Expressions.Expression1[[System.Func2[[Mobile.Application.DTO.WindEnhancedWindDataFormCommentsDTO, Mobile.Application, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] -> System.Linq.Expressions.Expression1[[System.Func2[[Domain.Client.Sync.Domain.Wind_Comment, Domain.Client.Sync, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

Destination path:
Expression`1

Source value:
p => (p.ReportId == value(FlightKit.Data.Client.Sync.Services.WindReportLocalDataService+<>c__DisplayClass46_0).dto.ReportId)

Inner Exception

StackTrace

at AutoMapper.Mappers.ExpressionMapper.MappingVisitor.PropertyMap(MemberExpression node)
at AutoMapper.Mappers.ExpressionMapper.MappingVisitor.VisitMember(MemberExpression node)
at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at AutoMapper.Mappers.ExpressionMapper.MappingVisitor.VisitMember(MemberExpression node)
at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
at AutoMapper.Mappers.ExpressionMapper.MappingVisitor.VisitBinary(BinaryExpression node)
at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at AutoMapper.Mappers.ExpressionMapper.Map(ResolutionContext context, IMappingEngineRunner mapper)
at AutoMapper.MappingEngine.AutoMapper.IMappingEngineRunner.Map(ResolutionContext context)

Message

Object reference not set to an instance of an object.

Also I don't think this will work var query = Db.Table<T>().UseAsDataSource<TDto>(); Table<T>() return an AsyncTableQuery<T>.

@TylerCarlson1
Copy link
Member

value(FlightKit.Data.Client.Sync.Services.WindReportLocalDataService+<>c__DisplayClass46_0).dto.ReportId
this is probably the problem.

You aren't passing the whole object over so it gets a null trying to get the dto value.

So you would need to redo your predicate like so

var reportID = class.dto.ReportID;
var predicate = p => p.ReportId == reportID;

instead of

var predicate = p => p.ReportId == class.dto.ReportID;

@xplatsolutions
Copy link
Author

It worked like a charm, I've seen this but didn't even think the cast is failing.

Thanks a lot @TylerCarlson1.

@xplatsolutions
Copy link
Author

I guess classes consuming the dataservice will do the same thing and I will still get errors like this, do you have any suggestion working around this? @TylerCarlson1

@TylerCarlson1
Copy link
Member

I think what you can do is if you make your classes serializable they can then be passed over with the expression and executed as such. If somehow they are in the different namespaces between the two dlls you need to remove the full qualified name from the serialization before you de-serialize it, so that it works. IDK exactly but you are going to have to tinker with the transfer or something like that to get it to work with classes.

@xplatsolutions
Copy link
Author

xplatsolutions commented May 12, 2016

I think the best at this point is to expose the domain to the predicates and the consumer class will actually filter the domain directly but receive DTOs. Also makes more sense to avoid filtering DTO properties that might not exist.

@jbogard jbogard closed this as completed May 19, 2016
@lock
Copy link

lock bot commented May 7, 2019

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.

@lock lock bot locked as resolved and limited conversation to collaborators May 7, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants