Lambda syntax in linq to db4o? - c#

I know the following is possible with linq2db4o
from Apple a in db
where a.Color.Equals(Colors.Green)
select a
What I need however is something that allows me to build my query conditionally (like I can in other linq variants)
public IEnumerable<Apple> SearchApples (AppleSearchbag bag){
var q = db.Apples;
if(bag.Color != null){
q = q.Where(a=>a.Color.Equals(bag.Color));
}
return q.AsEnumerable();
}
In a real world situation the searchbag will hold many properties and building a giant if-tree that catches all possible combinations of filled in properties would be madman's work.
It is possible to first call
var q = (from Color c in db select c);
and then continue from there. but this is not exactly what I'm looking for.
Disclaimer: near duplicate of my question of nearly 11 months ago.
This one's a bit more clear as I understand the matter better now and I hope by now some of the db4o dev eyes could catch this on this:
Any suggestions?

Yes it's definitely possible to compose optimized LINQ queries using db4o. Granted that db is defined as follows:
IObjectContainer db;
Here is your query:
public IEnumerable<Apple> SearchApples (AppleSearchbag bag)
{
var query = db.Cast<Apple> ();
// query will be a Db4objects.Db4o.Linq.IDb4oLinqQuery<Apple>
if (bag.Color != null)
query = query.Where (a => a.Color == bag.Color);
return query;
}
In that case, the query will be executed whenever the returned enumerable is being iterated over.
Another possibility is to use the IQueryable mechanism, that has the advantage of being better recognized by developers:
public IQueryable<Apple> SearchApples (AppleSearchbag bag)
{
var query = db.AsQueryable<Apple> ();
// query will be a System.Linq.IQueryble<Apple>
if (bag.Color != null)
query = query.Where (a => a.Color == bag.Color);
return query;
}
In both cases, db4o will try to deduce an optimized query from the lambda expression upon execution, and if it fails, will fallback to LINQ to objects. The first one has the advantage of being more direct, by avoiding the queryable to LINQ to db4o transformation.

Related

Does .First() operate at linq level or on the returned enumerable collection?

I came across a colleagues code and thought it was possibly inefficient
bool any = (from c in listDeviceMaxDate
where c.DeviceKey == m_deviceList[i].deviceKey
select c).Any();
if (!any)
{
latestDate = (DateTime)System.Data.SqlTypes.SqlDateTime.MinValue;
}
else
{
// from the list we have get the lastest max date from the flow table
DeviceDateTimeItem temp = (from c in listDeviceMaxDate
where c.DeviceKey == m_deviceList[i].deviceKey
select c).First();
latestDate = Convert.ToDateTime(temp.dateTimeMax);
}
My first instinct is to store the linq query and then just reference it as needed but then I realised that the First() operator may prevent linq from actually getting ALL the rows which an unconstrained query would do.
How I initially thought about restructuring the code:
var deviceList = from c in listDeviceMaxDate
where c.DeviceKey == m_deviceList[i].deviceKey
select c;
if (!deviceList.Any())
{
latestDate = (DateTime)System.Data.SqlTypes.SqlDateTime.MinValue;
}
else
{
// from the list we have get the lastest max date from the flow table
DeviceDateTimeItem temp = deviceList.First();
latestDate = Convert.ToDateTime(temp.dateTimeMax);
}
My question is does the First() call on the second linq query prevent it from returning all results, and as such, is it actually quicker to do it the original way?
It actually depends on what LINQ implementation it is. If it is LINQ-to-Objects (i.e. IEnumerable<T>), then it is basically just enumerating the data whatever it is, and returning the first item if one. So First() is the moral equivalent of:
foreach(var val in sequence) return val;
throw OopsNoData();
and Any() should compare well to:
foreach(var val in sequence) return true;
return false;
(it probably uses a raw iterator in the actual implementation, rather than foreach)
However! If it is LINQ-to-anything-else, all bets are off. LINQ queries (especially IQueryable<T>) are designed to be composable - and I would expect LINQ-to-SQL, for example, to make First() into a select TOP 1 ... TSQL query, and similarly for most other database-backends. So yes, telling it that you only want one row should be helpful. However! I would also expect .Any() to do something very similar, so that shouldn't (in theory) be a big difference. In a perfect world, it might even use exists(...) in the TSQL, but the world is far from perfect.
The way to find out: attach a sql tracer, and see what the final TSQL was.
The ultimate way to do this is simpler:
var obj = someQuery.FirstOrDefault();
if(obj == null) {
// no match
} else {
// do something with "obj"
}

Can't get deferred LINQ statements to work in nHibernate

Trying to write dynamic queries using the LINQ provider for NHibernate, but I am having issues. My understanding was that LINQ queries were deferred until called, (i.e. with ToList()), so I have the following code:
string[] filteredIds = new[] { "someIdNotInUse"};
var result = _products
.GetAll()
.Skip(0)
.Take(10);
if (filteredIds != null)
{
result.Where(x => x.Child1.Child2.Any(z => filteredIds.Contains(z.Child3.Id)));
}
var r = result.ToList();
The Where filter in the conditional block is not applied; when I run .ToList, I get records where I expect none. However, if I remove the where filter and append it directly to the _products call, it works as expected. Am I misunderstanding how the LINQ provider works? How is creating a query like this possible, without rewriting the query for every possible filter condition and combination?
Methods in LINQ don't affect the object they're called on - they return a new object representing the result of the call. So you want:
if (filteredIds != null)
{
result = result.Where(...);
}
(Think of it as being a bit like calling Replace or Trim on a string - the string is immutable, so it's only the return value which matters.)

Dynamically Building a List of Func<t,t> - then applying to a linq query

Not sure if this is the best approach, however here are my thoughts
I am using Entity Framework Data Model v 4.1 - i'm trying to build a where statement dynamically so that I dont have to query the db everytime, instead i can build a "list of" conditionals, then apply them all at once so the db is only queryed once as opposed to everytime i add my new conditional - if that makes sense...
here is what i have
List<Func<Order, bool>> orderFunctions = new List<Func<Order, bool>>();
if (this.LoggedInUser.UserId == 9)
{
Func<Order, bool> deleg = o => o.Status.Equals(OrderStatus.NewOrder);
orderFunctions.Add(deleg);
}
else if (this.LoggedInUser.UserId == 22)
{
Func<Order, bool> deleg = o => o.AssignedToUserId.Equals(22);
orderFunctions.Add(deleg);
}
List<Orders> orders = Entities.Orders.Where( Some How Apply my Order Functions List Here ).ToList();
I'm not sure if i'm even taking the right approach here - hopefully this makes sense - any guidance, sample code would be fantastic, i've having a heck of a time finding examples / tutorials for this online
You can do this just by calling Where multiple times:
IQueryable<Order> query = Entities.Orders;
if (this.LoggedInUser.UserId == 9)
{
query = query.Where(o => o.Status.Equals(OrderStatus.NewOrder));
}
else if (this.LoggedInUser.UserId == 22)
{
query = query.Where(o => o.AssignedToUserId.Equals(22));
}
List<Order> orders = query.ToList();
Until you start trying to retrieve the results, it won't contact the database.
If you really want to create a list, you'd need to create a List<Expression<Func<Order, bool>>> - they have to be expression trees rather than delegates, so that the Entity Framework can deal with them. Then you could just do:
foreach (var predicate in predicates)
{
query = query.Where(predicate);
}
Or you could use PredicateBuilder to build up a single expression tree.
Jon gave - of course - the answer how to do this better.
But for your original point, and why this won't work:
It is not dificult to apply the hole list (just do
x => orderFunctions.All(f => f(x))
) but you will not get any SQL-love from this - meaning you will get an error because EF cannot know what to do with the all (or whatever you will code there).
You would have to query all entries (with ToList, or ToArray) and after this it would work ... but without performance ;)

Avoid "Nullable object must have a value." in Linq-To-Sql

I have a method query like this:
public IList<BusinessObject> GetBusinessObject(Guid? filterId)
{
using (var db = new L2SDataContext())
{
var result = from bo in db.BusinessObjects
where (filterId.HasValue)
? bo.Filter == filterId.value
: true
orderby bo.Name
select SqlModelConverters.ConvertBusinessObject(bo);
return result.ToList();
}
}
At runtime, this throws a System.InvalidOperationException: Nullable object must have a value.
Looking at the Debugger, the problem is my Where Clause: Linq To SQL tries to convert the entire thing to SQL, so even if filterId is NULL, it will still try to access filterId.value.
I thought/hoped the C# compiler/CLR would evaluate that where clause as a code block and only send one of the two branches to Linq To SQL, but that's not how it works.
My refactored version works, but is not very elegant:
public IList<BusinessObject> GetBusinessObject(Guid? filterId)
{
using (var db = new L2SDataContext())
{
var temp = from bo in db.BusinessObjects select bo;
if(filterId.HasValue) temp = temp.Where(t => t.Filter == filterId.Value);
var result = from t in temp
orderby t.Name
select SqlModelConverters.ConvertBusinessObject(bo);
return result.ToList();
}
}
I know that Lazy-Evaluation will make sure that only one query is really sent, but having that temp object in there isn't that great really.
Did you try:
where filterId == null || t.Filter == filterId
Your fix is exactly correct. You are effectively trying to build up a query dynamically, based on your function input. It's a good idea to omit the where clause instead of supplying WHERE TRUE anyway. If I were writing this query, I would go with your fixed version myself.
It's not as pretty as using the language keywords, but it's still the right way to approach the query in my opinion.

Return IQueryable containing 2 Subclasses

Can you return IQueryable which is composed of two or more different subclasses ? Here's my attempt to show what I mean. It throws the error:
System.NotSupportedException: Types in
Union or Concat have members assigned
in different order..
var a = from oi in db.OrderItems
where oi.OrderID == ID
&& oi.ListingID != null
select new OrderItemA {
// etc
} as OrderItem;
var b = from oi in db.OrderItems
where oi.OrderID == ID
&& oi.AdID != null
select new OrderItemB {
//etc
} as OrderItem;
return a.Concat<OrderItem>(b);
Try doing the concat on IEnumerable instead of IQueryable:
return a.AsEnumerable().Concat(b.AsEnumerable());
If you need an IQueryable result you could do this:
return a.AsEnumerable().Concat(b.AsEnumerable()).AsQueryable();
Doing this will force the concat to happen in-memory instead of in SQL, and any additional operations will also happen in-memory (LINQ To Objects).
However, unlike the .ToList() example, the execution should still be deferred (making your data lazy loaded).
My guess is that this is because you are using LINQ in an LINQ-to-SQL context.
So using Concat means that LINQ2SQL will need to join both query into a SQL UNION query which might be where the System.NotSupportedException originated from.
Can you try this:
return a.ToList().Concat<OrderItem>(b.ToList());
And see if it make any difference?
What the above does is that it executes the query twice and then concatenate them in-memory instead of hot-off-SQL as to avoid the query translation problem.
It might not be the ideal solution, but if this work, my assumption is probably correct, that it's a query translation problem:
More information about Union and Concat translation to SQL:
http://blog.benhall.me.uk/2007/08/linq-to-sql-difference-between-concat.html
http://msdn.microsoft.com/en-us/library/bb399342.aspx
http://msdn.microsoft.com/en-us/library/bb386979.aspx
Hope this helps.
Interestingly after reading your post and a bit of testing, I realized that what your actually doing does seem to work just fine for me given that the projection part you show as ellipsis in both of your queries match. You see, LINQ to SQL appears to construct the underlying projection for the SQL select command based off of the property assignment statements as opposed to the actual type being materialized so as long as both sides have the same number, type, and order (not sure about this) of member assignments the UNION query should be valid.
My solution that I've been working with is to create a property on my DataContext class which acts much like a SQL View in that it allows me to write a query (in my case a Union between two different tables) and then use that query as if it is itself like a table when composing read-only select statements.
public partial class MyDataContext
{
public IQueryable<MyView> MyView
{
get
{
var query1 =
from a in TableA
let default_ColumnFromB = (string)null
select new MyView()
{
ColumnFromA = a.ColumnFromA,
ColumnFromB = default_ColumnFromB,
ColumnSharedByAAndB = a.ColumnSharedByAAndB,
};
var query2 =
from a in TableB
let default_ColumnFromA = (decimal?)null
select new MyView()
{
ColumnFromA = default_ColumnFromA,
ColumnFromB = b.ColumnFromB,
ColumnSharedByAAndB = b.ColumnSharedByAAndB,
};
return query1.Union(query2);
}
}
}
public class MyView
{
public decimal? ColumnFromA { get; set; }
public string ColumnFromB { get; set; }
public int ColumnSharedByAAndB { get; set; }
}
Notice two key things:
First of all the projection formed by the queries which make up both halves of the Union have the same number, type, and order of columns. Now LINQ may require the order to be the same (not sure about this) but it is definitely true that SQL does for a UNION and we can be sure that LINQ will require at least the same type and number of columns and these "columns" are known by the member assignments and not from the properties of the type you are instantiating in your projection.
Secondly LINQ currently doesn't allow for multiple constants to be used within a projections for queries which formulate a Concat or Union and from my understanding this is mainly because these two separate queries are separately optimized before the Union operation is processed. Normally LINQ to SQL is smart enough to realize that if you have a constant value which is only being used in the projection, then why send it to SQL just to have it come right back the way it was instead of tacking it on as a post process after the raw data comes back from SQL Server. Unfortunately the problem here is that this is a case of LINQ to SQL being to smart for it's own good, as it optimizes each individual query too early in the process. The way I've found to work around this is to use the let keyword to form a range variable for each value in the projection which will be materialized by getting it's value from a constant. Somehow this tricks LINQ to SQL into carrying these constants through to the actual SQL command which keeps all expected columns in the resulting UNION. More on this technique can be found here.
Using this techinque I at least have something reusable so that no matter how complex or ugly the actual Union can get, especially with the range variables, that in your end queries you can write queries to these pseudo views such as MyView and deal with the complexity underneath.
Can you do the projection after the concat?
// construct the query
var a = from oi in db.OrderItems
where oi.OrderID == ID
&& oi.ListingID != null
select new {
type = "A"
item = oi
}
var b = from oi in db.OrderItems
where oi.OrderID == ID
&& oi.AdID != null
select new {
type = "B"
item = oi
}
var temp = a.Concat<OrderItem>(b);
// create concrete types after concatenation
// to avoid inheritance issue
var result = from oi in temp
select (oi.type == "A"
? (new OrderItemA {
// OrderItemA projection
} as OrderItem)
: (new OrderItemB {
// OrderItemB projection
} as OrderItem)
);
return result
Not sure if the ternary operator works in LINQ2SQL in the above scenario but that might help avoid the inheritance issue.

Categories