Comparing String Column with Int column - c#

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

Related

Where condition equal true and Nullable object must have a value

I've been looking for an answer but I couldn't find anything to help me. I get this error
Nullable object must have a value.
My request is:
from e in dc.tblElements
where
e.IsUnique &&
(e.TypeID == 2) &&
(categoryId != null ? e.CategoryId.Value == categoryId.Value : true) &&
((e.Name.Contains(keyword)) ||
(e.Keywords.Contains(keyword)))
select e
The third line of the where condition is the problem (categoryId). If categoryId has a value, it works but not when it is null. However, I replaced this line with true and it works as well. I can't understand what is the problem here.
in my table CategoryId can be null so I tried:
(categoryId.HasValue && e.CategoryId.HasValue ? e.CategoryId.Value == categoryId.Value : true)
What I want to do: I want to select all the elements of this table depending on the where condition. categoryId comes from a drop down so if the default value is still selected when the user does the request, I want to display all the elements no matter what the category.
You should be good with just comparing your two variables:
e.CategoryId == categoryId
If you want special treatment of one being NULL, maybe because you want that to be a special case where NULL matches everything instead of just another NULL, you can add that:
e.CategoryId == categoryId || !e.CategoryId.HasValue || !categoryId.HasValue
Your problem with your statement is that you access .Value. Yes, if you would run the code with Linq-To-Objects in memory, it would work because the compiler will only run the code of one branch of your if-statement (ternary operator, I know, but you get what I mean). But for a database, there needs to be a statement prepared. That statement needs to be there in full, it does not use any short-circuiting. So the statement builder will access both your branches to build that statement for the database and one of those branches will fail because it accesses .Value although there is none.
Make CategoryId as nullable type and try.
Nullable<int> CategoryId = null;
Looks like you are trying to implement a "catch-all" categoryId parameter. That's an anti-pattern in SQL and a strong smell that can lead to bad performance.
In LINQ, it's not necessary since you can add .Where() conditions just by adding another .Where() call to your query, eg :
var query = from e in dc.tblElements
where
e.IsUnique &&
e.TypeID == 2 &&
( e.Name.Contains(keyword) ||
e.Keywords.Contains(keyword) )
select e;
if (categoryId.HasValue)
{
query=query.Where(e.CategoryId == categoryId);
}
You can use this to add multiple conditions at runtime
Try this:
from e in dc.tblElements
where
e.IsUnique &&
(e.TypeID == 2) &&
(categoryId.HasValue && e.CategoryId.Value == categoryId.Value) &&
((e.Name.Contains(keyword)) ||
(e.Keywords.Contains(keyword)))
select e

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();

How can I parse a value in linq to entity?

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();

LINQ query, ignoring results with certain decimal points

I need to perform a LINQ query on a large database in C#. One of the columns I need to use in the query is a double. I need to omit results that have more than 4 decimal places in this column. The database can't be changed as other programs need to use it and make use of what I don't want. The results are then added to a list to use later. I thought that this would work.
where fun.Units != '*.?????*'
However it returns the error that too many characters are in the character literal.
The whole query looks like this so far
var clientQuery1 = from cli in main1.Clients
from pol in main1.Policies
from fun in main1.FundHoldings
from uni in main1.UnitPrices
where cli.AccountNumber == accNum
&& pol.ClientRef == cli.ClientRef
&& fun.FKeyRef == pol.PolicyRef
&& uni.UnitPriceRef == fun.UnitPriceRef
&& fun.Units != '*.?????*'
select uni.UnitName;
Can you please try with this below query and let me know.
var clientQuery1 = from cli in main1.Clients
from pol in main1.Policies
from fun in main1.FundHoldings
from uni in main1.UnitPrices
where cli.AccountNumber == accNum
&& pol.ClientRef == cli.ClientRef
&& fun.FKeyRef == pol.PolicyRef
&& uni.UnitPriceRef == fun.UnitPriceRef
&& fun.Units == Math.Round(Convert.ToDouble(fun.Units),4)
select uni.UnitName;
Well you can solve that particular error using:
&& fun.Units != "*.?????*"
Note the change from single quotes to double quotes. However, that's not going to help you overall. What's the type of fun.Units in LINQ? If it's decimal, you might be able to use:
&& decimal.Round(fun.Units, 4) == fun.Units
... but it's not clear to me what that will do in the generated SQL. It's worth a try, but even if it works you should see what the SQL looks like.

Categories