I am trying to use LINQ methods to filte a list of client by cheking whether or not the client is in a table of my BDD
using(var db = new CrmXefi())
{
var activeAccount = db.AccountBase.Where(account => account.StateCode == 0);
List<Client> toImport = clients.Where(client => activeAccount
.Where(account => account.IsAccountSageNumberForAgency(client.CODECLIENT, new Guid("2CA81DBC-8261-EB11-B061-00155D53D276")))
.Any()).ToList();
}
LINQ cannot translate that expression. I've checked the type of my elements to make sure that none of the ones inside the queries are IEnumerable. Can't find the origin of the problem.
This expression works :
var test = activeAccount.Where(account => account.IsAccountSageNumberForAgency("", new Guid("2CA81DBC-8261-EB11-B061-00155D53D276")));
is it because I get client.CODECLIENT in a Where() that is inside an other Where() and client is the in the first predicate?
Your query cannot be executed on the database side, so you will have to do it client side, by loading all the objects in memory.
You can use ToList() to achieve this.
By example :
using(var db = new CrmXefi())
{
var activeAccount = db.AccountBase.Where(account => account.StateCode == 0);
List<Client> toImport = clients.ToList().Where(client => activeAccount
.Where(account => account.IsAccountSageNumberForAgency(client.CODECLIENT, new Guid("2CA81DBC-8261-EB11-B061-00155D53D276")))
.Any()).ToList();
}
"Linq" is confusing here. On one side you have the "traditional" Linq, that works on IEnumerable and does all the processing in memory. On the other side you have Frameworks like Entity Framework that uses a Linq-like interface to compose queries. Ef Core is actually based on "Linq2Sql". EF converts your Linq to SQL and sends that to the server (it works on the IQueryable type. But not everything can be converted to SQL. In your case you're calling a specific method of a class, which is specific to your C# code. The SQL server doesn't know anything about it. And EF cannot convert it an expression.
Unless you can make something that can run on the server, you will need to pull the data to the local memory. You can do that with AsEnumerable.
E.g.
List<Client> toImport = clients
.AsEnumerable()
.Where(client => activeAccount
.Where(account => account
.IsAccountSageNumberForAgency(
client.CODECLIENT,
new Guid("2CA81DBC-8261-EB11-B061-00155D53D276")))
.Any())
.ToList();
Note, this will pull all data from the server to the local memory, thus costs memory and performance
Related
How to have the select query projection to a lambda expression in Entity framework..
In repo class
database.Employees.Where(s => s.EmpID == curEmp.ID)
.Select(s => new EmployeeModel
{
Value = s.Name,
Text = s.ID
// I have 40 fields and 10 join table projections here so much code
});
Since I have many fields to project, so created a separate class with the lambda expressions for those long select projections.
public class ProjectionQueries
{
public static readony Fun<Employee, EmployeeModel> GetEmployeeAsModel = (emp)
=> new EmployeeModel
{
Value = s.Name,
Text = s.ID...
...
..
..
Address = new AddressModel{.....},
Country = new CountryModel{.....}
};
}
Then called the above lambda expression from the select clause of the repository method.
database.Employees.Where(s => s.EmpID == curEmp.ID)
.Select(x => ProjectionQueries.GetEmployeeAsModel(x));
This way it is well segregated the code. But when I do this I get an error saying
The client projection contains a reference to a constant expression of the System.Func<*,*>. This could potentially cause a memory leak; consider assigning this constant to a local variable and using the variable in the query instead.
Can't use this way to make my code less in the repository class. ?
You could separate this out as a static extension method.
public static class EmployeeExtensions{
public static IQueryable<EmployeeModel> AsEmployeeModels(this IQueryable<Employee> emps){
return emps.Select(emp=> new EmployeeModel
{
Value = emp.Name,
Text = emp.ID...
});
}
}
and use it as:
database.Employees.Where(s => s.EmpID == curEmp.ID).AsEmployeeModels();
The #gokul-panigrahi answer show a solution with extension method, that I advice to use because I found the syntax smoothest.
The error is :
The client projection contains a reference to a constant expression of the System.Func<,>. This could potentially cause a memory leak; consider assigning this constant to a local variable and using the variable in the query instead.
The error indicate EF Core can't translate System.Func<*,*> to SGBD language. EF Core client evaluation. A solution is to execute the untranslatable part in client :
database.Employees
// 1) Prepare Query
.Where(s => s.EmpID == curEmp.ID)
// 2) Execute query on server
.AsEnumerable()
// 3) Next is execute in client
.Select(x => ProjectionQueries.GetEmployeeAsModel(x));
See the ms-doc for more information about Client vs Server Evaluation
But client evaluation could result in poor performance. In your case, if the projection can limit the number of column read in database (and limit the data transmit on network).
A other solution, it is to modify the type of GetEmployeeAsModel :
public static readony Expression<Fun<Employee, EmployeeModel>> GetEmployeeAsModel = ...
Thus EF Core can try translate the expression to SGBD's language. Try because the expression can use some term untranslatable like Func<*,*>.
What is the difference between Func and Expression<Func>?
A Func is delegate (trivially a pointer to a zone code in memory). EF Core don't know how translate a delegate.
A Expression is a specific structure of instruction, that is possible to explore :
Expression<Func<int,int>> expression = (x) => x % 2;
// Explore the expression
var operation = expression.Body as BinaryExpression;
var leftPart = operation.Left as ParameterExpression;
var rightPart = operation.Right as ConstantExpression;
// Translate
Console.WriteLine("#" + leftPart.Name);
// #x
Console.WriteLine(operation.NodeType);
// modulo
Console.WriteLine(rightPart.Value);
// 2
EF Core do this to translate the expression to SGBD's language, but sometime the expression contains a term unknown by EF Core. Then this produce a error like above.
I am having difficulty understanding how Entity Framework generates a single SQL Query from a couple of Linq methods. Here is a code which selects Username from a table.
var result1 = context.UserMasters
.Where(t => t.UserRole == "LOCAL")
.Skip(100)
.Take(100)
.Select(t => new { t.UserName });
Typically the above code is identical to the below code.
var test = context.UserMasters.Where(t=> t.UserRole == "LOCAL");
test = test.Skip(100);
test = test.Take(100);
var result2 = test.Select(t => new { t.UserName });
I hope we can get results from the table after any Linq Method. So there is a list readily available always. Is it selecting all results from the table initially and doing operation on the list ?
If the type is IQueryable, then the filtering of data happens at the database side. But if the type of query is IEnumerable or other than IQueryable, all the data from the table is fetched and filtering is done at .Net side. It is always advised to use IQueryable in these cases. Please read about IQueryable and IEnumerable difference.
I have lots of queries like sample1,sample2 and sample3. There are more than 13 million records in mongodb collection. So this query getting long time. Is there any way to faster this query?
I think using IMongoQuery object to resolve this problem. Is there any better way?
Sample 1:
var collection = new MongoDbRepo().DbCollection<Model>("tblmodel");
decimal total1 = collection.FindAll()
.SelectMany(x => x.MMB.MVD)
.Where(x => x.M01.ToLower() == "try")
.Sum(x => x.M06);
Sample 2:
var collection = new MongoDbRepo().DbCollection<Model>("tblmodel");
decimal total2 = collection.FindAll().Sum(x => x.MMB.MVO.O01);
Sample 3:
var list1= collection.FindAll()
.SelectMany(x => x.MHB.VLH)
.Where(x => x.V15 > 1).ToList();
var list2= list1.GroupBy(x => new { x.H03, x.H09 })
.Select(lg =>
new
{
Prop1= lg.Key.H03,
Prop2= lg.Count(),
Prop3= lg.Sum(w => w.H09),
});
The function FindAll returns a MongoCursor. When you add LINQ extension methods on to the FindAll, all of the processing happens on the client, not the Database server. Every document is returned to the client. Ideally, you'll need to pass in a query to limit the results by using Find.
Or, you could use the AsQueryable function to better utilize LINQ expressions and the extension methods:
var results = collection.AsQueryable().Where(....);
I don't understand your data model, so I can't offer any specific suggestions as to how to add a query that would filter more of the data on the server.
You can use the SetFields chainable method after FindAll to limit the fields that are returned if you really do need to return every document to the client for processing.
You also might find that writing some of the queries using the MongoDB aggregation framework might produce similar results, without sending any data to the client (except the results). Or, possibly a Map-Reduce depending on the nature of the data.
I am a beginner in EF and LINQ and I would like to retrieve a list of categories (with filter) by product id.
So, I have many-to-many relation between Product * <---> * Category and I use the following code:
var categList = dbContext.Products
.Where(prod => prod.PROD_UID == 1234)
.SelectMany(prod => prod.Categories)
.Distinct();
categList = SearchCategories(filter, categList);
categList = SortCategories(filter, categList);
categList = PageCategories(filter, categList);
where SearchCategories is used to reuse some code and looks like below
IQueryable<Category> SearchCategories(MyFilter filter, IQueryable<Category> source = null)
{
source = source ?? this.dbContext.Categories;
return source.Where(cat => [...filtering...] );
}
Although this looks ok, I would like to have it slightly optimized, with filtering inside the SelectMany (make use of SearchCategories inside SelectMany)... but I cannot make it work. I tried this, but gives me error
var categList = dbContext.Products
.Where(prod => prod.PROD_UID == 1234)
.SelectMany(cat => SearchCategories(filter, prod.Categories.AsQueryable()).AsEnumerable());
// throws LINQ to Entities does not recognize the method 'SearchCategories'
How could I filter the categories inside SelectMany?
Thank you!
Your problem is you are confusing the server query with the client query there is no magic here.
your first query until Distinct is serialized and sent to the server, then the server sends a response and you then run a filter in your client.
when you put the SearchCategories in the server query it cant be resolver so you get the error.
you have two options here:
1:Just write all your query from SearchCategories in the first query making it run in the server
.SelectMany(prod => prod.Categories.Where(c => [...filtering...]))
remember the filtering cant call client code.
2:You put a ToList or ToArray and then use the SearchCategories but this option wont optimize anything.
I am using LINQ to create a list. But I want to use a function at the end to generate the object iself, something LINQ complains about
LINQ to Entities does not recognize the method 'WashroomStatusItem GetWashroomStatusForItem(WashroomStatus)' method, and this method cannot be translated into a store expression.
What am I doing wrong?
var query = (from c in context.WashroomStatus
where c.WashroomId == GroupItem.WashroomID
select GetWashroomStatusForItem(c));
private WashroomStatusItem GetWashroomStatusForItem(WashroomStatus item)
{
WashroomStatusItem temp = new WashroomMonitorWCF.WashroomStatusItem();
//do stuff with it
return temp;
}
The problem is that the SQL conversion can't convert your method into SQL. You should use AsEnumerable() to "switch" from the out-of-process provider to LINQ to Objects. For example:
var query = context.WashroomStatus
.Where(c => c.WashroomId == GroupItem.WashroomID)
.AsEnumerable()
.Select(c => GetWashroomStatusForItem(c));
Note that if GetWashroomStatusForItem only uses some properties, you may want to project to those separately first, to reduce the amount of information fetched from the server:
var query = context.WashroomStatus
.Where(c => c.WashroomId == GroupItem.WashroomID)
.Select(c => new { c.Location, c.Date };
.AsEnumerable()
.Select(p => GetWashroomStatusForItem(p.Location, p.Date));
Jon Skeet's answer is correct, but I'd add that depending on the nature of GetWashroomStatusForItem(), it should probably either be broken down into LINQ statements and added into the query itself, or it should be executed after the query has returned.
So, lets say GetWashroomStatusForItem() looks something like this: note that this is extremely oversimplified.
public static WashroomStatus GetWashroomStatusForItem(Item c)
{
return c.WashroomStatus;
}
it should just be added to the LINQ query like this:
var query = (from c in context.WashroomStatus
where c.WashroomId == GroupItem.WashroomID
select c.WashroomStatus);
But if it relies heavily on stuff not in the db, I'd just end the Linq statement before you get the WashroomStatus, and then call GetWashroomStatusForItem() on the results. It's not gonna a performance difference since Linq uses lazy evaluation, and you generally want to keep db operations separate from "programmatic" ones.