I would like to create a repository model that could take an Expression and use Linq-To-Sql to generate the required SQL statement.
For example, I have a function such as this:
// Possible criteria
Expression<Func<Purchase,bool>> criteria1 = p => p.Price > 1000;
// Function that should take that criteria and convert to SQL statement
static IEnumerable<Customer> GetCustomers (Expression<Func<Purchase,bool>> criteria)
{
// ...
}
Inside the function, I would like to convert criteria to a SQL statement using Linq-To-Sql.
I am aware that you can use DataContext.Log to see the executed queries and DataContext.GetCommand(query).CommandText to see the full query before it is executed. However, I would like just a part of the entire expression generated.
What I am hoping to accomplish is to make my repository abstract the underlying technology (Linq-to-Sql, Dapper, etc). That way I could pass the Expression to the repository, have it generate the right statement and use the right technology to execute it.
You could do something like this:
string sql = DataContext.GetTable<Customer>().Where(criteria).ToString();
ToString() gives you the SQL expression. You could then use regex to pull out the WHERE clause.
This is a code excerpt that I use to build my own predicate to use in the Where function. The compiler can't cope with ienumerables of complex objects, so you have to do it yourself.
Essentially, the code gets passed an ienumerable of (string code, string exchange) tuples, and then builds an expression to retrieve all Security objects that have Security.Code == tuple.Code AND (Security.MasterExchangeForStocksId == tuple.exchange OR SecurityExchangeId == tuple.exchange).
CreateTrEntitiesAsync() simply returns a Entity Framework context, which has a DbSet Security property.
public async Task<Security[]> GetSecurities(IEnumerable<(string code, string exchange)> tickers)
{
using (var ctx = await CreateTrEntitiesAsync())
{
var securityExpr = Expression.Parameter(typeof(Security), "security");
Expression expr = null;
Expression exprToadd;
foreach (var item in tickers)
{
exprToadd = Expression.And(
Expression.Equal(Expression.Property(securityExpr, nameof(Security.Code)), Expression.Constant(item.code)),
Expression.Or(
Expression.Equal(Expression.Property(Expression.Property(securityExpr, nameof(Security.Exchange)), nameof(Exchange.MasterExchangeForStocksId)), Expression.Constant(item.exchange)),
Expression.Equal(Expression.Property(securityExpr, nameof(Security.ExchangeId)), Expression.Constant(item.exchange))
)
);
if (expr == null)
expr = exprToadd;
else
expr = Expression.Or(expr, exprToadd);
}
var criteria = Expression.Lambda<Func<Security, bool>>(expr, new ParameterExpression[] { securityExpr });
var items = ctx.Securities.Where(criteria);
return await items.ToArrayAsync();
}
}
Related
I'm trying to write a piece of code that is going to be used to select a value from a database. As a test I have already created this test version of the method that "works" but isn't great.
private object? FindObjectByProperty(IQueryable query, PropertyInfo primaryKeyProperty, object lookupValue)
{
object? result = null;
foreach (var value in query)
{
if (!primaryKeyProperty.GetValue(value)!.Equals(lookupValue))
continue;
result = value;
break;
}
return result;
}
The code will iterate through the query and find the first entry that matches the Equals. However, if the query contains millions of rows, as it's likely to when the IQueryable is really a DbSet (Which in this use case it is. Only I don't know what T, as it could be any of the DbSets on my EF Core data context.
What I'd like to end up with is something that looks something along these lines....
private object? FindObjectByProperty(IQueryable query, PropertyInfo primaryKeyProperty, object lookupValue)
{
return query.Where( x => x.*primaryKeyProperty* == lookupValue).FirstOrDefault();
}
The above code is pseudo code, the problems that I have is that query does not have a .Where available. Also primaryKeyProperty will need to be translated to the actual property.
The idea is that when this code is executed, ultimately executing the query, will generate a sql statement which selects a single item and returns it.
Can anyone help with solving this?
Update:
I'm working on a solution to this, so far this is what I've come up with
//using System.Linq.Dynamic.Core;
private object? FindObjectByProperty(IQueryable query, PropertyInfo primaryKeyProperty, object lookupValue)
{
var parameter = Expression.Parameter(query.ElementType);
var e1 = Expression.Equal(Expression.Property(parameter, primaryKeyProperty.Name), Expression.Constant(lookupValue));
var lambda = Expression.Lambda<Func<object, bool>>(e1, parameter);
return query.Where(lambda).FirstOrDefault();
}
This is failing on the because the func uses Object, when it really needs the real type. Trying to figure that bit out. This is getting closer.
Update #2:
Here's the answer that I needed
private object? FindObjectByProperty(IQueryable query, PropertyInfo primaryKeyProperty, object lookupValue)
{
var parameter = Expression.Parameter(query.ElementType);
var propExpr = Expression.Property(parameter, primaryKeyProperty);
var lambdaBody = Expression.Equal(propExpr, Expression.Constant(lookupValue, primaryKeyProperty.PropertyType));
var filterEFn = Expression.Lambda(lambdaBody, parameter);
return query.Where(filterEFn).FirstOrDefault();
}
The difference is that the Expression.Lambda no longer tries to define the func using generics. This is the only change that I needed to do to make the code function as I wanted.
In the test case that I was using, the T-SQL produced to lookup the value looks like this...
SELECT TOP(1) [g].[Id], [g].[Deleted], [g].[Guid], [g].[Name], [g].[ParentId]
FROM [Glossaries].[Glossaries] AS [g]
WHERE [g].[Id] = CAST(3 AS bigint)
The table [Glossaries].[Glossaries] is provided by the input query. The column name Id is provided by the primaryKeyProperty, and the number 3 is provided by the lookupValue.
This is perfect for my needs as I simply needed to select that one row and nothing else, so that I can effectively lazy load the my object property when I need it, and not before.
Also this code will be reused for many different tables.
In order to build add Where to an IQueryable where you don't have access to the actual IQueryable<T>, you need to take a step back (or up?) from calling Where at compile-time, and build the Where call at runtime, as well as the predicate lambda:
public static class DBExt {
public static IQueryable WherePropertyIs<T2>(this IQueryable src, PropertyInfo propInfo, T2 propValue) {
// return src.Where(s => s.{propInfo} == propValue)
// (T s)
var sParam = Expression.Parameter(src.ElementType, "s");
// s.propInfo
var propExpr = Expression.Property(sParam, propInfo);
// s.{propInfo} == propValue
var lambdaBody = Expression.Equal(propExpr, Expression.Constant(propValue));
// (T s) => s.{PropInfo} == propValue
var filterEFn = Expression.Lambda(lambdaBody, sParam);
var origQuery = src.Expression;
// IQueryable<Tx>.Where<Tx, Expression<Func<Tx, bool>>>()
var whereGenericMI = typeof(Queryable).GetMethods("Where", 2).Where(mi => mi.GetParameters()[1].ParameterType.GenericTypeArguments[0].GenericTypeArguments.Length == 2).First();
// IQueryable<T>.Where<T, Expression<Func<T, bool>>>()
var whereMI = whereGenericMI.MakeGenericMethod(src.ElementType);
// src.Where(s => s.{propertyInfo} == propValue)
var newQuery = Expression.Call(whereMI, origQuery, filterEFn);
return src.Provider.CreateQuery(newQuery);
}
}
I have this idea to create a "list" of IQueryables that do different kinds of operations.
So basically:
var query1 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Name == "Ronald");
var query2 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Age == 43);
var query3 = Enumerable.Empty<Person>().AsQueryable().Select(e => e.EyeColor);
var listOfQueries = new List<IQueryable<Person>
{
query1,
query2,
query3
};
Now, I also have this DbSet full of "Persons" and I would like to "apply" all my queries against that DbSet. How would I do that? Is it even possible?
An updated example:
var personQueryFactory = new PersonQueryFactory();
var personQueryByFirstname = personQueryFactory.CreateQueryByFirstname("Ronald"); //Query by Firstname.
var personQueryByAge = personQueryFactory.CreateQueryByAge(42); //Query by age.
var personQueryByHasChildWithAgeOver = personQueryFactory.CreateQueryByChildAgeOver(25); //Query using a "join" to the child-relationship.
var personQuerySkip = personQueryFactory.Take(5); //Only get the 5 first matching the queries.
var personQuery = personQueryFactory.AggregateQueries //Aggragate all the queries into one single query.
(
personQueryByFirstname,
personQueryByAge,
personQueryByHasChildWithAgeOver,
personQuerySkip
);
var personSurnames = personsService.Query(personQuery, e => new { Surname = e.Surname }); //Get only the surname of the first 5 persons with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
var personDomainObjects = personsService.Query<DomainPerson>(personQuery); //Get the first 5 persons as a domain-object (mapping behind the "scenes") with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
var personDaos = personsService.Query(personQuery); //Get the first 5 persons as a DAO-objects/entityframework-entities with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
The reason for doing this would be to create a more "unified" way of creating and re-using predefined queries, and then to be able to execute them against the DbSet and return the result as a domain-object and not an "entity-framework-model/object"
This is possible, but you need to reframe your approach slightly.
Storing the filters
var query1 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Name == "Ronald");
var query2 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Age == 43);
var query3 = Enumerable.Empty<Person>().AsQueryable().Select(e => e.EyeColor);
Reading your intention, you don't actually want to handle IQueryable objects, but rather the parameter that you supply to the Where method.
Edit: I missed that the third one was a Select(), not a Where(). Ive adjusted the rest of the answer as if this had also been a Where(). See the comments below for my response as to why you can't mix Select() with Where() easily.
Func<Person,bool> filter1 = (e => e.Name == "Ronald");
Func<Person,bool> filter2 = (e => e.Age == 43);
Func<Person,bool> filter3 = (e => e.EyeColor == "Blue");
This stores the same information (filter criteria), but it doesn't wrap each filter in an IQueryable of its own.
A short explanation
Notice the Func<A,B> notation. In this case, A is the input type (Person), and B is the output type (bool).
This can be extended further. A Func<string,Person,bool> has two input parameters (string, Person) and one output parameter (bool). A usage example:
Func<string, Person, bool> filter = (inputString, inputPerson) => inputString == "TEST" && inputPerson.Age > 35;
There is always one output parameter (the last type). Every other mentioned type is an input parameter.
Putting the filters in a list
Intuitively, since Func<Person,bool> represents a single filter; you can represent a list of filters by using a List<Func<Person,bool>>.
Nesting generic types get a bit hard to read, but it does work just like any other List<T>.
List<Func<Person,bool>> listOfFilters = new List<Func<Person,bool>>()
{
(e => e.Name == "Ronald"),
(e => e.Age == 43),
(e => e.EyeColor == "Blue")
};
Executing the filters
You're in luck. Because you want to apply all the filters (logical AND), you can do this very easily by stacking them:
var myFilteredData = myContext.Set<Person>()
.Where(filter1)
.Where(filter2)
.Where(filter3)
.ToList();
Or, if you're using a List<Func<Person,bool>>:
var myFilteredData = myContext.Set<Person>().AsQueryable();
foreach(var filter in listOfFilters)
{
myFilteredData = myFilteredData.Where(filter);
}
Fringe cases
However, if you were trying to look for all items which fit one or more filters (logical OR), it becomes slightly more difficult.
The full answer is quite complicated.. You can check it here.
However, assuming you have the filters set in known variables, there is a simpler method:
Func<Person, bool> filterCombined =
e => filter1(e) || filter2(e) || filter3(e);
var myFilteredData = myContext.Set<Person>()
.Where(filterCombined)
.ToList();
One of the problems is that the collection of IQueryable is only valid as long as your DbSet is valid. As soon as your DbContext is Disposed your carefully filled collection is worthless.
So you have to think of another method to reconstruct the query than the one that uses the DbSet<Person>
Although at first glance they seem the same, there is a difference between IEnumerable and IQueryable. An Enumerable has everything in it to enumerate over the resulting sequence.
A Queryable on the other hand holds an Expression and a Provider. The Provider knows where the data can be fetched. This is usually a database, but it can also be a CSV-file or other items where you can fetch sequences. It is the task of the Provider to interpret the Expression and to translate it info a format that the database can understand, usually SQL.
While concatenating the linq statements into one big linq statements, the database is not accessed. Only the Expression is changed.
Once you call GetEnumerator() and MoveNext() (usually by doing ForEach, or ToList(), or similar), the Expression is sent to the Provider who will translate it into a query format that the database understands and perform the query. the result of the query is an Enumerable sequence, so Getenumerator() and MoveNext() of the provider's query result are called.
Because your IQueryable holds this Provider, you can't enumerate anymore after the Provider has been disposed.
When using entity framework, the DbSet holds the Provider. In the Provider is the Database information held by the DbContext. Once you Dispose the DbContext you can't use the IQueryable anymore:
IQueryable<Person> query = null;
using (var dbContext = new MyDbcontext())
{
query = dbContext.Persons.Where(person => person.Age > 20);
}
foreach (var person in query)
{
// expect exception: the DbContext is already Disposed
}
So you can't put the Provider in your collection or possible queries. However, you could remember the Expression. The only thing your require from your Expression is that it returns a Person. You also need a function that takes this Expression and a QueryaProvider for Persons to convert it to an IQueryable.
Let's create a generic function for this, so It can be used for any type, not just for Persons:
static IQueryable<TSource> ToQueryable<TSource>(this IQueryProvider provider,
Expression expression)
{
return provider.CreateQuery(expression);
}
// well, let's add the following also:
static IQueryable<Tsource> ToQueryable<TSource>(this DbContext dbContext,
Expression expression)
{
return dbContext.Set<TSource>.Provider.ToQueryable<TSource>(expression);
}
For help on extension functions see Extension Functions Demystified
Now only once you create your collection of Expressions. For fast lookup make it a Dictionary:
enum PersonQuery
{
ByFirstname,
ByAge,
ByHasChildWithAgeOver,
Skip,
}
public IReadOnlyDictionary<PersonQuery, Expression> CreateExpressions()
{
Dictionary<PersonQuery, Expression> dict = new Dictionary<PersonQuery, Expression>();
using (var dbContext = new MyDbContext())
{
IQueryable<Person> queryByFirstName = dbContext.Persons
.Where(...);
dict.Add(PersonQuery.ByfirstName, queryByFirstName.Expression);
... // etc for the other queries
}
return dict.
}
Usage:
IReadOnlyCollection<Person> PerformQuery(PersonQuery queryId)
{
using (var dbContext = new MyDbContext())
{
// get the Expression from the dictionary:
var expression = this.QueryDictionary[queryId];
// translate to IQueryable:
var query = dbContext.ToQueryable<Person>(expression);
// perform the query:
return query.ToList();
// because all items are fetched by now, the DbContext may be Disposed
}
}
I am trying to create a custom collection from an IQueryable object, where i am trying to perform a select statement but getting an error cannot convert to store expression. I am new to Lambda Expression. Kindly help me how to fix this problem.
Getting error at line c.Event.FirstUpper()
public static string FirstCharToUpper(string input)
{
if (String.IsNullOrEmpty(input))
return string.Empty;
var trimmed = input.Trim();
return trimmed.First().ToString().ToUpper() + trimmed.Substring(1);
}
public static Expression<Func<string, string>> GetFirstCaseToUpperExpression()
{
var expression = NJection.LambdaConverter.Fluent.Lambda.TransformMethodTo<Func<string, string>>()
.From(() => StringFormatter.FirstCharToUpper)
.ToLambda();
return expression;
}
Calling the Expression
return new List<LoggerModel>(
logDB.PELoggers
.Where(c => (c.SubscriberCode == SubscriberCode)).OrderByField(sortBy, ascendingOrder).Select(c => new LoggerModel()
{
DateTime = c.DateTime.Value,
Event = c.Event.FirstUpper()
})
I suppose you are using Entity Framework or a smiliar O/R mapper.
Think about what you are doing here: you are writing a LINQ query that should be executed against your database. To do this, it will translate your LINQ query into a SQL query which will then be executed against your database.
But FirstCharToUpper() is a custom method in your code. Your database does not know anything about it, so your O/R mapper's LINQ provider cannot translate it into anything meaningful in SQL, hence you get the error.
So what you need to do is to first "finish" the query against your database to have the results in-memory and after that, apply any further processing that can only be done within the boundaries of your code on that in-memory collection.
You can do this simply by inserting .AsEnumerable() in your LINQ query before you do the select with your custom expression:
logDB.PELoggers
.Where(c => (c.SubscriberCode == SubscriberCode))
.OrderByField(sortBy, ascendingOrder)
.AsEnumerable()
.Select(c => new LoggerModel()
{
DateTime = c.DateTime.Value,
Event = c.Event.FirstUpper()
})
When calling AsEnumerable(), the query against your database will be executed and the results are copied into an IEnumerable in memory. The Select() afterwards will now already be executed against the in-memory collection and not against the database anymore, thus it can use your custom FirstCharToUpper() method.
Edit based on your comments below:
Everything above is still valid, but in the comments you said your function needs to return IQueryable. In your case, what your FirstCharToUpper() method is doing is pretty simple and the LINQ-to-Entities provider does support methods like ToUpper and Substring. So I'd recommend to simply get rid of your helper method and instead write your LINQ query to do just that with methods that Entity Framework can translate to valid SQL:
logDB.PELoggers
.Where(c => (c.SubscriberCode == SubscriberCode))
.OrderByField(sortBy, ascendingOrder)
.Select(c => new LoggerModel()
{
DateTime = c.DateTime.Value,
Event = c.Event.Substring(0, 1).ToUpper()
+ c.Event.Substring(1)
})
This will result in a SQL query that will already return the content in Event with an uppercase first letter right from the database.
To also support the IsNullOrEmpty check and the Trim you are doing (both also supported by LINQ-to-Entities) I recommend to change the lambda syntax to the LINQ query syntax so you can use the let statement for the trimming, which makes the code cleaner:
from c in logDB.PELoggers
let trimmedEvent = c.Event.Trim()
where c.SubscriberCode == SubscriberCode
select new LoggerModel()
{
DateTime = c.DateTime.Value,
Event = !string.IsNullOrEmpty(trimmedEvent)
? trimmedEvent.Substring(0, 1).ToUpper()
+ trimmedEvent.Substring(1)
: string.Empty
};
In case you do not want to have this done in the LINQ query, you would need to do the uppercasing at some point later when your query against the DB has been executed, for example right in the View that will show your data. Or one option could be to apply the uppercasing in the Event property setter of your LoggerModel:
public class LoggerModel
{
// ...
private string event;
public string Event
{
get { return event; }
set { event = FirstCharToUpper(value); }
}
// ...
}
But there is no way to make custom functions work inside LINQ-to-Entities queries.
In our application we want to have standard methods for various conditions in our database. For instance, we have different types of transactions, and we want to create standard methods for retrieving them within other queries. However, this gives us the error:
Method '' has no supported translation to SQL
The method might look like this:
public static bool IsDividend(this TransactionLog tl)
{
return tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
}
To be used as such:
var dividends = ctx.TransactionLogs.Where(x => x.IsDividend());
Of course, if I copy the logic from IsDividend() into the Where clause, this works fine, but I end up duplicating this logic many places and is hard to track down if that logic changes.
I think if I would convert this to an expression like this it would work, but this is not as preferable a setup as being able to use methods:
public Expression<Func<TransactionLog, bool>> IsDividend = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
var dividends = ctx.TransactionLogs.Where(IsDividend);
Is there a way to force Linq to evaluate the method as an expression? Or to "transform" the method call into an expression within a linq query? Something like this:
var dividends = ctx.TransactionLogs.Where(tl => ToExpression(tl.IsDividend));
We are using Linq-to-SQL in our application.
Well having static property containing the expressions seems fine to me.
The only way to make it work with Methods would be to create a method which returns this expression, and then call it inside where:
public class TransactionLog
{
Expression<Func<TransactionLog, bool>> IsDividend() {
Expression<Func<TransactionLog, bool>> expression = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
return expression;
}
}
public class TransactionLogExtension
{
Expression<Func<TransactionLog, bool>> IsDividend(this TransactionLog log) {
Expression<Func<TransactionLog, bool>> expression = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
return expression;
}
}
and use it via
var dividends = ctx.TransactionLogs.Where(TransactionLog.IsDividend());
or as extension method
var dividends = ctx.TransactionLogs.Where(x.IsDividend());
But none of it is will work with var dividends = ctx.TransactionLogs.Where(x => x.IsDividend()); because x => x.IsDividend(); itself is an expression tree and your database provider can't translate "IsDividend" into an SQL statement.
But the other two options will at least allow you to pass in parameters (which doesn't work if you store the Expressions as instance or static properties).
I think that LINQ to SQL doesn't fully supports even common and build-in functions. (At least EF does not do it). And moreover - when it deals with user defined methods. I predict that your variant with expression will fall as well as the variant with method call unless you call it after enumeration (ToList or similar method). I suggest to keep the balance between 1) stored procedures at server, 2) common conditions hardcoded in Where clauses in C#, 3) expression trees generation in C#. All these points are relatively complex, for sure (and to my mind there is no easy and general solution).
Try using Extension Methods, like so:
public static class TransactionLogExtensions {
public static IQueryable<TransactionLog> OnlyDividends(this IQueryable<TransactionLog> tl)
{
return tl.Where(t=>t.SourceTypeID == (int)JobType.Dividend || t.SourceTypeID == (int)JobType.DividendAcct);
}
}
Call it like so:
var dividends=db.TransactionLogs.OnlyDividends();
or
var dividends=db.TransactionLogs.OnlyDividends().OrderBy(...);
For this scenario you can use Func. Linq works very good with those.
Here is the simple example of using Func in Linq query. Feel free to modify and use.
Func<int,bool> isDivident = x => 3==x;
int[] values = { 3, 7, 10 };
var result = values.Select (isDivident );
I'm migrating some stuff from one mysql server to a sql server but i can't figure out how to make this code work:
using (var context = new Context())
{
...
foreach (var item in collection)
{
IQueryable<entity> pages = from p in context.pages
where p.Serial == item.Key.ToString()
select p;
foreach (var page in pages)
{
DataManager.AddPageToDocument(page, item.Value);
}
}
Console.WriteLine("Done!");
Console.Read();
}
When it enters into the second foreach (var page in pages) it throws an exception saying:
LINQ to Entities does not recognize the method 'System.String
ToString()' method, and this method cannot be translated into a store
expression.
Anyone know why this happens?
Just save the string to a temp variable and then use that in your expression:
var strItem = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == strItem
select p;
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.
Note:
Make sure you also check out Alex's answer regarding the SqlFunctions helper class that was added later. In many cases it can eliminate the need for the temporary variable.
As others have answered, this breaks because .ToString fails to translate to relevant SQL on the way into the database.
However, Microsoft provides the SqlFunctions class that is a collection of methods that can be used in situations like this.
For this case, what you are looking for here is SqlFunctions.StringConvert:
from p in context.pages
where p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;
Good when the solution with temporary variables is not desirable for whatever reasons.
Similar to SqlFunctions you also have the EntityFunctions (with EF6 obsoleted by DbFunctions) that provides a different set of functions that also are data source agnostic (not limited to e.g. SQL).
The problem is that you are calling ToString in a LINQ to Entities query. That means the parser is trying to convert the ToString call into its equivalent SQL (which isn't possible...hence the exception).
All you have to do is move the ToString call to a separate line:
var keyString = item.Key.ToString();
var pages = from p in context.entities
where p.Serial == keyString
select p;
Cast table to Enumerable, then you call LINQ methods with using ToString() method inside:
var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)
But be careful, when you calling AsEnumerable or ToList methods because you will request all data from all entity before this method. In my case above I read all table_name rows by one request.
Had a similar problem.
Solved it by calling ToList() on the entity collection and querying the list.
If the collection is small this is an option.
IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())
Hope this helps.
Upgrading to Entity Framework Version 6.2.0 worked for me.
I was previously on Version 6.0.0.
Hope this helps,
Change it like this and it should work:
var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
where p.Serial == key
select p;
The reason why the exception is not thrown in the line the LINQ query is declared but in the line of the foreach is the deferred execution feature, i.e. the LINQ query is not executed until you try to access the result. And this happens in the foreach and not earlier.
If you really want to type ToString inside your query, you could write an expression tree visitor that rewrites the call to ToString with a call to the appropriate StringConvert function:
using System.Linq;
using System.Data.Entity.SqlServer;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using System;
namespace ToStringRewriting {
class ToStringRewriter : ExpressionVisitor {
static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
.Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));
protected override Expression VisitMethodCall(MethodCallExpression node) {
var method = node.Method;
if (method.Name=="ToString") {
if (node.Object.GetType() == typeof(string)) { return node.Object; }
node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
}
return base.VisitMethodCall(node);
}
}
class Person {
string Name { get; set; }
long SocialSecurityNumber { get; set; }
}
class Program {
void Main() {
Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
var rewriter = new ToStringRewriter();
var finalExpression = rewriter.Visit(expr);
var dcx = new MyDataContext();
var query = dcx.Persons.Where(finalExpression);
}
}
}
In MVC, assume you are searching record(s) based on your requirement or information.
It is working properly.
[HttpPost]
[ActionName("Index")]
public ActionResult SearchRecord(FormCollection formcollection)
{
EmployeeContext employeeContext = new EmployeeContext();
string searchby=formcollection["SearchBy"];
string value=formcollection["Value"];
if (formcollection["SearchBy"] == "Gender")
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
return View("Index", emplist);
}
else
{
List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
return View("Index", emplist);
}
}
I got the same error in this case:
var result = Db.SystemLog
.Where(log =>
eventTypeValues.Contains(log.EventType)
&& (
search.Contains(log.Id.ToString())
|| log.Message.Contains(search)
|| log.PayLoad.Contains(search)
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
)
)
.OrderByDescending(log => log.Id)
.Select(r => r);
After spending way too much time debugging, I figured out that error appeared in the logic expression.
The first line search.Contains(log.Id.ToString()) does work fine, but the last line that deals with a DateTime object made it fail miserably:
|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
Remove the problematic line and problem solved.
I do not fully understand why, but it seems as ToString() is a LINQ expression for strings, but not for Entities. LINQ for Entities deals with database queries like SQL, and SQL has no notion of ToString(). As such, we can not throw ToString() into a .Where() clause.
But how then does the first line work? Instead of ToString(), SQL have CAST and CONVERT, so my best guess so far is that linq for entities uses that in some simple cases. DateTime objects are not always found to be so simple...
My problem was that I had a 'text' data type for this column (due to a migration from sqlite).
Solution: just change the data type to 'nvarchar()' and regenerate the table.
Then Linq accepts the string comparison.
I am working on retiring Telerik Open Access and replacing it with Entity Framework 4.0. I came across same issue that telerik:GridBoundColumn filtering stopped working.
I find out that its not working only on System.String DataTypes. So I found this thread and solved it by just using .List() at the end of my Linq query as follows:
var x = (from y in db.Tables
orderby y.ColumnId descending
select new
{
y.FileName,
y.FileSource,
y.FileType,
FileDepartment = "Claims"
}).ToList();
Just turn the LINQ to Entity query into a LINQ to Objects query (e.g. call ToArray) anytime you need to use a method call in your LINQ query.