How to make a linq query wrapper function? - linq

I have come accross this code on SO and I am trying to convert it to VB.net however I cannot find type 'Result'
public Result QueryWithRetry(IQueryable query)
{
RetryPolicy retry = new RetryPolicy<MyRetryStrategy>(5, TimeSpan.FromSeconds(5));
(() =>
{
return retry.ExecuteAction(query);
}
}
What is 'Result' and what namespace do I need to import to access that? I can't find it anywhere!

Related

Call custom filtering method on IQueryable

I have this linq query (using linq 4.3.0):
return ExecuteODataQuery(Db.AdvisorFees, f => f.Advisor);
Implemented as follows:
protected IQueryable<TType> ExecuteODataQuery<TType>(IQueryable<TType> queryFunc, Func<TType, Advisor> advisorFunc) where TType : Entity
{
var systemAccount = GetSystemAccountThrow();
var results = queryFunc.Where(x => x.Active).AsEnumerable().Where(x => CheckAdvisorOrChannel(systemAccount, advisorFunc(x)));
return results;
}
This is my custom method:
public static bool CheckAdvisorOrChannel(SystemAccount systemAccount, Advisor advisor)
{
return (systemAccount.IsChannelSystemAccount ? systemAccount.Channel.Code == advisor.Channel.Code : systemAccount.Advisor.Code == advisor.Code);
}
This currently works. If I remove the AsEnumerable() I get a LinqToEntities not supported exception on my custom method. I cannot use AsEnumerable() as I have too many records and do not want to do the rest of the filtering in memory. Is it possible to call a custom method on IQueryable instead? I've been playin with extension methods and expressions but have not managed to get this working yet. Any pointers?
I would prefer to not use expression trees as much as possible.

Why QueryContainer is not updating from a Descriptor NESt C#

Hi I have following descriptors in my NEST query...
queryContainer.DateRange(b => dateRangeDescriptor);
queryContainer.MatchPhrase(b => matchPhraseDescriptor);
And finally I use this QueryContainerDescriptor in the following BoolQueryDescriptor
boolDescriptor.Must(q => queryContainer);
The problem is although I could see values in my dateRangeDescriptor as well as matchPhraseDescriptor, it is not available in side queryContainer.
Not sure what is going wrong here.
Must has the following overloads (in NEST 2.x)
public BoolQueryDescriptor<T> Must(
params Func<QueryContainerDescriptor<T>, QueryContainer>[] queries)
{
// impl
}
public BoolQueryDescriptor<T> Must(
IEnumerable<Func<QueryContainerDescriptor<T>, QueryContainer>> queries)
{
// impl
}
public BoolQueryDescriptor<T> Must(
params QueryContainer[] queries)
{
// impl
}
So you need to pass a collection of queries to apply multiple must clauses rather than adding them all to one QueryContainer.

Linq Parsing Error when trying to create seperation of concerns

I am in the middle of a refactoring cycle where I converted some extension methods that used to look like this:
public static IQueryable<Family> FilterOnRoute(this IQueryable<Family> families, WicRoute route)
{
return families.Where(fam => fam.PODs
.Any(pod => pod.Route.RouteID == route.RouteID));
}
to a more fluent implementation like this:
public class SimplifiedFamilyLinqBuilder
{
private IQueryable<Family> _families;
public SimplifiedFamilyLinqBuilder Load(IQueryable<Family> families)
{
_families = families;
return this;
}
public SimplifiedFamilyLinqBuilder OnRoute(WicRoute route)
{
_families = _families.Where(fam => fam.PODs
.Any(pod => pod.Route.RouteID == route.RouteID));
return this;
}
public IQueryable<Family> AsQueryable()
{
return _families;
}
}
which I can call like this: (note this is using Linq-to-Nhibernate)
var families =
new SimplifiedFamilyLinqBuilder()
.Load(session.Query<Family>())
.OnRoute(new WicRoute() {RouteID = 1})
.AsQueryable()
.ToList();
this produces the following SQL which is fine with me at the moment: (of note is that the above Linq is being translated to a SQL Query)
select ... from "Family" family0_
where exists (select pods1_.PODID from "POD" pods1_
inner join Route wicroute2_ on pods1_.RouteID=wicroute2_.RouteID
where family0_.FamilyID=pods1_.FamilyID
and wicroute2_.RouteID=#p0);
#p0 = 1
my next effort in refactoring is to move the query part that deals with the child to another class like this:
public class SimplifiedPODLinqBuilder
{
private IQueryable<POD> _pods;
public SimplifiedPODLinqBuilder Load(IQueryable<POD> pods)
{
_pods = pods;
return this;
}
public SimplifiedPODLinqBuilder OnRoute(WicRoute route)
{
_pods = _pods.Where(pod => pod.Route.RouteID == route.RouteID);
return this;
}
public IQueryable<POD> AsQueryable()
{
return _pods;
}
}
with SimplifiedFamilyLinqBuilder changing to this:
public SimplifiedFamilyLinqBuilder OnRoute(WicRoute route)
{
_families = _families.Where(fam =>
_podLinqBuilder.Load(fam.PODs.AsQueryable())
.OnRoute(route)
.AsQueryable()
.Any()
);
return this;
}
only I now get this error:
Remotion.Linq.Parsing.ParserException : Cannot parse expression 'value(Wic.DataTests.LinqBuilders.SimplifiedPODLinqBuilder)' as it has an unsupported type. Only query sources (that is, expressions that implement IEnumerable) and query operators can be parsed.
I started to implement IQueryable on SimplifiedPODLinqBuilder(as that seemed more logical than implementing IEnumberable) and thought I would be clever by doing this:
public class SimplifiedPODLinqBuilder : IQueryable
{
private IQueryable<POD> _pods;
...
public IEnumerator GetEnumerator()
{
return _pods.GetEnumerator();
}
public Expression Expression
{
get { return _pods.Expression; }
}
public Type ElementType
{
get { return _pods.ElementType; }
}
public IQueryProvider Provider
{
get { return _pods.Provider; }
}
}
only to get this exception (apparently Load is not being called and _pods is null):
System.NullReferenceException : Object reference not set to an instance of an object.
is there a way for me to refactor this code out that will parse properly into an expression that will go to SQL?
The part fam => _podLinqBuilder.Load(fam.PODs.AsQueryable() is never going to work, because the linq provider will try to parse this into SQL and for that it needs mapped members of Family after the =>, or maybe a mapped user-defined function but I don't know if Linq-to-Nhibernate supports that (I never really worked with it, because I still doubt if it is production-ready).
So, what can you do?
To be honest, I like the extension methods much better. You switched to a stateful approach, which doesn't mix well with the stateless paradigm of linq. So you may consider to retrace your steps.
Another option: the expression in .Any(pod => pod.Route.RouteID == route.RouteID)); could be paremeterized (.Any(podExpression), with
OnRoute(WicRoute route, Expression<Func<POD,bool>> podExpression)
(pseudocode).
Hope this makes any sense.
You need to separate methods you intend to call from expressions you intend to translate.
This is great, you want each of those methods to run. They return an instance that implements IQueryable<Family> and operate on that instance.
var families = new SimplifiedFamilyLinqBuilder()
.Load(session.Query<Family>())
.OnRoute(new WicRoute() {RouteID = 1})
.AsQueryable()
.ToList();
This is no good. you don't want Queryable.Where to get called, you want it to be an expression tree which can be translated to SQL. But PodLinqBuilder.Load is a node in that expression tree which can't be translated to SQL!
families = _families
.Where(fam => _podLinqBuilder.Load(fam.PODs.AsQueryable())
.OnRoute(route)
.AsQueryable()
.Any();
You can't call .Load inside the Where expression (it won't translate to sql).
You can't call .Load outside the Where expression (you don't have the fam parameter).
In the name of "separation of concerns", you are mixing query construction methods with query definition expressions. LINQ, by its Integrated nature, encourages you to attempt this thing which will not work.
Consider making expression construction methods instead of query construction methods.
public static Expression<Func<Pod, bool>> GetOnRouteExpr(WicRoute route)
{
int routeId = route.RouteID;
Expression<Func<Pod, bool>> result = pod => pod.Route.RouteID == route.RouteID;
return result;
}
called by:
Expression<Func<Pod, bool>> onRoute = GetOnRouteExpr(route);
families = _families.Where(fam => fam.PODs.Any(onRoute));
With this approach, the question is now - how do I fluidly hang my ornaments from the expression tree?

compiling a linq to sql query

I have queries that are built like this:
public static List<MyObjectModel> GetData (int MyParam)
{
using (DataModel MyModelDC = new DataModel())
{ var MyQuery = from....
select MyObjectModel { ...}
}
return new List<MyObjectModel> (MyQuery)
}
}
It seems that using compiled linq-to-sql queries are about as fast as stored procedures and so the goal is to convert these queries into compiled queries. What's the syntax for this?
Thanks.
Put something like this inside of your DataContext (or in your case your "DataModel"):
private static Func<DataModel, int, MyObjectModel> _getObjectModelById =
CompiledQuery.Compile<DataModel, int, MyObjectModel>(
(dataModel, myParam) =>
dataModel.PersonDtos.where(c => c.ObjectModelId == myParam).FirstOrDefault()
);
Then add amethod in there to call it like this:
internal List<MyObjectModel> GetObjectModel(int myParam)
{
var results = _getObjectModelById(this, myParam);
return results.SingleOrDefault();
}
Inside of your repository where your original method was call the internal function to get the result you are looking for.
Hope this helps -> I can post more code if necessary :)

Converting Foreach Loop to Linq and getting error

I've currently got the following foreach loop:
List<SearchResult> searchResults = new List<SearchResult>();
foreach (Transmission trans in Results)
{
searchResults.Add(new SearchResult(trans));
}
return searchResults;
And I'd like to convert this to a Linq expression, I've tried the following which looks like it achieve the same thing in linq to me:
return Results.Select(x => new SearchResult(x)).ToList();
However when executed I get the following error:
System.InvalidCastException: Object must implement IConvertible.
I think I understand the gist of that error but the issue I have is that I'm not actually trying to convert the Transmission Objects in the Results collection to SearchResult objects but instead to return a list of SearchResult objects, a SearchResult object being intialized like so:
Transmission transmission = new Transmission(...);
SearchResult result = new SearchResult(trans);
Any help on this would be great, I've been tearing my hair out!
EDIT: As per comments here is the full method stub:
public IQueryable<Transmission> Results
{
get;
set;
}
public virtual IEnumerable<SearchResult> ResultsNetwork
{
get
{
List<SearchResult> searchResults = new List<SearchResult>();
foreach (Transmission trans in Results)
{
searchResults.Add(new SearchResult(trans));
}
return searchResults;
}
}
I get the impression that Results is a collection of object, so Select is defining x as object, but you want it to be Transmission.
Try either of these options:
return Results.Cast<Transmission>().Select(x => new SearchResult(x)).ToList();
return Results.OfType<Transmission>().Select(x => new SearchResult(x)).ToList();
Cheers.
Its hard to guess what you are trying to cast (you need to show your method signature, as well as a blurb showing how you convert Transmissions to SearchResult)
However and easier way to do it:
return Results.ConvertAll(x=> new SearchResult(x));

Resources