.net linq to N1QL derived usage of BucketContext.Query<T>()

Hello
I have following intention to develop my application

class Amodel{
     string model {get; set;}
     public  IEnumerable<Amodel> Search<T>(<Func<Amodel, bool> predicate){
                 var tname = typeof(T).Name.ToLower();                 
                 return myBucket.bucketContext.Query<Amodel>()
                                             .Where(m => m.model == tname)
                                             .Where(predicate);
      }
}


class Cmodel {
     string myproperty {get; set;}
}

and when I try to use it like:
Cmodel.Search<Cmodel>(m => true).ToList()

it throws error as follows:

An error occurred executing the N1QL query. See the inner exception for details.

  • Couchbase.Linq.Execution.BucketQueryExecutor.ParseResult(IQueryResult)
  • Couchbase.Linq.Execution.BucketQueryExecutor.ExecuteCollection(Couchbase.Linq.QueryGeneration.LinqQueryRequest)
  • Couchbase.Linq.Execution.BucketQueryExecutor.ExecuteCollection(Remotion.Linq.QueryModel)
  • Remotion.Linq.Clauses.StreamedData.StreamedSequenceInfo.ExecuteCollectionQueryModel(Remotion.Linq.QueryModel, Remotion.Linq.IQueryExecutor)
  • Remotion.Linq.Clauses.StreamedData.StreamedSequenceInfo.ExecuteQueryModel(Remotion.Linq.QueryModel, Remotion.Linq.IQueryExecutor)
  • Remotion.Linq.QueryModel.Execute(Remotion.Linq.IQueryExecutor)
  • Remotion.Linq.QueryProviderBase.Execute(System.Linq.Expressions.Expression)
  • Remotion.Linq.QueryProviderBase.System.Linq.IQueryProvider.Execute(System.Linq.Expressions.Expression)
  • QueryableBase.GetEnumerator()
  • List…ctor(IEnumerable)
  • System.Linq.Enumerable.ToList(IEnumerable)

I think derived usage of Query Result Bind would not interfere main concept of Linq2N1ql.
please help, what Am I doing wrong?

@ptulgaa

You can’t pass Func<Amodel, bool> into a LINQ provider because it isn’t an expression tree, so it can’t parse that into N1QL (or SQL if it was EF).

Instead, try making your parameter type Expression<Func<Amodel, bool>>. The compiler should automatically convert the predicate being passed to your function into an expression tree at compile time, which can then be used by LINQ providers.

Brant

Hi
@btburnett3 thank for you solution
it successfully returned in IEnumerable<Amodel> results
How can I return result of type IEnumerable<Cmodel> from Query() function.

with IEnumerable<Amodel> result json parse just into scope of Amodel additional properties of
Cmodel are ignored.

when I call just Query<T>() I can’t filter rows by default like
.Where(m => m.model == tname) because the T don’t have model property.

Any suggestions?

Regards,
Tulgaa P

@ptulgaa

Sure. You actually have a couple of options.

Your first option is to have Amodel and Cmodel both inherit from the same base class or implement the same interface. Then declare the model property on that base class or interface. Finally, you can use a type constraint on your method to require that T be inherited from the base or implement the interface. This makes the property available within your method.

public interface IMyInterface
{
    string model {get; set;}
}

public IEnumerable<T> Search<T>(Expression<Func<T, bool>> predicate)
    where T: IMyInterface
{
    // your logic here, T will have the model property
}

Your second option is to apply the model filter using filter attributes. This is specific to Linq2Couchbase. We implement one such filter attribute for you, the DocumentTypeFilterAttribute. Details here https://github.com/couchbaselabs/Linq2Couchbase/blob/master/docs/document-filters.md.

By inheriting from DocumentFilterAttribute, you could create your own filter attribute that works dynamically. It would look something like this:

public class ModelFilterAttribute : DocumentFilterAttribute
{
    public override IDocumentFilter<T> GetFilter<T>()
    {
        return new WhereFilter<T>
        {
            Priority = Priority,
            WhereExpression = GetExpression<T>()
        };
    }

    private Expression<Func<T, bool>> GetExpression<T>()
    {
        var parameter = Expression.Parameter(typeof (T), "p");

        return Expression.Lambda<Func<T, bool>>(Expression.Equal(Expression.PropertyOrField(parameter, "model"), Expression.Constant(typeof(T).Name.ToLower())), parameter);
    }

    private class WhereFilter<T> : IDocumentFilter<T>
    {
        public Expression<Func<T, bool>> WhereExpression { get; set; }
        public int Priority { get; set; }

        public IQueryable<T> ApplyFilter(IQueryable<T> source)
        {
            return source.Where(WhereExpression);
        }
    }
}

You can then apply this attribute to each model class. Linq2Couchbase will then automatically apply the where predicate on every query for those models.

Brant

1 Like

hello,

first solution did the trick “ALL I WANTED” thank you so much,
I was ready to give up and decided to just use n1ql based approach as base

I’ll definitely try second approach too, thank you so much.

Tulgaa, P