Not working as expected, when using IQueryProvider.CreateQuery
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; }
}
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 :thinking: ?
object[] args = new object[] { Connection, _saveState, _chunkSize };
return (IQueryable)Activator.CreateInstance(typeof(RedisCollection<>).MakeGenericType(elementType), args);
I'm encountering the same issue with AutoMapper:
System.MissingMethodException: 'Constructor on type >'Redis.OM.Searching.RedisCollection
1[[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<T>(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. <FindWhenExpressionProvidedShouldReturnEntity2>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<TElement>(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?
Did this problem ever get resolved? I'm running into the exact same problem today while trying to use the AutoMapper IQueryableExtensions with RedisConnection.
If not, is there a workaround?