While somewhat powerful, the System.Linq.Dynamic library has a surprising lack of documentation, especially in regards to what conventions must be followed for more complex queries.
In a query I'm working on, it contains a FirstOrDefault call, but I can't seem to get it to work.
Here's the whole (unworking) expression:
"Locations.FirstOrDefault(x => x.IsPrimaryLocation).Address1 as Address"
Can I write this FirstOrDefault expression to work with Dynamic linq?
What is the correct way to write this expression?
Extending the dynamic library is certainly an option as already suggested.
an alternative given the Where in Dynamic Linq returns an Iqueryable
public static class DynamicQueryable {
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values) { return (IQueryable<T>) Where((IQueryable) source, predicate, values); }
public static IQueryable Where(this IQueryable source, string predicate, params object[] values) {
using a DYnamic Object for the context or repository "locations".
Then use a where which could contain dynamic string predicate and follow with firstOrDefault.
(catch or test for null not considered)
DynamicLocations.Where(x => x.IsPrimaryLocation).FirstOrDefault( ).Address1 as Address;
or dynamic where if needed
DynamicLocations.Where("IsPrimaryLocation",new string[]).FirstOrDefault( ).Address1 as Address;
Details:
You can expose on a method on a generic repository Class which you instantiate as a dynamic
public virtual IQueryable<TPoco> DynamicWhere(string predicate, params object[] values) {
return AllQ().Where(predicate, values);
}
Dynamic Generic Repository instantiation Sample
public class RepositoryFactory<TPoco> where TPoco : BaseObject,new() {
public IRepositoryBase<TPoco> GetRepository(DbContext context) {
// get the Pocotype for generic repository instantiation
var pocoTypes = new[] {typeof (TPoco)}; // but supports <T,U>
Type repBaseType = typeof (RepositoryBase<>);
IRepositoryBase<TPoco> repository = InstantiateRepository(context, repBaseType, pocoTypes);
return repository;
}
private IRepositoryBase<TPoco> InstantiateRepository(DbContext context, Type repType, params Type[] args) {
Type repGenericType = repType.MakeGenericType(args);
object repInstance = Activator.CreateInstance(repGenericType, context);
return (IRepositoryBase<TPoco>)repInstance;
}
}
Related
Assuming that we have an object with the following attributes:
public class MyObject {
private String attr1;
private Integer attr2;
//...
public String getAttr1() {
return this.attr1;
}
public Integer getAttr2() {
return this.attr2;
}
}
One way of sorting a list mylist of this object, based on its attribute attr1 is:
mylist.sort(Comparator.comparing(MyObject::getAttr1));
Is it possible to use this code inside a method in a dynamic way and replace the getAttr1 part with a method that returns the getter of an attribute of the object based on its name? Something like:
public void sortListByAttr(List<MyObject> list, String attr) {
list.sort(Comparator.comparing(MyObject::getGetterByAttr(attr)));
}
The MyObject::getGetterByAttr(attr) part does not compile, I wrote it just as an example to explain my idea
I tried to implement a method with the following code new PropertyDescriptor(attr, MyObject.class).getReadMethod().invoke(new MyObject()) but It's still not possible to call a method with a parameter from the comparing method
You could add a method like
public static Function<MyObject,Object> getGetterByAttr(String s) {
switch(s) {
case "attr1": return MyObject::getAttr1;
case "attr2": return MyObject::getAttr2;
}
throw new IllegalArgumentException(s);
}
to your class, but the returned function is not suitable for Comparator.comparing, as it expects a type fulfilling U extends Comparable<? super U> and while each of String and Integer is capable of fulfilling this constraint in an individual invocation, there is no way to declare a generic return type for getGetterByAttr to allow both type and be still compatible with the declaration of comparing.
An alternative would be a factory for complete Comparators.
public static Comparator<MyObject> getComparator(String s) {
switch(s) {
case "attr1": return Comparator.comparing(MyObject::getAttr1);
case "attr2": return Comparator.comparing(MyObject::getAttr2);
}
throw new IllegalArgumentException(s);
}
to be used like
public void sortListByAttr(List<MyObject> list, String attr) {
list.sort(getComparator(attr));
}
This has the advantage that it also may support properties whose type is not Comparable and requires a custom Comparator. Also, more efficient comparators for primitive types (e.g. using comparingInt) would be possible.
You may also consider using a Map instead of switch:
private static Map<String,Comparator<MyObject>> COMPARATORS;
static {
Map<String,Comparator<MyObject>> comparators=new HashMap<>();
comparators.put("attr1", Comparator.comparing(MyObject::getAttr1));
comparators.put("attr2", Comparator.comparing(MyObject::getAttr2));
COMPARATORS = Collections.unmodifiableMap(comparators);
}
public static Comparator<MyObject> getComparator(String s) {
Comparator<MyObject> comparator = COMPARATORS.get(s);
if(comparator != null) return comparator;
throw new IllegalArgumentException(s);
}
More dynamic is only possible via Reflection, but this would complicate the code, add a lot of potential error source, with only little benefit, considering that you need only to add one line of source code for adding support for another property in either of the examples above. After all, the set of defined properties gets fixed at compile time.
You could also have a single place where this comparators would be defined:
static enum MyObjectComparator {
ATTR1("attr1", Comparator.comparing(MyObject::getAttr1));
MyObjectComparator(String attrName, Comparator<MyObject> comparator) {
this.comparator = comparator;
this.attrName = attrName;
}
private final Comparator<MyObject> comparator;
private final String attrName;
private static MyObjectComparator[] allValues = MyObjectComparator.values();
public static Comparator<MyObject> findByValue(String value) {
return Arrays.stream(allValues)
.filter(x -> x.attrName.equalsIgnoreCase(value))
.map(x -> x.comparator)
.findAny()
.orElseThrow(RuntimeException::new);
}
}
And your usage would be:
public void sortListByAttr(List<MyObject> list, String attr) {
list.sort(MyObjectComparator.findByValue(attr));
}
I am trying to apply additional server-side filtering to entities exposed by an asp.net OData service, using Linq to Entities.
So far so good, but the Linq query that I build for the filter can sometimes get to an enormous length, causing a StackOverflowException.
As a workaround, I want to execute the query in a separate thread, giving that thread a bigger stack size limit. To achieve this, I implemented an IQueryable and IQueryProvider wrappers, so that when the IQueryable's Execute method is called, it would be executed in a separate thread.
To keep it simple and focused on my problem, I will omit the threading part in the following code samples, as this is not the issue here.
Here's the relevant parts of the code:
public class MyQueryable<T> : IQueryable<T> {
private IQueryable<T> _Inner { get; set; }
private IQueryProvider _Provider { get; set; }
public MyQueryable(IQueryable<T> inner) {
_Inner = inner;
_Provider = new MyQueryProvider(inner.Provider);
}
public Type ElementType {
get { return _Inner.ElementType; }
}
public Expression Expression {
get { return _Inner.Expression; }
}
public IQueryProvider {
get { return _Provider; }
}
/* ... Implementations of GetEnumerator ... */
}
public class MyQueryProvider : IQueryProvider {
private IQueryProvider _Inner { get; set; }
public MyQueryProvider(IQueryProvider inner) {
_Inner = inner;
}
public IQueryable<T> CreateQuery<T>(Expression expression) {
return new MyQueryable<T>(_Inner.CreateQuery<T>(expression));
}
public T Execute<T>(Expression expression) {
// The problem occurs on the following line.
return _Inner.Execute<T>(expression);
}
/* ... Implementation of the non-generic versions of CreateQuery, Execute ... */
}
When I run this, I get an InvalidOperationException in MyQueryProvider.Execute method:
Cannot compare elements of type 'System.Collections.Generic.ICollection`1'.
Only primitive types (such as Int32, String, and Guid) and entity types are supported.
This is how I build the original IQueryable that is being passed to MyQueryable:
I get the DbSet from my entities context, apply OData filter query option on it, and call Count(). Something along the lines of:
((odataQueryOptions.Filter.ApplyTo(
context.Entities.AsQueryable(), new ODataQuerySettings()))
as IQueryable<Entity>).Count()
I partially identified that the problem is that the odata filter query option contains a nested 'any' filter, something like:
Entities/$filter=RelatedEntities/any(entity: (entity/ID eq 1))
which adds a check to the IQueryable's Where expression whether the RelatedEntities collection is null. This check is suppostedly what causes the above exception.
What I cannot understand, is why the original IQueryable fails when I try to delegate the Execute method's execution to it from the MyQueryable.Execute method, but it all works fine when I use the original IQueryable directly.
Any help on this would be highly appreciated. Thanks in advance.
I'm implementing the service \ repository pattern in a new project. I've got a base interface that looks like this. Everything works great until I need to use the GetMany method. I'm just not sure how to pass a LINQ expression into the GetMany method. For example how would I simply sort a list of objects of type name?
nameRepository.GetMany( ? )
public interface IRepository<T> where T : class
{
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
T GetById(long Id);
T GetById(string Id);
T Get(Expression<Func<T, bool>> where);
IEnumerable<T> GetAll();
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return dbset.Where(where).ToList();
}
Assuming that you had an implementation of IRepository<MyClass>, you would make a call to GetMany like so:
IRepository<MyClass> repository = ...;
IEnumerable<MyClass> filtered = repository.GetMany(
mc => true);
Note the mc => true lambda expression. mc in this case is the parameter (in this case, of type MyClass) passed to the Expression<Func<T, bool>> which will be evaluated (one would assume through an IQueryable<T>) and the mc.SomeProperty == someValue is an expression which returns true, which is the second type parameter of the Expression<Func<T, bool>>.
Once you have the filtered, you can use the order by clause (or the OrderBy extension method, they're the same) to sort the results, like so:
var filteredAndOrdered = filtered.OrderBy(mc => mc.MyProperty);
Note that GetMany returns an IEnumerable<T>, not an IQueryable<T>; this is important if your result set is large, as the ordering will happen on the client, and not be sent to the server.
For an OrderBy operation, this can be expensive because it has to go through the entire sequence (to order) before returning the first result.
I'm trying to do some eager loading on an EF Entity.
so, if the entity is called Orders .. then I guess i would do the following...
_someContext.Orders.Include("Whatever") ....
But the problem is, I have a method like the following ...
public IQueryable<Order> Find(Expression<Func<Order, bool>> predicate)
{
return CurrentContext.Orders.Where(predicate);
}
which works great .. but can i leverage the Expression predicate to include the Include("whatever") in there, instead of having to add another method parameter?
I don't think so. Since the predicate and ObjectQuery.Where Method in genral has nothing to do with eager loading by Include. You can create a extension method though, but that does not save you from having yet another parameter for specifying the include:
public IQueryable<Order> Find(Expression<Func> predicate, string include) {
return CurrentContext.Orders.Where(predicate, include);
}
public static ObjectQuery<T> Where<T>(this ObjectQuery<T> entity, Expression<Func<T, bool>> predicate, string include) {
return (ObjectQuery<T>)entity.Include(include).Where<T>(predicate);
}
In my code I'd like to make my repositories IQueryable. This way, the criteria for selection will be a linq expression tree.
Now if I want to mock my repository in theorie this is very easy : just implement the interface of my repository (which is also a IQueryable object).
My mock repository implementation would be only a in memory collection, but my question is : Do you know an easy way to implement the IQueryable interface of my mock, to transfer the query to my in-memory collection (IEnumerable) ?
Thanks for your response,
Some precision
The client object of my repository will use my repository this way :
var result = from entry in MyRepository where entry.Product == "SomeProduct" select entry;
What does ToList or AsEnumerable is to execute the query and return the result as a List or as a IEnumerable. But I have to implement the IQueryable interface on my repository, with a IQueryProvider which transform the expression in a call to a IEnumerable object.
Solution
The implementation of the solution is delegating call to IQueryable to my inmemory collection with AsQueryable.
public class MockRepository : IQueryable<DomainObject>
{
private List<DomainObject> inMemoryList = new List<DomainObject>();
#region IEnumerable<DomainObject> Members
public IEnumerator<DomainObject> GetEnumerator()
{
return inMemoryList.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return inMemoryList.GetEnumerator();
}
#endregion
#region IQueryable Members
public Type ElementType
{
get
{
return inMemoryList.AsQueryable().ElementType;
}
}
public Expression Expression
{
get
{
return inMemoryList.AsQueryable().Expression;
}
}
public IQueryProvider Provider
{
get
{
return inMemoryList.AsQueryable().Provider;
}
}
#endregion
}
Use AsQueryable on your mocks. Now they're queryable and you can treat them like any other queryable.
Can you use the extension method
.ToList<>