I'm using IDocumentClient to retrieve and update items in a DocumentDB collection.
The code for retrieving a list of documents that match a condition looks like this:
public static async Task<FeedResponse<T>> GetDocuments<T>(
this IDocumentClient client,
string collection,
Expression<Func<T, bool>> filter)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(GetCollectionUri(collection)).Where(filter)
.AsDocumentQuery();
return await query.ExecuteNextAsync<T>().ConfigureAwait(false);
}
In order to unit test the code above against a list of mock items, I've created a mock class:
public class MockIOrderedQueryable<T> : List<T>, IOrderedQueryable<T>, IDocumentQuery<T>
{
public Expression Expression
{
get
{
var content = this.ToList();
return content.AsQueryable().Expression;
}
}
public IQueryProvider Provider => new MyProvider<T>(this.ToList());
public Task<FeedResponse<TResult>> ExecuteNextAsync<TResult>(CancellationToken token = new CancellationToken())
{
var tmp = new FeedResponse<TResult>((IEnumerable<TResult>)this);
return Task.FromResult(tmp);
}
}
The Where filter is an extension method on IQueryable<T>, so I needed an implementation that looks like this:
public class MyQueryable<T> : IQueryable<T>, IDocumentQuery<T>
{
private readonly List<T> _list;
public MyQueryable(List<T> list)
{
_list = list;
}
public Task<FeedResponse<TResult>> ExecuteNextAsync<TResult>(CancellationToken token = new CancellationToken())
{
var tmp = new FeedResponse<TResult>(_list as List<TResult>);
return Task.FromResult(tmp);
}
}
And also an implementation of IQueryProvider that returns the IQueryable instance to my original mock class via CreateQuery:
public class MyProvider<T> : IQueryProvider
{
private readonly List<T> _list;
public MyProvider(List<T> list)
{
_list = list;
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new MyQueryable<TElement>(_list as List<TElement>);
}
}
For brevity, I omitted the code for the methods that throw NotImplementedException and also for the fields that are not used.
This all looks fine, but there's one thing I did not manage to do: applying the real filtering passed as Expression in CreateQuery on the _list member of MyProvider. I tried calling Invoke and retrieving the arguments, but it did not work. The expression returned in MockIOrderedQueryable is probably not the good one (.AsQueryable on the list). I'd like to get to a lambda Expression<Func<T, bool>> and call it on the list.
Any help appreciated.
Related
The method I use is returning a queryable object of type IMongoQueryable<TEntity>. Result of this method returns default(IMongoQueryable<TEntity>) when mongodb database throws an error. Instead of the default result, I want to replace that row with an empty queryable object, however MongoDb Linq library does not provide any.
//Sample of the code
public IMongoQueryable<TEntity> AllQueryable<TEntity>(AggregateOptions options = null) where TEntity : Entity
{
try
{
return GetCollection<TEntity>().AsQueryable(options);
}
catch (Exception ex)
{
return default(IMongoQueryable<TEntity>);
}
}
So, how can I implement a custom type equivalent to the Enumerable.Empty<TEntity>().AsQueryable() or something like IEnumerable<TEntity>().AsMongoQueryable()?
//I need something like this
public IMongoQueryable<TEntity> AllQueryable<TEntity>(AggregateOptions options = null) where TEntity : Entity
{
try
{
return GetCollection<TEntity>().AsQueryable(options);
}
catch (Exception ex)
{
return Enumerable.Empty<TEntity>().AsMongoQueryable();
}
}
When you debug your C# application you can notice that running .AsQueryable<T> on MongoDB collection returns an instance of MongoQueryableImpl class which is internal in MongoDB.Driver.Linq namespace. You can try to instantiate that class using reflection but there's an easier way.
You can implement your own implementation of IMongoQueryable<T> interface following the Null Object design pattern.
The interface requires a handful of methods but you can keep most of them unimplemented - you'll never need them. So your EmptyMongoQueryable<T> may look like this:
public class EmptyMongoQueryable<T> : IMongoQueryable<T>
{
public Expression Expression => Expression.Constant(this, typeof(IMongoQueryable<T>));
public Type ElementType => typeof(T);
public IQueryProvider Provider => new EmptyQueryProvider();
public IEnumerator<T> GetEnumerator()
{
yield break;
}
public QueryableExecutionModel GetExecutionModel() => throw new NotImplementedException();
public IAsyncCursor<T> ToCursor(CancellationToken cancellationToken = default(CancellationToken)) => throw new NotImplementedException();
public Task<IAsyncCursor<T>> ToCursorAsync(CancellationToken cancellationToken = default(CancellationToken)) => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Then you can run following code:
var queryable = new EmptyMongoQueryable<YourType>();
var emptyList = queryable.ToList();
EDIT: since you've mentioned in a comment that you want to run .Where() you need to provide the IQueryProvider. Same story - MongoDB driver uses internal class but you can create your own:
public class EmptyQueryProvider : IQueryProvider
{
public IQueryable CreateQuery(Expression expression) => null;
public IQueryable<TElement> CreateQuery<TElement>(Expression expression) => new EmptyMongoQueryable<TElement>();
public object Execute(Expression expression) => default(object);
public TResult Execute<TResult>(Expression expression) => default(TResult);
}
This will allow you to run Where() on your EmptyMongoQueryable<T>.
I am writing a system that has something similar to roles; a user can only see data that they have rights to. This applies to data used for form population, searches, lists, reports, etc.
The way I planned on implementing this adding a filter to Get requests by adding a "WHERE" clause to the EF query just before execution.
This would be simple were it not for the fact that we are using generics.
The Get function used to be this
public class EntityFactory<TEntity, TDto> : IEntityFactory<TEntity, TDto> where TEntity : class
{
private readonly DBContext _context;
private readonly IMapper _mapper;
private DbSet<TEntity> _dbset;
public EntityFactory(DBContext context, IMapper mapper)
{
//...
}
public async Task<List<TDto>> GetAsync()
{
List<TEntity> d = await _dbset.AsNoTracking().ToListAsync();
return _mapper.Map<List<TDto>>(d);
}
}
And what I would like to do:
public async Task<List<TDto>> GetAsync()
{
//If the object implements a special interface
if (i.GetInterfaces().Contains(typeof(IFoo)))
{
//expression to filter the data on a linked table containing the user's Id.
Expression<Func<Bar, bool>> exp = x => x.Foos.Any(a => a.UserId == _user.UserId);
//add the expression to the dbSet
_dbSet = _dbSet.Where(exp);
}
//Execute the get
List<TEntity> d = await q.AsNoTracking().ToListAsync();
//return the converted objects
return _mapper.Map<List<TDto>>(d);
}
But this does not work! I get this compiler error:
Argument 2: cannot convert from 'System.Linq.Expressions.Expression<System.Func<Bar, bool>>' to 'System.Linq.Expressions.Expression<System.Func<TEntity, int, bool>>'
Is there a way to either:
create a "dynamic" query that is not checked or
change the dbset to the required type, apply the expression and return it to the generic type?
You could use Linq dynamic query to apply where clause.
var query = _dbSet.AsQueryable();
//If the object implements a special interface
if (typeof(IFoo).IsAssignableFrom(typeof(TEntity)))
{
query = query.Where("UserId = #0", _userId);
}
List<TEntity> d = await query.AsNoTracking().ToListAsync();
You may be better served by finding a different way of looking at this problem. Since this is a security filter, you could find that unexpectedly a IFoo wasn't implemented and you aren't filtering when you thought you were.
That being said, you can do this but it requires a couple of steps. You could build the expression manually, but I prefer to build the expression in code and then use a ReplaceVisitor to modify the expression for the current type.
First I create a filtering expression of type Expression<Func<IFoo,bool>>. This isn't the final filtering expression but it is compatible since we are checking to see that the generic type implements IFoo.
Next I use an Expression Visitor to replace each reference to the IFoo parameter expression with a TEntity parameter expression. The resulting expression will be an Expresssion<Func<TEntity,bool>>, so I cast it to that so the compiler will be happy.
Finally, I pass the resulting expression to the Where clause.
Note: This is a non-async version, but it demonstrates the principles.
public class EntityFactory<TEntity, TDto> : IEntityFactory<TEntity, TDto> where TEntity : class
{
private readonly DbContext _context;
private readonly IMapper _mapper = new Mapper(new MapperConfiguration(v => { }));
private DbSet<TEntity> _dbSet;
private Type i = typeof(TEntity);
private Expression<Func<IFoo, bool>> getFilterExpression(int userId)
{
return (x => x.Foos.Any(a => a.UserId == userId));
}
private class ReplaceVisitor : ExpressionVisitor
{
readonly Expression _originalExpression;
readonly Expression _replacementExpression;
public ReplaceVisitor(Expression originalExpression, Expression replacementExpression)
{
_originalExpression = originalExpression;
_replacementExpression = replacementExpression;
}
public override Expression Visit(Expression node)
{
return _originalExpression == node ? _replacementExpression : base.Visit(node);
}
public static Expression VisitExpression(Expression node, Expression originalExpression, Expression replacementExpression)
{
return new ReplaceVisitor(originalExpression, replacementExpression).Visit(node);
}
}
public List<TDto> Get()
{
IQueryable<TEntity> query = _dbSet;
//If the object implements a special interface
if (i.GetInterfaces().Contains(typeof(IFoo)))
{
var userId = 7;
var baseFilterExpression = getFilterExpression(userId);
var filterExpression = (Expression<Func<TEntity, bool>>)ReplaceVisitor.VisitExpression(
baseFilterExpression,
baseFilterExpression.Parameters.First(),
Expression.Parameter(typeof(TEntity)));
//add the expression to the dbSet
query = query.Where(filterExpression);
}
List<TEntity> d = query.AsNoTracking().ToList();
return _mapper.Map<List<TDto>>(d);
}
}
public interface IEntityFactory<TEntity, TDTO> { }
public interface IFoo
{
List<FooItem> Foos { get; set; }
}
public class FooItem
{
public int UserId { get; set; }
}
Some of my entities have IEnabledEntity interface.
I want to check in repository if entity implements interface then add some predicate. I have the following code:
public class Repository<T> : IRepository<T> where T : class, IEntity, new()
{
public IQueryable<T> Get(Expression<Func<T, bool>> predicate, params string[] includes)
IQueryable<T> query = Context.Set<T>();
foreach (var include in includes)
{
query = query.Include(include);
}
query = query.Where(predicate);
var isEnabledEntity = typeof(IEnabledEntity).IsAssignableFrom(typeof(T));
if (isEnabledEntity)
{
query = query.Where(e => ((IEnabledEntity) e).IsEnabled);
}
return query;
}
public interface IEnabledEntity
{
bool IsEnabled { get; set; }
}
public class Test : IBaseEntity, IEnabledEntity
{
// ...
public bool IsEnabled { get; set; }
}
But, I get exception about casting:
Unable to cast the type 'Domain.Test' to type 'Domain.Interfaces.IEnabledEntity'. LINQ to Entities only supports casting EDM primitive or enumeration types.
How to make it work?
Linq-to-Entities only knows models which are classes, that's why an expression can't contain an interface type. However clearly it's possible runtime to access the IsEnabled property if T implements it, so if you do the check yourself with IsAssignableFrom() (like you do), it's possible to use the ExpressionVisitor class to bypass the casting:
internal class IgnoreCast : ExpressionVisitor
{
protected override Expression VisitUnary(UnaryExpression e)
{
if(e.NodeType == ExpressionType.Convert && e.Type.IsAssignableFrom(typeof(e.Operand))
return e.Operand;
else
return e;
}
}
Then you need to create your filter with an extensionmethod which implements the IgnoreCast class:
internal static class LocalExtensions
{
internal static IgnoreCast ic = new IgnoreCast();
internal static IQueryable<T> FilterEnabled<T>(this IQueryable<T> query) where T: class
{
Expression<Func<T,bool>> expr = e => ((IEnabledEntity)e).IsEnabled;
expr = (Expression<Func<T,bool>>)ic.Visit(e);
return query.Where(expr);
}
}
Then you can just use that method in your program:
if(typeof(IEnabledEntity).IsAssignableFrom(T))
query = query.FilterEnabled();
The base method Visit(Expression e) will pass each node of the expression to a more specialized Visit method for that kind of node. The Convert nodetype is a UnaryExpression so this method will be overriden in the derived class. If the unaryexpression is of the Convert nodetype and the operand implements the type it will just return the operand, thus removing the casting.
The type parameter in IQueryable<T> is covariant, so instead of worrying about casting the entity in your expression, just safe-cast the entire query itself and then use Cast<T>() to get it back to your entity type:
public IQueryable<T> Get(Expression<Func<T, bool>> predicate, params string[] includes)
{
IQueryable<T> query = Context.Set<T>();
foreach (var include in includes)
{
query = query.Include(include);
}
query = query.Where(predicate);
var enabledQuery = query as IQueryable<IEnabledEntity>;
if (enabledQuery != null)
query = enabledQuery.Where(e => e.IsEnabled).Cast<T>();
return query;
}
I have a class called IDCollection which is not actually a List<>, Dictionary<,>, or similar IEnumerable-based type. Though it does have an indexer, it's based on a private Dictionary<string, ID> called innerList, so objects of the class themselves are not lists, nor does it hold key value pairs or a collection of items through its own this accessor.
Is it possible to make an IDCollection object queryable in a LINQ expression? If so, how?
For example, suppose the object is called testIdColl. In that case...
from KeyValuePair<string, ID> k in testIDColl
select k;
etc.
And is the IQueryable interface involved at all?
This is all you would need to do to your class:
public class IDCollection : IEnumerable<KeyValuePair<string, ID>>
{
private IDictionary<string, ID> List = new Dictionary<string, ID>();
public IEnumerator<KeyValuePair<string, ID>> GetEnumerator()
{
return List.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Then you could run this code:
var testIDColl = new IDCollection();
var query =
from KeyValuePair<string, ID> k in testIDColl
select k;
If you want you could make the entire IEnumerable<...> interface private like so:
public class IDCollection : IEnumerable<KeyValuePair<string, ID>>
{
private IDictionary<string, ID> List = new Dictionary<string, ID>();
IEnumerator<KeyValuePair<string, ID>> IEnumerable<KeyValuePair<string, ID>>.GetEnumerator()
{
return List.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return List.GetEnumerator();
}
}
Now nothing is directly exposed.
I created an implementation of the three instance methods from Jakub's answer:
public class IDCollection
{
private IDictionary<string, ID> List = new Dictionary<string, ID>() { { "x", new ID() }, } ;
public IEnumerable<KeyValuePair<string, ID>> Where(Func<KeyValuePair<string, ID>, bool> selector)
{
return List.Where(selector);
}
public IEnumerable<TResult> Select<TResult>(Func<KeyValuePair<string, ID>, TResult> selector)
{
return List.Select(selector);
}
}
The Cast method was not needed to perform basic queries.
I would suggest adding an explicit AsEnumerable() method instead to expose the full range of LINQ operators.
This would be the simplest and most robust way of doing queries:
public class IDCollection
{
private IDictionary<string, ID> List = new Dictionary<string, ID>() { { "x", new ID() }, } ;
public IEnumerable<KeyValuePair<string, ID>> AsEnumerable()
{
return List.Select(x => x);
}
}
The queries would have to look like:
var query =
from k in testIDColl.AsEnumerable()
where k.Key == "x"
select k;
The easiest solution is to implement IEnumerable<T>. It's only one method, which you can usually delegate to inner collection's GetEnumerator() or implement easily with yield return.
The name of your type (IDCollection) suggests that it should implement IEnumerable<T>, probably some other collection interface (e.g. ICollection<T>, IReadonlyCollection<T>).
If for some reason you don't want to use IEnumerable<T>, you can still make it work. First you need to understand how the compiler processes query expressions.
A LINQ query expression is first translated by the compiler to a series of method calls. For example the query expression
from KeyValuePair<string, int> item in collection
where item.Key == "abc"
select item.Value;
Is translated to
testIDColl
.Cast<KeyValuePair<string, int>>()
.Where(item => item.Key == "abc")
.Select(item => item.Value);
All you need to do to use a type as a source in a query expression is to make the above code compile. It means you will need Cast, Where, Select (and others too, like GroupBy, OrderBy) methods either as instance methods or extension methods.
For example the following class and extension methods will make the above query expression compile (although the methods don't do anything at all):
class IDCollection
{
}
static class IDCollectionExtensions
{
public static IDCollection Cast<TResult>(this IDCollection source)
{
return source;
}
public static IDCollection Where(this IDCollection source, Func<KeyValuePair<string, int>, bool> selector)
{
return source;
}
public static IDCollection Select<TResult>(this IDCollection source, Func<KeyValuePair<string, int>, TResult> selector)
{
return source;
}
}
Instance methods will work too:
class IDCollection
{
public IDCollection Cast<TResult>()
{
return this;
}
public IDCollection Where(Func<KeyValuePair<string, int>, bool> selector)
{
return this;
}
public IDCollection Select<TResult>(Func<KeyValuePair<string, int>, TResult> selector)
{
return this;
}
}
I'm trying to build a generic class to work with entities from EF. This class talks to repositories, but it's this class that creates the expressions sent to the repositories. Anyway, I'm just trying to implement one virtual method that will act as a base for common querying. Specifically, it will accept a an int and it only needs to perform a query over the primary key of the entity in question.
I've been screwing around with it and I've built a reflection which may or may not work. I say that because I get a NotSupportedException with a message of LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object, System.Object[])' method, and this method cannot be translated into a store expression. So then I tried another approach and it produced the same exception but with the error of The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities. I know it's because EF will not parse the expression the way L2S will.
Anyway, I'm hopping someone with a bit more experience can point me into the right direction on this. I'm posting the entire class with both attempts I've made.
public class Provider<T> where T : class {
protected readonly Repository<T> Repository = null;
private readonly string TEntityName = typeof(T).Name;
[Inject]
public Provider(
Repository<T> Repository) {
this.Repository = Repository;
}
public virtual void Add(
T TEntity) {
this.Repository.Insert(TEntity);
}
public virtual T Get(
int PrimaryKey) {
// The LINQ expression node type 'ArrayIndex' is not supported in
// LINQ to Entities.
return this.Repository.Select(
t =>
(((int)(t as EntityObject).EntityKey.EntityKeyValues[0].Value) == PrimaryKey)).Single();
// LINQ to Entities does not recognize the method
// 'System.Object GetValue(System.Object, System.Object[])' method,
// and this method cannot be translated into a store expression.
return this.Repository.Select(
t =>
(((int)t.GetType().GetProperties().Single(
p =>
(p.Name == (this.TEntityName + "Id"))).GetValue(t, null)) == PrimaryKey)).Single();
}
public virtual IList<T> GetAll() {
return this.Repository.Select().ToList();
}
protected virtual void Save() {
this.Repository.Update();
}
}
UPDATE for #Gabe
This is what my repository class looks like:
public class Repository<T> where T : class {
protected readonly ObjectContext ObjectContext = null;
private readonly IObjectSet<T> ObjectSet = null;
[Inject]
public Repository(
ObjectContext ObjectContext) {
this.ObjectContext = ObjectContext;
this.ObjectSet = this.ObjectContext.CreateObjectSet<T>();
}
public virtual void Delete(
T Entity) {
this.ObjectSet.DeleteObject(Entity);
}
public virtual void Insert(
T Entity) {
this.ObjectSet.AddObject(Entity);
}
public virtual IQueryable<T> Select() {
return this.ObjectSet;
}
public virtual IQueryable<T> Select(
Expression<Func<T, bool>> Selector) {
return this.ObjectSet.Where(Selector);
}
public virtual void Update() {
this.ObjectContext.SaveChanges();
}
}
The names of the methods are based on the SQL functions, not on the LINQ methods, which is where I think you're getting confused on how my repository functions.
As Pauli alludes to, you need to manually create Expression trees, although reflection isn't necessary in this case. Here's how you could write your Get function:
public virtual T Get(
int PrimaryKey)
{
var param = Expression.Parameter(typeof(T));
// create expression for param => param.TEntityNameId == PrimaryKey
var lambda = Expression.Lambda<Func<T, bool>>(
Expression.Equal(
Expression.Property(param, TEntityName + "Id"),
Expression.Constant(PrimaryKey)),
param);
return this.Repository.Single(lambda);
}
Also, note that your GetAll function doesn't need Select -- return Repository.ToList(); will work just as well.