Search code examples
c#expression-trees

Using Expressions to combine Funcs with different signature


I am using the following class to wrap some DocumentDB access which allows me to store multiple entities in the same collection:

public class TypedEntity<T> {
    public string Type { get; set; }

    public T Item { get; set; }

    public TypedEntity(T item) {
        Id = Guid.NewGuid().ToString();
        Item = item;
        Item.Id = Id;
        Type = typeof (T).FullName;
    } 
}

The usage of this class is encapsulated inside a repository class. I'm trying to build the API of the repository class such that the consumer doesn't need to know about the usage of TypedEntity<T> and can instead treat it as a source for just <T>. For example, the repository has a method with this signature:

public async Task<IQueryable<T>> WhereAsync(Func<T, bool> predicate)

In order to actually retrieve this data, the predicate needs to be combined/converted with one that interacts with TypedEntity<T>. This is the pseudo code that I'm picturing in my head for what I'd ultimately like to achieve:

public async Task<IQueryable<T>> WhereAsync(Func<T, bool> predicate) {
    // remembering that dataSource is talking to a backing store of TypedEntity<T>
    var queryable = dataSource.Where(x => x.Type == typeof(T).FullName && predicate(x.Item));
   // ... other business logic stuff
}

This actually builds but ultimately results in an Expression that uses .Invoke around the passed in predicate (which DocumentDb is unable to understand). Is there some way that I can combine the type part with the passed in Func to build up the Expression manually?


Solution

  • You'll want to take in an Expression<> rather than just a Func<>. Then, it should be fairly easy to apply it in a Where() clause after you've performed a Select() projection:

    public async Task<IQueryable<T>> WhereAsync(Expression<Func<T, bool>> predicate) {
        // remembering that dataSource is talking to a backing store of TypedEntity<T>
        var typeName = typeof(T).FullName;
        var queryable = dataSource.Where(x => x.Type == typeName)
            .Select(x => x.Item)
            .Where(predicate);
       // ... other business logic stuff
    }