Error Converting Enum to String in LINQ Query - c#

I have the following query, which is using Entity Framework.
Analytic firstSent = (from a in Repository.Query<Analytic>()
where a.EntityType == "Proposal" &&
a.EntityId == Program.ActiveProposal.Id &&
a.Marker == AnalyticMarker.EmailProposalUrl.ToString()
orderby a.TimestampUtc
select a).FirstOrDefault();
At run time, I get the following error:
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
a.Marker is a string column, and AnalyticMarker.EmailProposalUrl is an enum value, and I want to compare that column against the name of that enum.
I understand that the conversion from an enum to a string is not supported by SQL, but why won't it resolve the value of this string and then pass the resulting string to SQL? That should work just fine.

Try this:
var emailProposalUrl = AnalyticMarker.EmailProposalUrl.ToString();
Analytic firstSent = (from a in Repository.Query<Analytic>()
where a.EntityType == "Proposal" &&
a.EntityId == Program.ActiveProposal.Id &&
a.Marker == emailProposalUrl
orderby a.TimestampUtc
select a).FirstOrDefault();
This other answer is explaining the reason why this could work as well..
The problem arises because ToString() isn't really executed, it is
turned into a MethodGroup and then parsed and translated to SQL.
Since there is no ToString() equivalent, the expression fails.

Well, in the places where C# will normally resolve tostring for you, it normally takes an object. Here, for example though, you're asking it to compare a string to an enum. Although C# COULD call tostring(), its not correct for it to do so.
Think about if it was int you were trying to compare against, rather than an enum. You can't compare the number 1 and a string. (Well, you can, but it involves assumptions, which is the point; the language is just trying to prod you into making the logic that little more explicit)

Related

Error while using Where clause in LINQ to SQL?

i am getting below error while applying filter condition to the LINQ to SQL table.
Is there a way to create the SQL query with Where condition in LINQ to SQL?
Error:
"Method 'Boolean Equals(System.String, System.String, System.StringComparison)' has no supported translation to SQL."
Below is the code
public IQueryable<DocumentReplacementPack> GetDocumentReplacementPack(string state,int typeID)
{
if (this.DataContext.DocumentReplacementPacks.Count() > 0)
**return this.DataContext.DocumentReplacementPacks.Where(d => string.Equals(d.State, state, StringComparison.InvariantCultureIgnoreCase));**
else
return this.DataContext.DocumentReplacementPacks;
}
No this is not an error, sadly this comes up a lot more than you think. Let's look at how linq converts expressions to sql from a high level
this.DataContext.DocumentReplacementPacks.Where(d => string.Equals(d.State, state, StringComparison.InvariantCultureIgnoreCase));
this will generate a sql call that looks similar to
select * from [DocumentReplacementPacks] where ??
now what is going to happen is the expression is going to get to the string.Equals call and since there is no REAL equivalent of string.Equals in sql, linq has no idea how to generate the sql string. Since string.Equals is a c# function and not a sql function. A really good example would be something like
public bool AreEqual(string one, string two){
return one.ToCharArray()[0] == two.ToCharArray()[0];
}
Now this is a very trivial example, but think if you had a linq expression like such:
return this.DataContext.DocumentReplacementPacks.Where(d => AreEqual(d.State, state));
How would you expect this to get translated? There is just no way, how would linq know exactly how to the function acts.
The way around this would be to switch to == since linq knows how to translate that.
return this.DataContext.DocumentReplacementPacks.Where(d =>d.State == state);
and let the database do the InvariantCultureIgnoreCase, now if that is not an option you will just have to read the data into memory and do the call.
this.DataContext.DocumentReplacementPacks.ToList().Where(d => string.Equals(d.State, state, StringComparison.InvariantCultureIgnoreCase));
but I do not advise that since reading a whole dataset into memory can be very very expensive.
* OFF TOPIC *
Also I was looking at your code and I highly recomment changing this.DataContext.DocumentReplacementPacks.Count() > 0)
to
this.DataContext.DocumentReplacementPacks.Any())
Explanation. I'm often disappointed with how people use linq without truly understanding IEnumerable/IQuerable but how else do you become a better developer?
MS SQL by default treats strings case insensitive, so you should use '==' which can be translated by LINQ2SQL

Using string methods in EF LINQ queries

I have a situation wherein two urls need to be compared, one coming in to the method and the other in the db. The one in the db may or may not be Url decoded and will have %20 for space and so on.
Here's the linq query in the method:
var Result = (from s in context.301Redirects
where s.IsDeleted == false && s.Status == true && HttpUtility.UrlDecode(s.OldURL).ToUpper() == HttpUtility.UrlDecode(OldURLPath).ToUpper()
select s.NewURL).FirstOrDefault();
return Result;
Inside the method I can UrlDecode the incoming param but what about the one in the db?
This one fails because EF will not recognize UrlDecode method and throw and exception saying so.
Is there a workaround?
You can bring the collection in memory, then perform the UrlDecode at that point by evaluating the query. Try:
var Result = (from s in context.301Redirects
where s.IsDeleted == false && s.Status == true)
.AsEnumerable()
.FirstOrDefault(HttpUtility.UrlDecode(s.OldURL).ToUpper() == HttpUtility.UrlDecode(OldURLPath).ToUpper();
return Result;
If your database table is huge though, I'd consider creating some sort of TRIGGER/update script to either URL encode or decode all records, that way you don't have to check at all, you just encode/decode the parameter passed in.
Do your adjustments before the query so that you have absolute values for comparisons in the query that align with the particular formats, and always check against the ones in the database, rather than transform them - so, this potentially includes using OR in your query, instead of pulling the whole thing into memory a la ToList.

Convert IQueryable<T> or LINQ query to unique string

I have some LINQ query (or IQueryable<T> object based on LINQ query) and want to get some unique string based on this query.
I have, for example:
var someValue = 10;
var query = (from i in db.Customers
where i.Id == someValue
select i).AsQueryable();
I should get something like this:
"from i in db.Customers where i.Id == 10"
I am trying to use Expression object and play with it but I can not get generic approach to get string with exact parameters values.
E.g.:
public string GetKey<T>(IQueryable<T> query)
{
...
return unique_string;
}
Note that different parameters values for the same LINQ query should provide different strings.
Thanks in advance.
I strongly suspect that this is simply not going to work. Aside from anything else, if you have to use AsQueryable (i.e. if your original query is over IEnumerable<T> then the compiler will have used delegates instead of expression trees to start with.
If it's a pure IQuerable<T> all the way, you could try using query.Expression.ToString(), but frankly it's not something I'd want to rely on.
Solved this issue with Expression Tree Serialization with some improvements for getting exact values of passed parameters. It provides a big but unique XML file based on IQueryable objects.

Linq to SQL Join and Contains Operators

in the following query
var restrictions = from p in dcTrad.quop_restricted_items
where p.entry_status == 'P' && p.batch == "PRODUCTION" && p.problem != null
from q in dcTrad.model_companies
where q.co_name != null && p.brimsec == q.primary_bsec
select new { Company = q.co_name, Restriction = p.comment ?? "Restricted without comments", Portfolio = p.problem };
I need to replace
p.brimsec == q.primary_bsec
with
p.brimsec.StartsWith ( q.primary_bsec )
but I get the following error:
Only arguments that can be evaluated on the client are supported for the String.StartsWith method
How can I make this work?
Unfortunately the LINQ to SQL translator is not smart enough to translate this code, but there's a trick that achieves the same:
p.brimsec.StartsWith ( q.primary_bsec )
Translates to:
p.brimsec.SubString(0, q.primary_bsec.Length) == q.primary_bsec
The LINQ to SQL translator handles this just fine, and the semantics are equivalent to StartsWith.
Frankly, I don't see why translating StartsWith properly for server-side arguments was so hard that the LINQ developers just decided to throw an error instead.
Basically the linq to sql does not know how to convert startswith to Sql. This is because internally at run time your linq is code generated to sql.
You may go about achieving this by creating a UDF (user defined function in sql) and using it from your linq statement.
The article is as below:
http://msdn.microsoft.com/en-us/library/bb399416.aspx
Andrew
I think the problem you're running into is that linq-to-sql has no translation of String.StartsWith into SQL. String.Contains does work, though - you'd need to go through your resultant collection and filter out the items that don't start with q.primary_bsec.

Conditional shortcuts in LinqToSql query

Here's a little LinqToSql GOTCHA:
// Returns the number of counties in a state,
// or all counties in the USA if the state is null
public static int CountCounties(State s) {
var q =
from cy in County.GetTable() // my method to get the ITable
where (s == null || s.Code == cy.StateCode) // shortcut OR operator, right...?
select cy;
return q.Count();
}
Guess what - if you pass a null State object to this method, you get a null reference exception! It seems that LinqToSql doesn't use the || shortcut operator as a shortcut!
Answer credit goes to whoever proposes the best explanation & workaround for this.
If it's linq to sql then remeber that Linq is just parsing your query into SQL.
It is therefore sending both of your where clauses to the database, hence the exception. I dont find this surprising really, though it is arguably wrong.
You will just have to do an independant check.
if (!string.isNullOrEmpty(state.statecode)
q = q.where( s => s.code == state.statecode
This is not related to LINQ in general. In this case, the LINQ-to-SQL provider tries to parse the your lambda expression and make it a TSQL query. It cannot make too many assumptions based on your expression since it's trying to delegate most of the work to the database.
Long story short, the provider simply cannot translate it to SQL.

Categories