Squeryl - Means of combinations of queries - performance

I have the need to perform some queries that may depend on external supplied parameters via a REST interface. For instance a client may require an URL of the form
some/entities?foo=12&bar=17
The parameters, say foo, bar and quux are all optional. So I start from designing some queries with Squeryl, like
object Entity {
def byFoo(id: Long) = from(DB.entities)(e =>
where(e.foo === id)
select(e)
)
// more criteria
}
But of course I want to avoid the combinatorial explosion that arises, so I only design three queries, which in turn may take their data from another query:
object Entity {
def byFoo(id: Long, source: Query[Entity] = DB.entites) = from(source)(e =>
where(e.foo === id)
select(e)
)
def byBar(id: Long, source: Query[Entity] = DB.entites) = from(source)(e =>
where(e.bar === id)
select(e)
)
// more criteria
}
Now I can combine them and run a query like
val result = Entity.byFoo(12,
source = Entity.byBar(17)
)
The only problem I have with this approach is that behind the scenes Squeryl is going to generate a subquery, which may be inefficient. With a more typical query builder, I would be able to combine the queries and get the equivalent of the following:
from(DB.entities)(e =>
where(
e.foo === 12 and
e.bar === 17
)
select(e)
)
Is there a different way to dynamically combine queries in Squeryl that will lead to this more efficient form?

The first thing I think you should look into is inhibitWhen, which you can find in the documentation. The gist of it is that you model your query like:
var foo: Option[Int] = None
var bar: Option[Int] = None
from(DB.entities)(e =>
where(
e.foo === foo.? and
e.bar === bar.?)
select(e))
The ? operator in the first expression is equivalent to (e.foo === foo).inhibitWhen(foo == None)

Related

How can QueryOver be used to filter for a specific class?

I am currently dynamically constructing queries like so:
QueryOver<Base, Base> q = QueryOver.Of<Base>();
if (foo != null) q = q.Where(b => b.Foo == foo);
// ...
Now there are multiple mapped subclasses of Base (e. g. Derived) which I want to filter on, basically something like:
if (bar) q = q.Where(b => b is Derived); // does not work
or:
if (bar) q = q.Where(b => b.DiscriminatorColumn == 'derived'); // dito
How do I best achieve that, preferably - but not neccessarily - in a type-safe way? Can this be done using LINQ, too?
This is not intuitive, but the following should work fine (QueryOver):
if (bar) q = q.Where(b => b.GetType() == typeof(Derived));
I'm not sure about a way to do this in LINQ-to-NH.
The general QueryOver, asking for a subtype, would look like this:
Base alias = null;
var query = session.QueryOver<Base>(() => alias);
// this statement would be converted to check of the discriminator
query.Where(o => o is Derived);
var list = query.List<Derived>();
But this would result in a statement, expecting that discirminator is "MyNamespace.Derived". If this si not the case, we can use this approach:
Base alias = null;
var query = session.QueryOver<Base>(() => alias);
// here we can compare whatever value, we've used as discriminator
query.Where(Restrictions.Eq("alias.class", "Derived"));
var list = query.List<Derived>();
Here we are using the feature of NHibernate: ".class" which does return the value of discriminator
More details could be found here:
17.1.4.1. Alias and property references

dynamic asc desc sort

I am trying to create table headers that sort during a back end call in nhibernate. When clicking the header it sends a string indicating what to sort by (ie "Name", "NameDesc") and sending it to the db call.
The db can get quite large so I also have back end filters and pagination built into reduce the size of the retrieved data and therefore the orderby needs to happen before or at the same time as the filters and skip and take to avoid ordering the smaller data. Here is an example of the QueryOver call:
IList<Event> s =
session.QueryOver<Event>(() => #eventAlias)
.Fetch(#event => #event.FiscalYear).Eager
.JoinQueryOver(() => #eventAlias.FiscalYear, () => fyAlias, JoinType.InnerJoin, Restrictions.On(() => fyAlias.Id).IsIn(_years))
.Where(() => !#eventAlias.IsDeleted);
.OrderBy(() => fyAlias.RefCode).Asc
.ThenBy(() => #eventAlias.Name).Asc
.Skip(numberOfRecordsToSkip)
.Take(numberOfRecordsInPage)
.List();
How can I accomplish this?
One way how to achieve this (one of many, because you can also use some fully-typed filter object etc or some query builder) could be like this draft:
Part one and two:
// I. a reference to our query
var query = session.QueryOver<Event>(() => #eventAlias);
// II. join, filter... whatever needed
query
.Fetch(#event => #event.FiscalYear).Eager
var joinQuery = query
.JoinQueryOver(...)
.Where(() => !#eventAlias.IsDeleted)
...
Part three:
// III. Order BY
// Assume we have a list of strings (passed from a UI client)
// here represented by these two values
var sortBy = new List<string> {"Name", "CodeDesc"};
// first, have a reference for the OrderBuilder
IQueryOverOrderBuilder<Event, Event> order = null;
// iterate the list
foreach (var sortProperty in sortBy)
{
// use Desc or Asc?
var useDesc = sortProperty.EndsWith("Desc");
// Clean the property name
var name = useDesc
? sortProperty.Remove(sortProperty.Length - 4, 4)
: sortProperty;
// Build the ORDER
order = order == null
? query.OrderBy(Projections.Property(name))
: query.ThenBy(Projections.Property(name))
;
// use DESC or ASC
query = useDesc ? order.Desc : order.Asc;
}
Finally the results:
// IV. back to query... call the DB and get the result
IList<Event> s = query
.List<Event>();
This draft is ready to do sorting on top of the root query. You can also extend that to be able to add some order statements to joinQuery (e.g. if the string is "FiscalYear.MonthDesc"). The logic would be similar, but built around the joinQuery (see at the part one)

LINQ to Entities does not recognize the method 'System.Collections.Generic.List`1[System.Int32]

I have the following error:
LINQ to Entities does not recognize the method
'System.Collections.Generic.List`1 [System.Int32] get_st_past_enrollment_success()'
method, and this method cannot be translated into a store expression.
This is caused by the following linq
IEnumerable<subject> _subjects = (from subject in context.subjects
where
subject.enrollments.Count() < subject.sj_max_enrollment
&& subject.sj_availability == true
&& !this.get_st_past_enrollment_success().Contains(subject.sj_subject_id)
select subject);
get_st_past_enrollment_success() returns a List:
public List<int> get_st_past_enrollment_success()
{
return this.enrollments.Where(e => e.em_enrolled == false && e.em_result >= 50).Select(e => e.em_subject_id).ToList();
}
What am i doing wrong here?
Your query itself contains the method call - and Entity Framework doesn't know what to do with that. Try extracting the list fetch to before the query:
var enrollments = get_st_past_enrollment_success();
var _subjects = from subject in context.subjects
where subject.enrollments.Count() < subject.sj_max_enrollment
&& subject.sj_availability
&& !enrollments.Contains(subject.sj_subject_id)
select subject;
Also note that get_st_past_enrollment_success violates .NET naming conventions - that won't affect whether the code works, but it'll look odd to other developers who are used to the normal conventions.

Reversing IQueryable based on passed property for sorting logic

I am implementing sort based on parameter passed to ascending or descending OrderBy method
else if (showGrid.Sortdir == "DESC")
{
alerts = DB.Incidents.OfType<Alert>().Where(
a =>
a.IncidentStatusID == (int)AlertStatusType.New ||
a.IncidentStatusID == (int)AlertStatusType.Assigned ||
a.IncidentStatusID == (int)AlertStatusType.Watching)
.OrderByDescending(a => showGrid.Sort);
}
else
{
alerts = DB.Incidents.OfType<Alert>().Where(
a =>
a.IncidentStatusID == (int)AlertStatusType.New ||
a.IncidentStatusID == (int)AlertStatusType.Assigned ||
a.IncidentStatusID == (int)AlertStatusType.Watching)
.OrderBy(a => showGrid.Sort);
}
In case of ascending order sorting it works fine but for descending order sorting doesn't work. I debugged the code and I found that list is not revered its same as ascending order. Please help me
Ok. I've written a small test. It is funny, but your code can actually compile and work, but very differently from what you expect :)
Obviously showGrid is not of type Alert, it is an instance of some other class, that incidentally have the same propery as Alert, called Sort.
First I was confused, because expected this code to fail to compile.
// The signature of OrderBy
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
// In your case it will result in
public static IOrderedQueryable<Alert> OrderBy<Alert, string>(this IQueryable<Alert> source, Expression<Func<Alert, string>> keySelector)
//when you call it like you do
DB.Incidents.OfType<Alert>().OrderByDescending(a => showGrid.Sort);
// You supply a property from object of type different from your entity.
// This is incorrect usage, the only object you can use here is the
// "a" argument. Like this:
DB.Incidents.OfType<Alert>().OrderByDescending(a => a.Sort);
// Because anything else does not make any sense to entity provider.
So your order by simply does not work.
As far as I understood, what you want is to perform sorting based on selection in UI. This is not easily achieved in strongly-typed LINQ. Because as I showed above, you send a property, not a value to the OrderBy. It does not care about the value inside the prop. So there are several solutions to the problem:
Write a big switch, that will check every possible Sort value, and will append appropriate 'OrderBy(a => a.YouPropToSort)' to the query. This is straitforward, and you should begin with this. Of course this is a static way, and will require to change code everytime you want new columns to be added for sorting.
Create argument for your OrderBy using 'LINQ Expression Trees'. For you case it should not be very hard to do. Look for the term, you will find a lot of examples.
Try to use Dynamic LINQ. I did not not use it myself, just looked at the docs. This seems to be an extension to the normal LINQ which allows you to write parts of queries as strings, to overcome limitations like the current one with dynamic sorting.
Here's my solution to sorting based on user selections:
Create your base query
var query = DB.Incidents.OfType<Alert>.Where(
a =>
a.IncidentStatusID == (int)AlertStatusType.New ||
a.IncidentStatusID == (int)AlertStatusType.Assigned ||
a.IncidentStatusID == (int)AlertStatusType.Watching);
and then apply your sort using a case statement
bool desc = showGrid.SortDir = "DESC";
switch(showGrid.Sort)
{
case "col1":
query = desc ? query.OrderByDescending( a => a.Col1 ) : query.OrderBy( a => a.Col1 );
break;
case "col2":
query = desc ? query.OrderByDescending( a => a.Col2 ) : query.OrderBy( a => a.Col2 );
break;
...
}
var results = query.ToList();

conditional include in linq to entities?

I felt like the following should be possible I'm just not sure what approach to take.
What I'd like to do is use the include method to shape my results, ie define how far along the object graph to traverse. but... I'd like that traversal to be conditional.
something like...
dealerships
.include( d => d.parts.where(p => p.price < 100.00))
.include( d => d.parts.suppliers.where(s => s.country == "brazil"));
I understand that this is not valid linq, in fact, that it is horribly wrong, but essentially I'm looking for some way to build an expression tree that will return shaped results, equivalent to...
select *
from dealerships as d
outer join parts as p on d.dealerid = p.dealerid
and p.price < 100.00
outer join suppliers as s on p.partid = s.partid
and s.country = 'brazil'
with an emphasis on the join conditions.
I feel like this would be fairly straight forward with esql but my preference would be to build expression trees on the fly.
as always, grateful for any advice or guidance
This should do the trick:
using (TestEntities db = new TestEntities())
{
var query = from d in db.Dealership
select new
{
Dealer = d,
Parts = d.Part.Where
(
p => p.Price < 100.0
&& p.Supplier.Country == "Brazil"
),
Suppliers = d.Part.Select(p => p.Supplier)
};
var dealers = query.ToArray().Select(o => o.Dealer);
foreach (var dealer in dealers)
{
Console.WriteLine(dealer.Name);
foreach (var part in dealer.Part)
{
Console.WriteLine(" " + part.PartId + ", " + part.Price);
Console.WriteLine
(
" "
+ part.Supplier.Name
+ ", "
+ part.Supplier.Country
);
}
}
}
This code will give you a list of Dealerships each containing a filtered list of parts. Each part references a Supplier. The interesting part is that you have to create the anonymous types in the select in the way shown. Otherwise the Part property of the Dealership objects will be empty.
Also, you have to execute the SQL statement before selecting the dealers from the query. Otherwise the Part property of the dealers will again be empty. That is why I put the ToArray() call in the following line:
var dealers = query.ToArray().Select(o => o.Dealer);
But I agree with Darren that this may not be what the users of your library are expecting.
Are you sure this is what you want? The only reason I ask is, once you add the filter on Parts off of Dealerships, your results are no longer Dealerships. You're dealing in special objects that are, for the most part, very close to Dealerships (with the same properties), but the meaning of the "Parts" property is different. Instead of being a relationship between Dealerships and Parts, it's a filtered relationship.
Or to put it another way, if I pull a dealership out of your results and passed to a method I wrote, and then in my method I call:
var count = dealership.Parts.Count();
I'm expecting to get the parts, not the filtered parts from Brazil where the price is less than $100.
If you don't use the dealership object to pass the filtered data, it becomes very easy. It becomes as simple as:
var query = from d in dealerships
select new { DealershipName = d.Name,
CheapBrazilProducts = dealership.Parts.Where(d => d.parts.Any(p => p.price < 100.00) || d.parts.suppliers.Any(s => s.country == "brazil")) };
If I just had to get the filtered sets like you asked, I'd probably use the technique I mentioned above, and then use a tool like Automapper to copy the filtered results from my anonymous class to the real class. It's not incredibly elegant, but it should work.
I hope that helps! It was an interesting problem.
I know this can work with one single Include. Never test with two includes, but worth the try:
dealerships
.Include( d => d.parts)
.Include( d => d.parts.suppliers)
.Where(d => d.parts.All(p => p.price < 100.00) && d.parts.suppliers.All(s => s.country == "brazil"))
Am I missing something, or aren't you just looking for the Any keyword?
var query = dealerships.Where(d => d.parts.Any(p => p.price < 100.00) ||
d.parts.suppliers.Any(s => s.country == "brazil"));
Yes that's what I wanted to do I think the next realease of Data Services will have the possiblity to do just that LINQ to REST queries that would be great in the mean time I just switched to load the inverse and Include the related entity that will be loaded multiple times but in theory it just have to load once in the first Include like in this code
return this.Context.SearchHistories.Include("Handle")
.Where(sh => sh.SearchTerm.Contains(searchTerm) && sh.Timestamp > minDate && sh.Timestamp < maxDate);
before I tried to load for any Handle the searchHistories that matched the logic but don't know how using the Include logic you posted so in the mean time I think a reverse lookup would be a not so dirty solution

Resources