Nhibernate Linq Sum with calculation causes NotSupportedException - c#

I am using Nhibernate 3.3.3.4001 with SybaseSQLAnywhere12Dialect and trying to use a simple calculation within a sum linq function.
I would like to use Linq rather than HQL to achieve this and would be happy to extend Nhibernate with a default linq to hql generator.
Using the following Linq query raises the exception
System.NotSupportedException: Expression type 'NhSumExpression' is not supported by this SelectClauseVisitor
Linq Query
var query = (from c in session.Query<Entity>()
select c.FieldValue / 100M).Sum()
Expected SQL Statement
SELECT SUM(FieldValue / 100.0) FROM TableName
If the same query is run without the Sum() function it performs the calculation correctly, as such am confused why the Sum() wouldn't work.

You may try to rewrite your LINQ query a bit, just like this one:
var result = ((from c in session.Query<Entity>()
select c.FieldValue).Sum()) / 100;
Resulting query could also be optimized by moving division operator out of the sum:
SELECT SUM(FieldValue) / 100 FROM TableName

There is an open issue when using constants within Linq group function (Max, Min, Sum) functions. If another field is used it will work correctly.
Found bug within NHibernate 3.3.3
https://nhibernate.jira.com/browse/NH-3376

Related

force to use join in nhibernate syntax instead of iteration

i am trying to figure out why nhibernate query iterates on values instead of using joins internally. due to this iteration it becomes slower, cause it will iterates all values one by one.
i.e. it generates n no. of queries and execute it one by one instead of joins.
documentClrType is evaluated dynamically at runtime, so i can't use directly in QueryOver<> syntax
documentClrType is FactSheetPrivate as of now.
i observed query in logger it is created something like this.
select * from foo where col1=#val1
select * from foo where col1=#val2
select * from foo where col1=#val3
select * from foo where col1=#val4
so,
how could i turn this query to joins instead of iterations?
what could be syntax for this dynamic types with query over?
i am bit new to nhibernate, any guidance will be appreciated.
var criteria=
store.Session.
CreateCriteria(documentClrType)
.Add(Restrictions.Disjunction()
.Add(Restrictions.Le("CreationDate",)DateTime.Now )
.Add(Restrictions.Le("AccurateDate",)DateTime.Now )
)
criteria=criteria.CreateCriteria("Entity")
.Add(Restrictions.Eq("DBTypeString",receiverType))
return criteria.List<IDocument>()
// at this lines instead of join query iterates value one by one
following screen display entity nhibernate xml file
following screen display packets nhibernate XML file
NHibernate has a feature to batch the selects
configuration.SetProperty(NHibernate.Cfg.Environment.DefaultBatchFetchSize, "20")
or use eager fetching
return criteria.SetFetchMode("TheReferenceOrCollectionProperty", FetchMode.Eager).List<IDocument>()

Can I use Math.Max() / Math.Min() within a LINQ query?

I have a LINQ query which I'm using to select some rows out of a SQL database. Each row from my database represents a footprint of physical space, and I'm trying to select just the rows which contain a given point. The part of the query that is giving me issues is below:
LinqMetaData meta = new LinqMetaData(da);
var captures = (from c in meta.Capture
where
(c.TLLat.HasValue && lat < Math.Max(Math.Max(c.BLLat.Value, c.BRLat.Value), Math.Max(c.TLLat.Value, c.TRLat.Value)))
&&
(c.TLLat.HasValue && lat > Math.Min(Math.Min(c.BLLat.Value, c.BRLat.Value), Math.Min(c.TLLat.Value, c.TRLat.Value)))
select c);
When I run the code, I get this error:
The binary expression '(39.3237282094724 <
Max(Max(EntityField(LPLA_1.BLLat AS BLLat), EntityField(LPLA_1.BRLat
AS BRLat)), Max(EntityField(LPLA_1.TLLat AS TLLat),
EntityField(LPLA_1.TRLat AS TRLat))))' can't be converted to a
predicate expression.
I've assumed this means I can't use Math.Max() / Math.Min() in a LINQ query...Is this correct?
EDIT:
I am using LinqMetaData for my query, which comes from the LLBLGen Pro c# library. I think that this implementation of LINQ may not support Math.Max() / Math.Min() within its queries.
Simply move Max and Min calculations outside of your query as separate variables.
Not only that could affect your query overall performance but will remove the error you are seeing which is in this case connected to the provider unable to translate nested Max and Min methods.
Here is a Microsoft Documentation about some of the known issues with LINQ to Entities.
Another approach would be to convert the above query to pure SQL Procedure.

Advanced Linq sorting C#

I have an IQueryable that has a list of pages.
I want to do: Pages.OrderByDescending(o => CalculateSort(o.page));
the method calculate sort is similar to that here is a plain english version:
public int calculatesort(page p)
{
int rating = (from r in db.rating select r). sum();
int comments = //query database for comments;
float timedecayfactor = math.exp(-page.totalhoursago);
return sortscore = (rating +comments)* timedecayfactor;
}
when I run a code similar to the one above an error is thrown that the mothode calculatesort cannot be converted to sql.
How can I do a conver the function above to be understood by sql so that I can use it to sort the pages?
Is this not a good approach for large data? Is there another method used to sort sets of results other than dynamically at the database?
I havent slept for days trying to fix this one :(
your code is nowhere near compiling so I'm guessing a lot here but I hope this gives an idea none the less.
As several have posted you need to give Linq-2-Sql an expression tree. Using query syntax that's what happens (by compiler magic)
from p in pages
let rating = (from r in db.rating
where r.PageId == p.PageId
select r.Value).Sum()
let comments = (from c in db.Comments
where c.PageId == p.PageId
select 1).Count()
let timedecayfactor = Math.Exp(-(p.totalhoursago))
orderby (rating + comments)*timedecayfactor descending
select p;
I haven't actually tried this against a database, there's simply too many unknown based on your code, so there might still be stuff that can't be translated.
The error occurs because LINQ cannot convert custom code/methods into SQL. It can convert only Expression<Func<>> objects into SQL.
In your case, you have a complex logic to do while sorting, so it might make sense to do it using a Stored Procedure, if you want to do it in the DB Layer.
Or load all the objects into main memory, and run the calculate sort method on the objects in memory
EDIT :
I don't have the code, so Describing in english is the best I can do :
Have table with structure capable of temporarily storing all the current users data.
Have a calculated field in the Pages table that holds the value calculated from all the non-user specific fields
Write a stored procedure that uses values from these two sources (temp table and calc field) to actually do the sort.
Delete the temp table as the last part in the stored proc
You can read about stored procs here and here
var comments = db.comments.Where(...);
Pages.OrderByDescending(p=>(db.rating.Sum(r=>r.rate) + comments.Count()) * Math.Exp(-p.totalhoursago))
Linq is expecting Calculatesort to return a "queryable" expression in order to generate its own SQL.
In can embed your 'calculatesort' method in this lambda expression. (I replaced your variables with constants in order to compile in my environment)
public static void ComplexSort(IQueryable<string> Pages)
{
Pages.OrderByDescending(p =>
{
int rating = 99;//(from r in db.rating select r). sum();
int comments = 33;//query database for comments;
double timedecayfactor = Math.Exp(88);
return (rating + comments) * timedecayfactor;
});
}
Also, you can even try to run that in parallel (since .net 4.0) replacing the first line with
Pages.AsParallel().OrderByDescending(p =>
Yes, counting previous answers: the LINQ to SQL doesn't know how to translate CalculateSort method. You should convert LINQ to SQL to ordinary LINQ to Object before using custom method.
Try to use this in the way you call the CalculateSort by adding AsEnumerable:
Pages.AsEnumerable().OrderByDescending(o => CalculateSort(o.page));
Then you're fine to use the OrderByDescending extension method.
UPDATE:
LINQ to SQL will always translate the query in the code into Expression tree. It's quite almost the same concept as AST of any programming language. These expression trees are further translated into SQL expression specific to SQL Server's SQL, because currently LINQ to SQL only supports SQL Server 2005 and 2008.

subsonic - convert an linq query to sql query / DataReader

Let's say I have a the following query:
int x = 5;
var result = from p in db.products
where p.CategoryId == x
select p;
int count = result.Count();
List<product> products = result.ToList();
That's what I have now. But aditionally I need to have a DataReader from result:
// that's what I need:
var reader = ConvertSubSonicLinqQueryToDataReader(result);
How can I convert the linq statement to something I can work with?
A DataReader or a DbCommand or even plain sql with a list of paramters.
I know SubSonic can do that (since it translates the query to plain sql anyway) but I haven't found anything in the public accessible methods yet.
Any suggestions?
Converting the LINQ query is the wrong approach. LINQ returns results at a level of abstraction higher than a DataReader works at.
There's also the issue of deferred execution so your LINQ query may not be executed as a single SQL statement anyway.
Rater than use a LINQ statement why not just use an SqlQuery instead?
var qry = new Select().From(Product.Schema).Where(Product.CategoryIdColumn).IsEqualTo(x);
return qry.ExecuteReader();
Edit:
Just seen you're using SubSonic3 (not 2 as the above code would be for) but the potential misuse of LINQ and duplication of work still stands.
The code that creates object from the DataReader can be found in DbDataProvider.ToEnumerable. It's called from DbQueryProvider's Execute method (line 227). The best way to "understand" the LINQ magic is to place some breakpoints on DbQueryProvider methods.

Custom Method in LINQ to SQL query

Is it possible to use custom method In query for example:
var result = from u in context.MyTable where MyMethod(u) == 10 select u;
As Pranay explains, you cannot have a custom (C#) method as part of the LINQ to SQL query, because LINQ to SQL wouldn't be able to look at the expression tree of the method and so it cannot translate it to SQL.
One option that you have is to write your function in SQL and store it as a SQL function on the SQL Server (possibly, you could also use SQL CLR, but I have not tried that). Then you can add the function to your DataContext type and LINQ to SQL will translate it to calls to the function on SQL server. Something like:
var result = from u in context.MyTable
where context.MyMethod(u) == 10 select u;
The problem, of course, is that you'll need to write the function in SQL (I think SQL CLR could also work - not sure about the performance and other possible complications though)
I also wrote an article (some time ago) that shows how to do this when you write the "method" as an expression tree way (as a value of type Expression<Func<...>>), which is possible, because in this case, the code is compiled as an expression tree. However, there is some postprocessing that has to be done and you can still write just a single expression that can be easily inlined in the LINQ query.
Check this full article : What is and what isn't possible with linq
Following is not possible
// function used in filter
static bool MyFunc(Nwind.Product p)
{
return p.ProductName.StartsWith("B");
}
// query that uses MyFunc
var q =
from p in db.Products
where MyPriceFunc(p.UnitPrice) > 30m
select p
It compiles with no errors, but when you execute it LINQ to SQL throws an exception saying: "Static method System.Boolean MyTest(LINQTest.Nwind.Product) has no supported translation to SQL."
The exception is actually thrown when you try to fetch results from q (for example using the foreach statement), because LINQ to SQL attempts to convert the expression trees to T-SQL only when the results are needed and the query must be executed.
To fix the example you can simply copy the code that checks whether product name starts with "B" to the where clause of the query and it would work fine.
Yes, but if you are using Linq-to-Sql - your method has to have special code to handle to SQL conversion.

Categories

Resources