How can I parse a value in linq to entity? - c#

I want to do something like this:
var apps = from app in context.Apps
where (platform == AppPlatform.All ||
(app.Platform == sPlatform && new Version(app.PlatformVersion) <= version))&&
(availability == AppAvailability.All || app.Availability == sAvailability)
select app;
return apps.ToList();
The line new Version(app.PlatformVersion) <= version)) is causing an error: Only parameterless constructors and initializers are supported in LINQ to Entities.
Basically what I need is to make my entity model parse the app.PlatformVersion as a new Version() object, instead of string, but I apparently can't do this from within my linq-to-entity. Can I do this at the entity-model level? I have other fields (strings) that I'd like to parse into types as well (like parsing a string to an Enum). How would I accomplish this?

Because EF tries to translate the Linq query to SQL and there is no way to translate Version method. So you could just query with other conditions first and store it in memory, then query from the in-memory object using the complex Linq query.
Technically you could do this, (you can definitely make it better in terms of the performance)
var apps_temp = from app in context.Apps.All().ToList();
//this query will not be translated to SQL.
var apps = from app in apps_temp
where (platform == AppPlatform.All ||
(app.Platform == sPlatform && new Version(app.PlatformVersion) <= version))&&
(availability == AppAvailability.All || app.Availability == sAvailability)
select app;
return apps.ToList();

Related

C# linq database optimization

I have the following C# code:
MyObject myObj= dbCtx.MyObjects.FirstOrDefault(
p => p.Src.AccNum == srcAccNum && p.Dest.AccNum == destAccNum);
It executes fine. However, I would prefer to only query the database if p.SrcId < p.DestId
Is this possible and how would I code it?
Just add it to the predicate.
Querying the DbSet (or an IQueryable) will filter the data directly in database. That is, the predicate will be translated to SQL and just the data which match the predicate will be will be loaded in memory.
MyObject myObj = dbCtx.MyObjects.FirstOrDefault(
p => p.SrcId < p.DestId && p.Src.AccNum == srcAccNum && p.Dest.AccNum == destAccNum);

Entity Framework - String conversion error

I'm getting the following error, but im not sure how to rewrite my statement?
Any ideas?
Error:
LINQ to Entities does not recognize the method 'System.String
Convert(System.String)' method, and this method cannot be translated
into a store expression
Code:
public Client FindClientByMobile(string mobile, string accountId)
{
Client client = RepositorySet.Include("Account").FirstOrDefault(c => c.AccountId == accountId && !c.IsDeleted
&& ((Convert(c.TelephoneHome) == mobile) || (Convert(c.TelephoneMobile) == mobile) || (Convert(c.TelephoneWork) == mobile)));
return client;
}
public static string Convert(string mobile)
{
var filterNumber = from letter in mobile
where char.IsDigit(letter)
select letter;
StringBuilder number = new StringBuilder();
number.Append(filterNumber.ToArray());
return number.ToString();
}
The error means that Linq needs to translate your expression into a Sql statement. Your custom Convert method is not translatable because it is c# code and not something that also exists on the database server.
As you are already passing in your account id I am going to assume this is either unique OR filters it down enough to where it is close to unique to ensure you are not retrieving a large number of objects. You can then materialize the object graph first and then filter more in c# (linq to objects). This is done by using the ToList() call.
public Client FindClientByMobile(string mobile, string accountId)
{
var clients = RepositorySet.Include("Account").Where(c => c.AccountId == accountId && !c.IsDeleted).ToList();
return clients.FirstOrDefault(client => Convert(client.TelephoneHome) == mobile || Convert(client.TelephoneMobile) == mobile || Convert(client.TelephoneWork) == mobile);
}
Does this suits you, as you mentioned in your comment
#Imad, what im trying to do is validate, so if the number has been
stored in the database as 0331-9000-100, I want to remove all non
numeric characters, mobile has already had this applied, so mobile =
033319000100
public Client FindClientByMobile(string mobile, string accountId)
{
Client client = RepositorySet.Include("Account").FirstOrDefault(c => c.AccountId == accountId && !c.IsDeleted
&& ((c.TelephoneHome.Replace("-","") == mobile) || (Convert(c.TelephoneMobile) == mobile) || (Convert(c.TelephoneWork) == mobile)));
return client;
}
using Replace you can also replace other characters like ( and ) also.
Point to remember: Replace(char, char) won't work but Replace(string, string) will.
The first issue is that the Convert call is a C# and can't be translated into SQL. Not all functions can, but some (substring, for example) are 'known' to Entity Framework and it can convert them to the appopriate SQL. So you either need to rewrite your statement to use the string functions EF is aware of, or push the logic down into the database some other way.
#Imad has already suggested using string.Replace, which is known to EF, but of course it's not going to help you remove every possible non-digit character - only ones you are explicitly coding for, like '-', but not other alpha strings like 'extension'.
If you want to make this fast, it might be a good idea to store the 'cleaned' numbers in separate fields. Then the query becomes
Client client = RepositorySet.Include("Account").FirstOrDefault(c => c.AccountId == accountId && !c.IsDeleted
&& (c.TelephoneHomeClean == mobile) || (c.TelephoneMobileClean == mobile) || (c.TelephoneWorkClean == mobile)));
return client;
and you have something that can be made much faster and indexed.

Entity Framework - Linq to Entities - Optional filter

I am struggling to figure out how to get a LINQ statement to produce a specific WHERE clause in SQL in a single statement.
I am after it producing something like this:
SELECT ColA, ColB, ColC, ColN...
FROM Orders
WHERE Client = #ClientId
AND (#CompanyId IS NULL OR #CompanyId = CompanyId)
My (failing) LINQ statement looks like this:
var includeAllCompanies = company == null;
var data = context.Orders.Where(o => o.Client.Id == clientId
&& (includeAllCompanies
|| (c.Company != null && c.Company.Id == company.Id)).ToList();
However, it always throws an exception when the variable company is NULL (it works fine when it has been initialised). The exception being:
Non-static method requires a target.
My current fix is to split my LINQ statement into two. One using an Expression<Func<>> (to be transformed to a SQL statement with partial filtering). Then another that uses Func<> to perform the remaining filters on the returned list.
Expression<Func<>> to let SQL do some of the work (excluding nullable objects)
var data = context.Orders.Where(o => o.Client.Id == clientId).ToList();
Func<> to then filter out the nullable objects
data = data.Where(c => (territory == null
|| (c.Territory != null && c.Territory.Id == territory.Id))).ToList();
This works, however, I want SQL to be performing this query.
The problem is that, company is server-side variable. Regardles includeAllCompanies value, EF has to translate whole LINQ query to SQL - and in this case SQL doesn't know what is company.Id - so EF has to always get company.Id value in order to put into SQL query. Even if company is null (so that is why you get exception). I hope you see my point, if not - I'll try to give some sample.
In order get rid of exception you can do the following:
var companyId = company == null ? null : (int?)company.Id;
var data = context.Orders.Where(o => o.Client.Id == clientId
&& (companyId == null
|| (c.Company != null && c.Company.Id == companyId)).ToList();

LINQ to Entities does not recognize the method IsNullOrWhiteSpace

I have the below code:
var countries = from c in db.Countries
where (string.IsNullOrWhiteSpace(searchAlpha2) || (c.Alpha2 ?? string.Empty).ToUpper().Contains(searchAlpha2.ToUpper()))
&& (string.IsNullOrWhiteSpace(searchAlpha2) || (c.Alpha3 ?? string.Empty).ToUpper().Contains(searchAlpha3.ToUpper()))
&& (string.IsNullOrWhiteSpace(searchName) || (c.Name ?? string.Empty).ToUpper().Contains(searchName.ToUpper()))
select c;
This code uses Entity Framework v6 Code First over a SQL database.
Aside from performance, if I don't include the IsNullOrWhitespace I get no results when the filter criteria are blank (I've tested both null and blank values); however when a value is present this works as expected.
I'm getting the error:
LINQ to Entities does not recognize the method 'Boolean IsNullOrWhiteSpace(System.String)' method, and this method cannot be translated into a store expression.
I'm trying to use the searchXXX strings to filter on columns. I've tried using RegEx.IsMatch, SqlMethods.Like, and the code below, but all give me errors saying those functions are not allowed (errors come from either EntityFramework.SqlServer or from Linq to Entities). I've seen numerous posts on here where this has been done successfully though - so wonder if I'm missing something fundamental?
If you want to use your statement in current form you might want to replace
string.IsNullOrWhiteSpace(searchAlpha2)
to
!(searchAlpha2 == null || searchAlpha2.Trim() == string.Empty)
and all the other values too, for it to get translated to working SQL.
Update: Copied from comment by #DavidKempfner
As of EntityFramework.6.2.0 it generated SQL that checked for
!(searchAlpha2.Trim() == string.Empty),
I.E. It ignored the searchAlpha2 == null || part.
Use this instead:
!string.IsNullOrEmpty(entity.searchAlpha2.Trim())
I would suggest a different approach - use the ability to build queries up on the fly, and thus avoid passing optional query parameters to the expressions altogether - this will result in improved query plans when parsed to sql and executed on the database.
Also, if your database (?SqlServer) is not set to case sensitive collation (i.e. xx_CI_xx), you can avoid the casing conversion as well, as it is redundant:
var myQueryable = db.Countries.AsQueryable();
if (!string.IsNullOrWhiteSpace(searchAlpha2))
{
myQueryable = myQueryable.Where(c => c.Alpha2.Contains(searchAlpha2));
}
...
var countries = myQueryable.ToList();
You can get this and a bunch more functionality using PredicateBuilder
Update
JB: based on StuartLC's answer, here's the code amended to use PredicateBuilder:
var predicate = PredicateBuilder.True<Country>();
if (!string.IsNullOrWhiteSpace(searchAlpha2))
predicate = predicate.And(c => c.Alpha2 != null ? c.Alpha2.Contains(searchAlpha2) : false);
if (!string.IsNullOrWhiteSpace(searchAlpha3))
predicate = predicate.And(c => c.Alpha3 != null ? c.Alpha3.Contains(searchAlpha3) : false);
if (!string.IsNullOrWhiteSpace(searchName))
predicate = predicate.And(c => c.Name != null ? c.Name.Contains(searchName) : false);
IQueryable<Country> countries = db.Countries.AsExpandable().Where(predicate);
when you use linq in Entity Framework to get data from DataBase you need to use only with functions that the Entity Framework can convert to sql query.
I know that there an already accepted answer for this question but I got an idea to share.
Instead of putting multiple checks in the LINQ or using if statement to apply where clause on list result, you can use a simple trick. Just declare a bool variable and assign IsNullOrWhitespace of add to linq like:
bool isNull = string.IsNullOrWhiteSpace(searchAlpha2);
var countries = db.Countries.Where(c => isNull || c.Alpha2.Contains(searchAlpha2)).ToList();

Comparing String Column with Int column

I'm working on EF CTP5 Code First development with an existing database. I need to get the data from two tables by comparing columns of different types.
For example - Here p.ColumnA is varchar as q.ColumnA is int but the values might be the same for few records. So, I'm trying to do Convert.ToInt32 which does not work. I do not have complete control over the database to modify the table.
from p in context.TableA
from q in context.TableB
where p.ColumnZ == "ABC" &&
(p.ColumnA == null || Convert.ToInt32(p.ColumnA) == q.ColumnA) &&
(p.ColumnB == null || p.ColumnB == q.ColumnB)
select p.ColumnC
Can someone suggest a solution? Thanks.
When you write a linq statement that interacts with the entityframework it trys to convert all the commands to SQL. Because there is no sql command for Convert.ToInt32 it is throwing an error.
This post describes a way to cal the sql functions for converting types. It should help you.
As the other posters have explained, LINQ to SQL Entities doesn't know how to translate Convert.ToInt32 into a SQL expression (LINQ to SQL can apparently handle this). From what I can tell, it doesn't support int.Parse, either. But since you're just doing an equality comparison (rather than greater/less than), you should be able to achieve the same result by converting the int to a string, rather than converting the string to an int.
from p in context.TableA
from q in context.TableB
where p.ColumnZ == "ABC" &&
(p.ColumnA == null || p.ColumnA == q.ColumnA.ToString()) &&
(p.ColumnB == null || p.ColumnB == q.ColumnB)
select p.ColumnC

Categories