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

Not working as expected, when using IQueryProvider.CreateQuery #429

Open
zulander1 opened this issue Jan 13, 2024 · 2 comments
Open

Not working as expected, when using IQueryProvider.CreateQuery #429

zulander1 opened this issue Jan 13, 2024 · 2 comments

Comments

@zulander1
Copy link
Contributor

zulander1 commented Jan 13, 2024

I am using Automapper to map internal object to a DTO, however it is throwing

System.MissingMethodException: 'Constructor on type 'Redis.OM.Searching.RedisCollection1[[System.Linq.IQueryable1[[CustomerDTO, Redis_Graphql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], System.Linq.Expressions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]]' not found.'

on

public IQueryable CreateQuery(Expression expression)
{
	Type elementType = expression.Type;
	try
	{
		return
		   (IQueryable)Activator.CreateInstance(
			   typeof(RedisCollection<>).MakeGenericType(elementType),
			   this,
			   expression);
	}
	catch (TargetInvocationException e)
	{
		if (e.InnerException == null)
		{
			throw;
		}

		throw e.InnerException;
	}
}

runable code:

using AutoMapper;
using Redis.OM;
using Redis.OM.Modeling;
using AutoMapper.QueryableExtensions;

var provider = new RedisConnectionProvider("redis://localhost:6379");
provider.Connection.CreateIndex(typeof(CustomerModel));
var customers = provider.RedisCollection<CustomerModel>();

// Insert customer
customers.Insert(new CustomerModel()
{
    Id = 1,
    FirstName = "James",
    LastName = "Bond",
}, WhenKey.NotExists);

customers.Insert(new CustomerModel()
{
    Id = 2,
    FirstName = "Dr",
    LastName = "No",
}, WhenKey.NotExists);

var materialized= customers.ToList();

var mapperConfig = new MapperConfiguration(f => f.AddProfile(new CustomerDTOProfile()));
var mapper = new Mapper(mapperConfig);
var mapped = mapper.Map<IEnumerable<CustomerDTO>>(materialized);

var notWorking = customers.ProjectTo<CustomerDTO>(mapperConfig);

public class CustomerDTOProfile : Profile
{
    public CustomerDTOProfile()
    {
        CreateMap<CustomerModel, CustomerDTO>();
    }
}

[Document(StorageType = StorageType.Json, Prefixes = new[] { Prefix }, IndexName = Prefix + ":idx")]
public class 
{
    public const string Prefix = "customer";

    [Indexed][RedisIdField] public int Id { get; set; }
    [Indexed] public string FirstName { get; set; }
    [Indexed] public string LastName { get; set; }
}

public class CustomerDTO
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
@zulander1
Copy link
Contributor Author

Adding, this would solve the constructor issue but it's going into the exception of "The root attribute of a Redis Collection must be decorated with a DocumentAttribute"... perhaps adding a more generic constructor would help 🤔 ?

object[] args = new object[] { Connection, _saveState, _chunkSize };
return (IQueryable)Activator.CreateInstance(typeof(RedisCollection<>).MakeGenericType(elementType), args);

@shapik1234
Copy link

shapik1234 commented Jul 9, 2024

I'm encountering the same issue with AutoMapper:

System.MissingMethodException: 'Constructor on type >'Redis.OM.Searching.RedisCollection1[[System.Linq.IQueryable1[[Caching.Redis.Infra.RedisOmitAttributesCollectionTe>sts.RedisOmitAttributesCollectionTests+ItemEntity, Caching.Redis.Infra, Version=1.0.0.0, Culture=neutral.

The issue is related to the implementation of the RedisQueryProvider.CreateQuery(Expression) method:

/// <inheritdoc/>
        public IQueryable CreateQuery(Expression expression)
        {
            Type elementType = expression.Type;
            try
            {
                return
                   (IQueryable)Activator.CreateInstance(
                       typeof(RedisCollection<>).MakeGenericType(elementType),
                       this,
                       expression);
            }
            catch (TargetInvocationException e)
            {
                if (e.InnerException == null)
                {
                    throw;
                }

                throw e.InnerException;
            }
        }

There are no valid constructors for RedisCollection(RedisQueryProvider, Expression). You can reproduce the issue with the following code:

var redisCollection = new RedisCollection<ItemEntity>(_redisConnectionProvider.Connection, false, 100);
var query = redisCollection.Provider.CreateQuery(expression);

StackTrace:

at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture)
at System.Activator.CreateInstance(Type type, Object[] args)
at Redis.OM.Searching.RedisQueryProvider.CreateQuery(Expression expression)
at Caching.Redis.Infra.RedisOmitAttributesCollectionTests.
d__11.MoveNext() in C:\Users\User\OneDrive -
e\caching.redis\test\Caching.Redis.Infra\RedisOmitAttributesCollectionTests\RedisOmitAttributesCollectionTests.cs:line 138

The fix seems straightforward - we can take the implementation from the CreateQuery(Expression) method and adapt it. Here is the code:

        /// <inheritdoc/>
        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
            where TElement : notnull
        {
            var booleanExpression = expression as Expression<Func<TElement, bool>>;
            return new RedisCollection<TElement>(this, expression, StateManager, booleanExpression, true);
        }

What do you think?

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