I am trying to get this sorting algorithm to work with IQueryable.OrderBy():
https://www.dotnetperls.com/alphanumeric-sorting
I have the AlphanumComparatorFast class implemented and my code looks like so:
return this.DbContext.IssuesAccessView.AsQueryable()
.Include(r => r.Issue)
.Include(r => r.Issue.IssueAttachments)
.Include(r => r.Issue.IssueParticipants)
.Where(x => x.UserId == userId)
.Select(y => y.Issue)
.OrderBy(p => p.IssueNumber, new AlphanumComparatorFast());
It compiles fine but Linq has no idea to do with it at runtime because it cannot translate AlphanumComparatorFast into SQL, so I get this error
"this method cannot be translated into a store expression."
The key here is that I do not want to lose the IQueryable interface. I need to return queryable for delayed execution.
So my question is, can anyone think of a way to do this? Do I need to use a SQL function(or some such) or can this be done using IQueryable?
Thank you!
UPDATE
Thanks to CodeNotFound for answering that I do need to create a SQL function to do this.
I have created a function and am trying to call it from C# EF6 Code First(no edmx files) the problem is that I can't find a good example of how to do this. The closest I have gotten is this:
[Function(Name = "dbo.fn_CreateAlphanumericSortValue", IsComposable = true)]
[return: Parameter(DbType = "VarChar(100)")]
public string ReverseCustName([Parameter(Name = "string", DbType =
"VarChar(100)")] string #string)
{
return ((string)(this.ExecuteMethodCall(this,
((MethodInfo)(MethodInfo.GetCurrentMethod())),
#string).ReturnValue));
}
The problem here is that this is from Linq to SQL and uses the DataContext.ExecutMethodCall function instead of the DBContext object used in EF 6 and there appears to be no equivalent method in DBContext
Any help on how to do this in EF6(Code first, no .edmx files) would be appreciated
You've the following error
"this method cannot be translated into a store expression."
Because your query must be translated into SQL instructions therefore there is no available clause in SQL that can be mapped with AlphanumComparatorFast.
Do I need to use a SQL function(or some such) or can this be done
using IQueryable?
Yes you need to translate the logic of your sorting written in C# composable with SQL by creating a SQL function on the server side and use it in your Linq to Entites query.
Related
I'm using Entity Framework Core (2.0) and I have the following doubt.
I'm not sure about what happens when I do this:
context.Customers.Where(c => MyCustomMethod(c));
bool MyCustomMethod(Customer c)
{
return c.Name.StartsWith("Something");
}
Does it translate to SQL without problems?
Is it different than writing:
context.Customers.Where(c => c.StartsWith("Something"));
In short, will I be able to wrap my validations for the Where clase inside a method? Does it break the translation to SQL?
No, you cannot call your custom method in EF LINQ query because EF will not be able to generate expression tree of the method and so it cannot translate it to SQL.
For more info about expression trees, refer to the link.
if you need to get string from method you can write same query like this
from customer in contetx.Customer
let str = GetString()
where Name.Any(c=> c.StartsWith(str) )
select customer;
string GetString()
{
return "Something";
}
i dnt know this is helpfull, but this can achieve
I'm trying to retrieve the value for a particular property of an entity into a variable using the following code.
var item = db.Notices
.Where(a => a.ID == 0)
.Select(x => x
.GetType()
.GetProperty("Spell_ID")
.GetValue(x));
I'm just playing around with this at the moment, but at some point I'd like to be able to replace the 'Spell_ID' text with any column name and get the value dynamically. Not sure if I'm going the right way around this, but I'm getting the following error:-
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression.
I know I'm not doing this the right way (and I'm relatively new to C# MVC / LINQ), but I've spent so much time tinkering with the code I've lost my way...can somebody point me in the right direction please?
Your current code uses reflection to get the value of a property, but, from what I can infer from your exception message, db is an Entity Framework DbContext.
Entity framework does not support reflection at all, because your LINQ query is then converted into a SQL query by the framework itself. For this reason you have to change your approach if you really need to get a single property:
var items = db.Notices.Where(a => a.ID == 0).ToList();
var itemsProperty = items.Select(x => x.GetType().GetProperty("Spell_ID"));
This will fetch all the resources from the database and then execute the Select part in memory.
If you expect only a single entity from your database than this is a better approach:
var entity = db.Notices.SingleOrDefault(a => a.ID == 0);
var property = entity.GetType().GetProperty("Spell_ID");
Not to be tounge-in-cheek, but the error
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression.
is exactly what it sounds like. LINQ is unable to translate the GetValue() method to whatever it is that Entity Framework does exactly.
While there are ways to get EF and LINQ to recognize methods, its kinda a pain. The quickest solution would be to just use a loop.
I have a generic repository using EF6. The issue has to do with association properties requiring an "Include" even though it shouldn't. The following works:
IQueryable<User> dbQuery = _db.Set<User>();
return dbQuery.Where(x => x.Child.Name == "Foo").ToList();
However, the following does not work:
Func<User, bool> filter = x => x.Child.Name == "Foo";
IQueryable<User> dbQuery = _db.Set<User>();
return dbQuery.Where(filter).ToList();
It throws an "Object Reference not set..." exception on Child.
The following resolves it:
Func<User, bool> filter = x => x.Child.Name == "Foo";
IQueryable<User> dbQuery = _db.Set<User>();
dbQuery = dbQuery.Include(x => x.Child);
return dbQuery.Where(filter).ToList();
I don't understand why this is necessary though. Anyone know a way to resolve this without using the "Include"?
You are should use Expression to let EF provider parse your query.
Change the Func<User, bool> to Expression<Func<User, bool>>
The first snippet is providing an Expression to Where, which is being translated into SQL, and is doing the entire operation in the database. The latter two are passing a compiled method to Where, which it can't translate into SQL, which means that the entire database table is being pulled down into memory, and the entire operation is run in your application. When you pull down the whole table it doesn't pull down related records unless you Include them.
The solution is not to pull down both the entire table and also all of the data from all of the related records; the solution is to do the filtering in the database rather than in your application.
Why am I getting:
The method 'Where' cannot follow the method 'Select' or is not
supported. Try writing the query in terms of supported methods or call
the 'AsEnumerable' or 'ToList' method before calling unsupported
methods.
...when using the WHERE clause, like when calling:
XrmServiceContext.CreateQuery<Contact>().Project().To<Person>().Where(p => p.FirstName == "John").First();
?
This works:
XrmServiceContext.CreateQuery<Contact>().Project().To<Person>().First();
Also this works:
XrmServiceContext.CreateQuery<Contact>().Where(p => p.FirstName == "John").First();
I'm using AutoMapper QueryableExtension.
Additional info:
I don't want to call ToList() before the Where clause. I know it will works that way.
CreateQuery<TEntity>() returns IQueryable<TEntity>.
It's because whatever query provider you are using isn't able to handle this. It's not invalid in the general case; in fact most query providers do support filtering after projecting. Certain query providers simply aren't as robust as others, or they are representing a query model that is less flexible/powerful than the LINQ interface (or both). As a result, LINQ operations that are correct from the C# compiler's point of view might still not be translatable by the query provider, so the best it can do is throw an exception at runtime.
Why don't you just move the where so it is before the projection? It will result in a single query being executed which filters and projects:
XrmServiceContext.CreateQuery<Contact>().Where(p => p.FirstName == "John").Project().To<Person>().First();
Looking at AutoMapper's instructions for the QueryableExtensions it has an example showing the Where clause before the projection. You need to refactor your code to support this model, as opposed to placing the Where clause after the projection.
public List GetLinesForOrder(int orderId)
{
Mapper.CreateMap()
.ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name);
using (var context = new orderEntities())
{
return context.OrderLines.Where(ol => ol.OrderId == orderId)
.Project().To().ToList();
}
}
Given the limitations of Dynamic CRM's LINQ provider you should not expect AutoMapper to necessarily get the LINQ query correct.
There is actually a logic behind this design. As the developer you create a working Where clause. You then let AutoMapper's Project().To() define the select statement. Since CRM's LINQ provider has support for anonymous types in it should work correctly. The purpose of projection in AutoMapper is to limit the data retrieved from each class to only that needed for the projected to class. It is not intended to write a Where clause based on the projected to class.
I'm trying to do the following:
var query =
(from a in session.Query<A>()
where a.BasicSearch(searchString) == true
select a);
But it keeps giving me this exception "System.NotSupportedException"!
Any idea how to solve this?
It is not possible to use user-defined functions in a LINQ query. The NHibernate linq provider does not 'know' how to translate your function into SQL.
LINQ to NHibernate works by inspecting the LINQ expression that you provide at runtime, and translating what it finds in this expression tree into a regular SQL expression. Here's a good article to get some background on expression trees: http://blogs.msdn.com/b/charlie/archive/2008/01/31/expression-tree-basics.aspx
You CAN reuse predicates like this in another way however, using the techniques discussed here. (I'm not sure if this works with NHibernate however.) IF it works it would look something like this:
// this could be a static method on class A
public static Expression<Func<A, bool>> BasicSearch(string criteria)
{
// this is just an example, of course
// NHibernate Linq will translate this to something like
// 'WHERE a.MyProperty LIKE '%#criteria%'
return a => criteria.Contains(a.MyProperty);
}
Usage:
from a in Session.Query<A>().Where(A.BasicSearch(criteria))
UPDATE: apparently there will be issues with NHibernate. See this blog post for a version that ought to work.
It is possible to call your own and SQL functions, but you have to make a wrapper for them so that NHibernate knows how to translate the C# to SQL.
Here's an example where I write an extension method to get access to SQL Server's NEWID() function. You would use the same techniques to get access to any other function on your database server, built-in or user-defined.
Some examples to extend NHibernate LINQ:
http://fabiomaulo.blogspot.se/2010/07/nhibernate-linq-provider-extension.html
https://nhibernate.jira.com/browse/NH-3301
Declare a BasicSearch extension method. Supposing your udf is on dbo:
using NHibernate.Linq;
...
public static class CustomLinqExtensions
{
[LinqExtensionMethod("dbo.BasicSearch")]
public static bool BasicSearch(this string searchField, string pattern)
{
// No need to implement it in .Net, unless you wish to call it
// outside IQueryable context too.
throw new NotImplementedException("This call should be translated " +
"to SQL and run db side, but it has been run with .Net runtime");
}
}
Then use it on your entities:
session.Query<A>()
.Where(a => a.SomeStringProperty.BasicSearch("yourPattern") == true);
Beware, trying to use it without referencing an entity in its usage will cause it to get evaluated with .Net runtime instead of getting it translated to SQL.
Adapt this BasicSearch example to whatever input types it has to handle. Your question was calling it directly on the entity, which does not allow your readers to know on how many columns and with which types it need to run.