How can I use external method in LINQ OrderBy - c#

finalProducts = context.Products.OrderBy(p => p.Name.LevenshteinDistance(query))
.Skip(from)
.Take(36)
.ToList<Product>();
I use method LevenshteinDistance() to find match with query, but it shows error
LINQ to Entities does not recognize the method 'Int32 LevenshteinDistance(System.String, System.String)' method, and this method cannot be translated into a store expression.
What should I do to sort products from database using this method?

You will need to order the in-memory list:
var products = context.Products.ToList();
var finalProducts = products.OrderBy(p => p.Name.LevenshteinDistance(query)).Skip(from).Take(36).ToList<Product>();
...or re-write the logic of your method in a stored procedure and execute this one. LINQ-to-Entities will obviously not be able to translate your custom C# method into T-SQL.

You are 'trying' to create a query to Database, using a method most possibly written by yourself. However, the problem is that framework does not know how to convert that method to a logical query. You need to order the result after you get the results from the database.

As others have commented, the problem is that you are trying to run your own method on the database. You can't do this, as the database doesn't know about your method.
It's important to understand how Linq2Enties queries work. Until you enumerate the query, nothing is actually done. It's only when you enumerate the query (say by calling an extension method like ToList(), ToArray() or any of several OrAbc() methods) that the database accesses the data and returns some results to the code. At that point, any further code runs independently of the database, and so you can call your own method.
So, what you can do is this...
finalProducts = context.Products
.ToList<Product>() // Enumerates
.OrderBy(p => p.Name.LevenshteinDistance(query))
.Skip(from)
.Take(36)
.ToList<Product>();
That will work. However, you need to be aware of the consequences of doing this, as the database will return every product, which may take some time if you have a lot. You would have to try it and see if this is a problem for you.
If performance is an issue, then you would probably have to implement the distance function as a stored procedure, but that's a lot more work. Try it this way first.

Related

LINQ to Entities does not recognize the method 'System.Collections.Generic.IEnumerable`1

I am getting this error while using this IEquality comparer for intersection.
Can somebody identify where I am doing it wrong?
Pleas ask if you need more information.
When you write LINQ to Entities, you must remember that eventually all of your LINQ expression is translated to SQL. So for every method call you make in that expression, you should think if there's a reasonable way to translate it to SQL.
Having that in mind, you can see that:
SQL has no notion of .ToList(), which is probably the source of your current error. calls to .ToList() should be made at the end of the expression, as a way to "materialize" the query (i.e. make EF make the actual call to the database and return results).
Your database knows nothing about C# interfaces. You can't expect any implementation of IEqualityComparer to be translatable to SQL.
As #Dai has noted, you seem to be using too many joins. Make sure your model has the right navigation properties between entities, and use them.

error - calculating percentage when using Linq

I have a Ling Query for calculate percentage.
var total = db.Schema_Details.Where(x => x.RID == sName).Select(x => new{
Rid = x.RID,
Amount = Convert.ToDouble(x.Lum_Sum_Amount),
Allocation = Convert.ToDouble(x.Allocation),
total = ((x.Allocation / 100) * x.Lum_Sum_Amount)}).ToList();
But exception occurred.
How can I solve this?
Since you use the identifier db, I assume db is a DbContext. This means that your linq statements will be executed in SQL on the database server side and not in your memory (SQL or something similar, depending on the type of DB your are using).
You can see this if you check the type of your statement in the debugger: is it an IQueryable or an IEnumerable? IQueryables are executed by the database server, IEnumerable are usually executed in your memory. Usually your database server can execute your linq statements faster and more efficient than you, so it is best to make sure your linq statements are IQueryable.
However, the disadvantage is, that you database server does not know any of your classes, functions, nor .NET. So in IQueryables you can only use fairly simple calculations.
If you really need to call some local functions somewhere halfway your linq statement, add AsEnumerable()to your statement:
var myLinqResult = db. ... // something IQueryable
.Where(item => ... ) // simple statement: still IQueryable
.Select(item => ...) // still simple, still IQueryable
// now you want to call one of your own functions:
.AsEnumerable()
.Select(item => myOwnLocalFunctions(item));
See stackoverflow: Difference between IQueryable and IEnumerable
The local function you want to perform is Convert.ToDouble. IQueryable does not know the Convert class, and thus can't perform your function as IQueryable. AsEnumerable will solve this problem.
However, in this case I would not advise to use AsEnumerable. When executing linq statements on a database try to avoid using local classes and function calls as much as possible. Instead of Convert.ToDoubleuse (double)x.Sum_Amount. Your linq-to-sql translator (or similar database language converter) will know how to translate this as IQueryable.
There are some calculations of which there are SQL equivalents, like DateTime calculations, or string reverse. It would be a shame if you had to do things like DateTime.AddDays(1) in local memory while there are perfect SQL equivalents for it. This would especially be a problem if you need to Join, Select or GroupBy after your local functions. Your database server can do these things far more efficiently than you. Luckily there are some IQueryable extension functions for them. They can be found in System.Data.Entity.DbFunctions

Custom Comparer in LINQ query to sort strings/numerics

I've written a custom comparer that sorts strings and numeric. This all works fine.
However I'm rewriting my whole BLL to use LINQ where possible as I do like the syntax. Now i'm stumbling onto using my custom comparer. As LINQ syntax (query based) will not allow to use custom comparers Im now using method based LINQ.
But in order to make it to work, I need to do an intermediate ToList(), which again works fine, but looks somewhat weird to me?
var areas = cc.Areas.Where(a => a.ProjectId == ProjectId).ToList()
.OrderBy(a => a.UnitNumber, new Common.Comparers.StringNumericComparer());
Now i'm unsure if this has to do with the SQL query to be executed first and then the results sorted in my C# code side, but this is beyond my knowledge.
Does ToList() force the first part of the linq query to be executed on the database?
Yes your understanding is correct. Until the .ToList() it is an IQueryable. On ToList() a database query is fired and the results are fetched in memory and then a sort occurs on the List.
Your comparer cannot execute before this since it does not translate to an SQL query.
Yes, .ToList() forces your Areas to be loaded and the sorting occurs in memory.

Of what type is the result of a LINQ query?

Examples on LINQ gives this
var query = context.Contacts
.Where(q => q.FirstName == "Tom");
I'm wondering what object is "query"? And also is it possible (advisable) to pass it to a method (within the same class)?
The query object is most likely of type IQueryable<Contact>. You can of course pass it to a method, whether that is in the same class or in another class does not matter.
But keep in mind that LINQ does use a mechanism named "deferred execution". That means that query does not get enumerated immediately, but rather when it is needed. All the stuff you put in your query (the Where-clause for example) gets executed then. For more information about deferred execution have a look at MSDN: Query Execution.
NB: You can find out the exact type of the query variable if you hover you mouse over it or the var keyword in Visual Studio.

Understanding .AsEnumerable() in LINQ to SQL

Given the following LINQ to SQL query:
var test = from i in Imports
where i.IsActive
select i;
The interpreted SQL statement is:
SELECT [t0].[id] AS [Id] .... FROM [Imports] AS [t0] WHERE [t0].[isActive] = 1
Say I wanted to perform some action in the select that cannot be converted to SQL. Its my understanding that the conventional way to accomplish this is to do AsEnumerable() thus converting it to a workable object.
Given this updated code:
var test = from i in Imports.AsEnumerable()
where i.IsActive
select new
{
// Make some method call
};
And updated SQL:
SELECT [t0].[id] AS [Id] ... FROM [Imports] AS [t0]
Notice the lack of a where clause in the executed SQL statement.
Does this mean the entire "Imports" table is cached into memory?
Would this slow performance at all if the table contained a large amount of records?
Help me to understand what is actually happening behind the scenes here.
The reason for AsEnumerable is to
AsEnumerable(TSource)(IEnumerable(TSource))
can be used to choose between query
implementations when a sequence
implements IEnumerable(T) but also has
a different set of public query
methods available
So when you were calling the Where method before, you were calling a different Where method from the IEnumerable.Where. That Where statement was for LINQ to convert to SQL, the new Where is the IEnumerable one that takes an IEnumerable, enumerates it and yields the matching items. Which explains why you see the different SQL being generated. The table will be taken in full from the database before the Where extension will be applied in your second version of the code. This could create a serious bottle neck, because the entire table has to be in memory, or worse the entire table would have to travel between servers. Allow SQL server to execute the Where and do what it does best.
At the point where the enumeration is enumerated through, the database will then be queried, and the entire resultset retrieved.
A part-and-part solution can be the way. Consider
var res = (
from result in SomeSource
where DatabaseConvertableCriterion(result)
&& NonDatabaseConvertableCriterion(result)
select new {result.A, result.B}
);
Let's say also that NonDatabaseConvertableCriterion requires field C from result. Because NonDatabaseConvertableCriterion does what its name suggests, this has to be performed as an enumeration. However, consider:
var partWay =
(
from result in SomeSource
where DatabaseConvertableCriterion(result)
select new {result.A, result.B, result.C}
);
var res =
(
from result in partWay.AsEnumerable()
where NonDatabaseConvertableCriterion select new {result.A, result.B}
);
In this case, when res is enumerated, queried or otherwise used, as much work as possible will be passed to the database, which will return enough to continue the job. Assuming that it is indeed really impossible to rewrite so that all the work can be sent to the database, this may be a suitable compromise.
There are three implementations of AsEnumerable.
DataTableExtensions.AsEnumerable
Extends a DataTable to give it an IEnumerable interface so you can use Linq against the DataTable.
Enumerable.AsEnumerable<TSource> and ParallelEnumerable.AsEnumerable<TSource>
The AsEnumerable<TSource>(IEnumerable<TSource>) method has no effect
other than to change the compile-time type of source from a type that
implements IEnumerable<T> to IEnumerable<T> itself.
AsEnumerable<TSource>(IEnumerable<TSource>) can be used to choose
between query implementations when a sequence implements
IEnumerable<T> but also has a different set of public query methods
available. For example, given a generic class Table that implements
IEnumerable<T> and has its own methods such as Where, Select, and
SelectMany, a call to Where would invoke the public Where method of
Table. A Table type that represents a database table could have a
Where method that takes the predicate argument as an expression tree
and converts the tree to SQL for remote execution. If remote execution
is not desired, for example because the predicate invokes a local
method, the AsEnumerable<TSource> method can be used to hide the
custom methods and instead make the standard query operators
available.
In other words.
If I have an
IQueryable<X> sequence = ...;
from a LinqProvider, like Entity Framework, and I do,
sequence.Where(x => SomeUnusualPredicate(x));
that query will be composed and run on the server. This will fail at runtime because the EntityFramework doesn't know how to convert SomeUnusualPredicate into SQL.
If I want that to run the statement with Linq to Objects instead, I do,
sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x));
now the server will return all the data and the Enumerable.Where from Linq to Objects will be used instead of the Query Provider's implementation.
It won't matter that Entity Framework doesn't know how to interpret SomeUnusualPredicate, my function will be used directly. (However, this may be an inefficient approach since all rows will be returned from the server.)
I believe the AsEnumerable just tells the compiler which extension methods to use (in this case the ones defined for IEnumerable instead of those for IQueryable).
The execution of the query is still deferred until you call ToArray or enumerate on it.

Categories

Resources