LINQ to Entities Where clause with custom method - c#

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

Related

Entity Framework Core, emitting/generating expression in select

I'm using Entity Framework Core in .Net Core 2.2, with the recently released Oracle.EntityFrameworkCore library.
I'd like to be able to generate a query like this...
select nvl(nullablecolumn, 'N') from table;
I think I'm right in saying that I can't do this, at least not out of the box... I can however do something similar, using something like this (but then if I end up writing this, why not write actual SQL and skip Entity Framework???)...
from row in table
select new { somedata = row.nullablecolumn ?? "N" };
The above linq query gets me the same sort of answer as I'm after... question is, can I do some expression tree magic to get the same result?
For example, this question looks like it generates an expression tree for a "like" query, so how would I generate an expression tree (or modify the existing expression tree) to make the select side of the statement emit nvl()?
This would be useful where you have Entity Framework Value Conversions...
Bonus points (if I could give bonus points) if you can give me a clue on how to create an expression tree to manipulate the where side of the query... AutoMapper manages this somehow with "Projections"?
Any thoughts/pointers would be greatly appreciated...
To translate your own method invocation to proper SQL function you can use HasDbFunction from docs.
You have to define own static method as
public static class OracleDbFunction
{
public static string Nvl(string string1, string replace_with) => throw new NotImplementedException(); // You can provide matching in-memory implementation for client-side evaluation of query if needed
}
register it in your DbContext
protected overridevoid OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDbFunction(typeof(OracleDbFunction).GetMethod(nameof(OracleDbFunctionExtensions.Nvl, builder =>
{
builder.HasName("nvl");
});
}
and use it in your Where expression as
.Where(i => OracleDbFunction.Nvl(i.nullablecolumn, "N") == "N")
You can also use attribute DbFunctionAttribute on OracleDbFunctionExtensions.Nvl to avoid registering it in OnModelCreating

IQueryable with AlphaNumeric sorting

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.

Can I use predicates from my Domain Model in Entity Framework Code First?

I've got a class with a bool method, e.g.:
public bool IsInFuture()
{ return this.Date > DateTime.Now; }
And I store it in a database using EF Code First. If I'll try to use that predicate in Linq operations, I'll get an exception, as it can not be translated into SQL:
await context.Where(order => order.IsInFuture()).ToListAsync();
That predicate's logic can be rather complicated and I wouldn't like to duplicate it in my code. It there any way I can "inline" it's code into Linq operations? I'm pretty sure, this problem has a solution.
Thanks in advance!
You can't use the predicate directly. How should EF know how to convert it into an SQL WHERE statement?
You have a few alternatives:
Rephrase the query to use standard operations, e.g. smaller-than. This will duplicate the logic from the predicate, though.
Use a raw SQL query. This will also duplicate the logic, but in SQL.
Load the entire list of entities and then filter in-memory (i.e. Where() after ToList()). Like this, you can reuse the predicate. Note that this only a good idea for very small data sets.

Extension method on Enum causes Linq Query to Fail

I have an extension method on an Enum called GetName which returns a string. I'm using it in linq to entity framework to select rows with a specific product name. However, when the code is getting executed it's throwing a NotSupportedException
LINQ to Entities does not recognize the method System.String
GetName(Tool.ViewModels.Product) method, and this method cannot be
translated into a store expression.
Here is the code I am executing:
try
{
//Linq to Entity Framework
var contextRow = Contexts.Data.Source.SingleOrDefault(p => p.Product == Product.ProductOne.GetName());
}
catch (Exception e)
{
throw e;
}
Does Linq not recognize extension methods in its evaluations? Or is there something more going on?
Under the hood the Entity Framework is mapping the code you type into SQL queries. When it sees the call to GetName it sees a call into a user defined function. It has no power to translate, what essentially amounts to raw IL, into a well formed SQL query. This is why you are getting that exception
when using LINQ, you have to define a context and within that context perform operations on the database, for example the code you want to run, you can be this way
using (ModelLinq _context = new ModelLinq()){
var rows= _context.anyTable.Where(p=> p.anyColumn == value);
}
And Entity Framework is a mapping of your data base for you to execute SQL queries with lenguale c #
regards!

LINQ to Nhibernate user defined function in where clause

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.

Categories