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

ProjectToType with Mongodb Queryable #366

Open
ziaulhasanhamim opened this issue Aug 19, 2021 · 20 comments
Open

ProjectToType with Mongodb Queryable #366

ziaulhasanhamim opened this issue Aug 19, 2021 · 20 comments

Comments

@ziaulhasanhamim
Copy link

ziaulhasanhamim commented Aug 19, 2021

Is there any way to use the ProjectToType extension with IMongoQueryable of MongoDB c# driver? IMongoQueryable extends IQueryable.

@skalahonza
Copy link

I was solving a similar issue with AutoMaper. https://stackoverflow.com/a/66220779/4438653
You can cast the IQueryable back to IMongoQueryable.

@ziaulhasanhamim
Copy link
Author

var posts = db.GetCollection<Post>("posts");

var postList = await ((IMongoQueryable<PostDto>)posts.AsQueryable()
    .ProjectToType<PostDto>())
    .ToListAsync();

class Post
{
    [BsonId]
    public ObjectId Id { get; set; }

    public string Title { get; set; }

    public string Description { get; set; }
}

public record PostDto(string Title, string Description);

I tried that but got this exception

Unhandled exception. System.ArgumentNullException: Value cannot be null. (Parameter 'itemName')
   at MongoDB.Driver.Core.Misc.Ensure.IsNotNull[T](T value, String paramName)
   at MongoDB.Driver.Linq.Linq2Implementation.Expressions.SelectExpression..ctor(Expression source, String itemName, Expression selector)
   at MongoDB.Driver.Linq.Linq2Implementation.Processors.Pipeline.MethodCallBinders.SelectBinder.Bind(PipelineExpression pipeline, PipelineBindingContext bindingContext, MethodCallExpression node, IEnumerable`1 arguments)
   at MongoDB.Driver.Linq.Linq2Implementation.Processors.MethodInfoMethodCallBinder`1.Bind(PipelineExpression pipeline, TBindingContext bindingContext, MethodCallExpression node, IEnumerable`1 arguments)
   at MongoDB.Driver.Linq.Linq2Implementation.Processors.PipelineBinderBase`1.BindMethodCall(MethodCallExpression node)
   at MongoDB.Driver.Linq.Linq2Implementation.Processors.PipelineBinderBase`1.Bind(Expression node)
   at MongoDB.Driver.Linq.Linq2Implementation.Processors.Pipeline.PipelineBinder.Bind(Expression node, IBsonSerializerRegistry serializerRegistry)
   at MongoDB.Driver.Linq.Linq2Implementation.MongoQueryProviderImpl`1.Prepare(Expression expression)
   at MongoDB.Driver.Linq.Linq2Implementation.MongoQueryProviderImpl`1.Translate(Expression expression)
   at MongoDB.Driver.Linq.Linq2Implementation.MongoQueryProviderImpl`1.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at MongoDB.Driver.Linq.Linq2Implementation.MongoQueryableImpl`2.ToCursorAsync(CancellationToken cancellationToken)
   at MongoDB.Driver.IAsyncCursorSourceExtensions.ToListAsync[TDocument](IAsyncCursorSource`1 source, CancellationToken cancellationToken)
   at Program.<Main>$(String[] args) in C:\Users\ziaul\\MapsterMongo\Program.cs:line 13       
   at Program.<Main>(String[] args)

@skalahonza
Copy link

This happened to me with automapper as well. What helped was writing the record in the old syntax with default constructor with no parameters.

public record PostDto 
{
   public string Title { get; init; }
   public string Description { get; init; }
}

@skalahonza
Copy link

If that doesn't work for you, the last resort workaround could be that you can fetch your MongoDb entities into list. Then call Adpat and perform the mapping in memory and not in database,

@ziaulhasanhamim
Copy link
Author

ziaulhasanhamim commented Feb 6, 2022

This happened to me with automapper as well. What helped was writing the record in the old syntax with default constructor with no parameters.

doesn't help

@skalahonza
Copy link

Well, according to this: #403. This project might not be maintained anymore. So in my opinion you have two options:

  1. Do mapping in memory
  2. Switch to another mapping tool e.g. AutoMapper

@ziaulhasanhamim
Copy link
Author

yeah you are correct. But I really like the way mapster does things. No need for di containers needed. Very simple mapping with extensions

@skalahonza
Copy link

Yes, it works very well with Entity Framework, however acts weird with MongoDb.

@ziaulhasanhamim
Copy link
Author

it would be very sad if it's not maintained anymore

@ziaulhasanhamim
Copy link
Author

@andrerav can you give something on it?

@andrerav
Copy link
Contributor

Hi @ziaulhasanhamim, I'm currently working on streamlining the build and packaging and get a new version of Mapster.Tool as soon as possible. After that I can take a closer look at this issue. I've read through this thread but not sure if I am understanding the problem correctly. Can you give me a clear explanation of what kind of functionality is missing in Mapster to help you with this problem?

@ziaulhasanhamim
Copy link
Author

Mapster supports mapping for queryable. I get a weird exception when I try to use mapster mapping with MongoDB queryable. MongoDB Queryable is very similar to ef core. Also I think automapper supports it without any extra dependency.

I think the issue is about parameter names in the expression that mapster creates to map one type to other.
Unhandled exception. System.ArgumentNullException: Value cannot be null. (Parameter 'itemName')
here the item name is the parameter name. MongoDB doesn't allow the parameter name to be null. It can be anything other than null.

I think this is how parameters are created in mapster expressions

Expression.Parameter(parameterType)

but there is also a second parameter which is parameterName.

Expression.Parameter(parameterType, parameterName)

This is my observation of the problem. It's very much possible that I'm wrong because I'm not that familiar with expression and queryable APIs.

But mapster can support MongoDB. I won't think there will be a lot of work.

I know there are many works pending but I just hope this project keeps up. This is literally the best mapper I've used in .net. Thanks to you for taking responsibility for this project

@ziaulhasanhamim
Copy link
Author

@andrerav Anything on this?

@Ryba1986
Copy link

Ryba1986 commented Oct 3, 2022

I'm using extension method:

public static IMongoQueryable<TResult> ProjectTo<TResult>(this IMongoQueryable query, TypeAdapterConfig config) { return (IMongoQueryable<TResult>)query.ProjectToType<TResult>(config); }

@ziaulhasanhamim
Copy link
Author

@Ryba1986 does't that thow exception? For me it throws an exception

@Ryba1986
Copy link

Ryba1986 commented Oct 4, 2022

I am using LinqProvider v3

@ziaulhasanhamim
Copy link
Author

ziaulhasanhamim commented Oct 4, 2022

Thank you sir appreciate your efforts. I was searching a solution for this for a lot of time.

@andrerav
Copy link
Contributor

andrerav commented Mar 4, 2023

@ziaulhasanhamim Using ProjectToType() with IQueryable can cause problems in cases where there are asynchronous operations. I suggest that you materialize your query (with ToList(), for example) before doing any mapping. This should fix your problem.

@alex-tselikovsky
Copy link

alex-tselikovsky commented Mar 8, 2023

Hi

var posts = db.GetCollection<Post>("posts");

var postList = await ((IMongoQueryable<PostDto>)posts.AsQueryable()
    .ProjectToType<PostDto>())
    .ToListAsync();

class Post
{
    [BsonId]
    public ObjectId Id { get; set; }

    public string Title { get; set; }

    public string Description { get; set; }
}

public record PostDto(string Title, string Description);

I tried this code and it works correct if use MongoDb.Driver v2.19.0 and it gets exception in v2.18.0. I think it was bug of MongoDb.Driver

@Ryba1986
Copy link

Ryba1986 commented Jun 4, 2023

@Ryba1986 does't that thow exception? For me it throws an exception

I am using LinqProvider v3

I tried this code and it works correct if use MongoDb.Driver v2.19.0 and it gets exception in v2.18.0. I think it was bug of MongoDb.Driver

MongoDb.Driver before v2.19.0 using LinqProvider v2 for default.

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

No branches or pull requests

5 participants