Custom Linq functions for sql and entities - c#

Is there a way to create functions for linq to sql that act different when used in linq to entities?
I do not whant to use a AsQueriable. I becouse I whant to do as much calculations on the sql server as possible.
example:
var UserIDs = Users.Select(x=> ConvertToString(x.UserID));
For linq to sql the code has to be:
public string ConvertToString(int id)
{
return SqlFunctions.StringConvert((double?)id);
}
For linq to entities the code has to be:
public string ConvertToString(int id)
{
return id.ToString();
}

You can use SqlFunctions in Entity Framework 4 and above. In your example, you do not have to do the conversion in the application layer.
http://blogs.microsoft.co.il/blogs/gilf/archive/2009/05/23/calling-database-functions-in-linq-to-entities-in-entity-framework-4.aspx

In Linq-to-Entites you won't be allowed to use the ConvertToString call in a query anyway, so this is a brave, but fruitless effort. The reason is that every statement in an L2E query must be an Expression that must be translatable to SQL by the query provider. And (of course) there is no built-in translation for any arbitrary method. L2E is very limited here. It is not even possible to use most native .Net methods like
Users.Select(x=> Convert.ToString(x.UserID)); // NotSupportedException
Linq-to-Sql is a bit more lenient in this. It will run the statement decently. But that does not help you, because you are looking for a common denominator. The bottleneck is L2E and it is a narrow bottleneck.

Related

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

How can I determine if a LINQ query is going to be LINQ to SQL vs. LINQ to Objects?

Usually the distinction between LINQ to SQL and LINQ to Objects isn't much of an issue, but how can I determine which is happening?
It would be useful to know when writing the code, but I fear one can only be sure at run time sometimes.
It's not micro optimization to make the distinction between Linq-To-Sql and Linq-To-Objects. The latter requires all data to be loaded into memory before you start filtering it. Of course, that can be a major issue.
Most LINQ methods are using deferred execution, which means that it's just building the query but it's not yet executed (like Select or Where). Few others are executing the query and materialize the result into an in-memory collection (like ToLIst or ToArray). If you use AsEnumerable you are also using Linq-To-Objects and no SQL is generated for the parts after it, which means that the data must be loaded into memory (yet still using deferred execution).
So consider the following two queries. The first selects and filters in the database:
var queryLondonCustomers = from cust in db.customers
where cust.City == "London"
select cust;
whereas the second selects all and filters via Linq-To-Objects:
var queryLondonCustomers = from cust in db.customers.AsEnumerable()
where cust.City == "London"
select cust;
The latter has one advantage: you can use any .NET method since it doesn't need to be translated to SQL (e.g. !String.IsNullOrWhiteSpace(cust.City)).
If you just get something that is an IEnumerable<T>, you can't be sure if it's actually a query or already an in-memory object. Even the try-cast to IQueryable<T> will not tell you for sure what it actually is because of the AsQueryable-method. Maybe you could try-cast it to a collection type. If the cast succeeds you can be sure that it's already materialized but otherwise it doesn't tell you if it's using Linq-To-Sql or Linq-To-Objects:
bool isMaterialized = queryLondonCustomers as ICollection<Customer> != null;
Related: EF ICollection Vs List Vs IEnumerable Vs IQueryable
The first solution comes into my mind is checking the query provider.
If the query is materialized, which means the data is loaded into memory, EnumerableQuery(T) is used. Otherwise, a special query provider is used, for example, System.Data.Entity.Internal.Linq.DbQueryProvider for entityframework.
var materialized = query
.AsQueryable()
.Provider
.GetType()
.GetGenericTypeDefinition() == typeof(EnumerableQuery<>);
However the above are ideal cases because someone can implement a custom query provider behaves like EnumerableQuery.
I had the same question, for different reasons.
Judging purely on your title & initial description (which is why google search brought me here).
Pre compilation, given an instance that implements IQueryable, there's no way to know the implementation behind the interface.
At runtime, you need to check the instance's Provider property like #Danny Chen mentioned.
public enum LinqProvider
{
Linq2SQL, Linq2Objects
}
public static class LinqProviderExtensions
{
public static LinqProvider LinqProvider(this IQueryable query)
{
if (query.Provider.GetType().IsGenericType && query.Provider.GetType().GetGenericTypeDefinition() == typeof(EnumerableQuery<>))
return LinqProvider.Linq2Objects;
if (typeof(ICollection<>).MakeGenericType(query.ElementType).IsAssignableFrom(query.GetType()))
return LinqProvider.Linq2Objects;
return LinqProvider.Linq2SQL;
}
}
In our case, we are adding additional filters dynamically, but ran into issues with different handling of case-sensitivity/nullreference handling on different providers.
Hence, at runtime we had to tweak the filters that we add based on the type of provider, and ended up adding this extension method:
Using EF core in net core 6
To see if the provider is an EF provider, use the following code:
if (queryable.Provider is Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider)
{
// Queryable is backed by EF and is not an in-memory/client-side queryable.
}
One could get the opposite by testing the provider against System.Linq.EnumerableQuery (base type of EnumerableQuery<T> - so you don't have to test generics).
This is useful if you have methods like EF.Functions.Like(...) which can only be executed in the database - and you want to branch to something else in case of client-side execution.

Using DateTime.Add(TimeSpan) with LINQ

I have to run a query like the one bellow. It is actually more complex, but here is the part that matters:
var results =
from b in _context.Bookings
where b.ScheduleDate.Add(b.StartTime) >= DateTime.UtcNow
select b;
But it gives the following error:
LINQ to Entities does not recognize the method 'System.DateTime.Add
method(System.TimeSpan)', and this method cannot be translated into a
store expression.
How can I work around this?
Thanks in advance.
Try using the SqlFunctions.DateAdd method in the System.Data.Objects.SqlClient namespace. Documentation here. That will convert into the SQL method DateAdd, documented here. You might also be interested in using DateDiff instead, documented here.
In general, look at SqlFunctions for "common language runtime (CLR) methods that call functions in the database in LINQ to Entities queries." LINQ to Entities cannot convert any method call into SQL, but the functions in that class will work.
Your other option is to execute the LINQ to Entities query (using ToList or something similar) and then perform the logic in memory.
SqlFunctions only works with Microsoft Sql Server.
In pure EF you can write:
DbFunctions.AddMilliseconds(x.Date, DbFunctions.DiffMilliseconds(TimeSpan.Zero, x.Time))
This works on all database adapters

Why does this LINQ expression break my loop & conversion logic?

Background
ArticleService is a class that provides methods for front-end layers to facilitate business with the back-end.
Two of its base responsibilities are to convert ViewModels (ArticleViewModel) to the appropriate Models (Article) when persisting data, and the reverse, convert Models to ViewModels when fetching data... so often that I created a private method building ViewModel objects:
private ArticleViewModel BuildViewModel(Article a)
{
return new ArticleViewModel { Title = a.Title /* all properties */ }
}
Moving along, the ArticleService provides a method to fetch all articles from the data store, returning them as ViewModels: public IEnumerable<ArticleViewModel> All()
A calling class uses it like this: var articleViewModels = _articleService.All();
Simple, right?
Problem:
I originally wrote All() lazily with a classic foreach loop:
private IEnumerable<ArticleViewModel> All()
{
var viewModels = new List<ArticleViewModel>();
foreach (var article in _db.Articles)
viewModels.Add(BuildViewModel(article));
return viewModels;
}
It worked fine - articleViewModels was an instantiate list of all view models.
Next, I used ReSharper to convert this loop to a LINQ statement for performance and prettiness, and then combined the assignment statement with the return statement. Result:
private IEnumerable<ArticleViewModel> All()
{
return _db.Articles.Select(article => BuildViewModel(article)).ToList();
}
I debugged the LINQ statement and the beast awakened:
LINQ to Entities does not recognize the method 'ArticleViewModel
BuildViewModel(Article)' and this method cannot be translated into a store expression.
Question - Why does this LINQ statement break my code?
Note: Stepping back to an explicit declaration, assignment, return worked with the LINQ statement, so I'm almost certain it's something to do with the lambda logic.
Question - Why does this LINQ statement break my code?
Because LINQ to Entities is trying to translate BuildViewModel into SQL. It doesn't know how, so it dies miserably.
In your original version, you were streaming the entity from the database to your local box, and then doing the projection using BuildViewModel client-side. That's fine.
so I'm almost certain it's something to do with the lambda logic.
Nope. It's because LINQ to Entities can't translate BuildViewModel into SQL. It doesn't matter if you use a lambda expression to express the projection or not.
You can rewrite the code like this:
return _db.Articles.
.AsEnumerable()
.Select(article => BuildViewModel(article)).ToList();
This causes _db.Articles to be treated as a plain old enumerable, and then does the projection client side. Now LINQ to Entities doesn't have to figure out what to do with BuildViewModel.
The answer by Jason explains the error, but doesn't [didn't] list the simple fix (if you still want to use LINQ). Just add a call to .AsEnumerable() right after "_db.Articles". This will force any future LINQ statements to be performed using LINQ to Objects (in memory) rather than trying to use the IQueryable to perform the statements against the database.
Simply your function BuildViewModel is not traslable to raw SQL. That is.

Multi tenant EF implementation w/ Stored Procedures

When using SProcs in EF4, I understand the concept of mapping views to entities, then using function imports to map my set/update/deletes to sprocs. The question I have is how this applies to multi tenant architecture. Consider the following scenario:
We have several hundred customers utilizing our multi-tenant database/application. Each customer has somewhere between 50-200 Accounts in the Accounts table. If I expose a view to EF, I cannot parameterize that view. So the following line:
query = (from e in context.Accounts select e).where(e => e.companyID = 1)
[forgive me if I'm syntactically incorrect. still learning EF!]
,by definition, would have to return all of the Accounts first, then filter using my wear clause. is this correct? I can't imagine how else the process would work.
Am I missing something here?
That is the difference between Linq-To-Objects and Linq-To-Entities. Linq-To-Objects operates on IEnumerable<T> and you pass delegates to its methods to define the query which will be executed in the memory. Linq-To-Entities operates on IQueryable<T> and you pass expressions to its methods do define expression tree which is transformed by Linq-to-entities provider into another syntax - to SQL!
So your query will be executed in the database and filtering will be done in the database as well. Be aware that after executing commands like AsEnumerable, ToArray, ToDictionary or ToList you transform the rest of the query to Linq-to-objects.
If you write the query on the result of stored procedure execution you are always doing Linq-to-objects only querying ObjectSets directly forms Linq-to-entities queries.
EF shouldn't be bringing all the accounts back first and then filtering. Rather, it should be be emitting a query with a WHERE clause.
You can check using SQL Profiler, just to be 100% sure.

Categories

Resources