Entity Framework SQL Query Generation - c#

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.

Related

"the linq expression [...] could not be translated"

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

LINQ include a subset of a query

I have an Entity in EF Core that is using the structure like:
Course has an entity CourseUserRoles which has the CourseId, UserId and RoleId.
CourseViewModel has the same structure as Course, except CourseUserRoles, instead it has two booleans IsAdministrator and IsContributor, that are related to the RoleId.
I am trying to make a query that won't bring all CourseUserRoles for every course queried, but only the ones specific for that user.
I saw that syntactically the below is correct:
query = query.Include(x => x.CourseUserRoles.Where(y => y.UserId == userId));
where the query is trying to return a list of courses, I just want to include the ones that have the same Id as the user.
The problem is that the above is throwing an exception.
Is it possible to only include the CourseUserRoles when the course has the UserId? If it doesn't have it would return null or empty list.
I typically do this by creating separate queries and concatenating as follows:
query = query.Where(x => x.CourseUserRoles.UserId != userId)
var queryWithInclude = query.Where(x => x.CourseUserRoles.UserId == userId)
.Include(x => x.CourseUserRoles)
var fullDataset = query.Concat(queryWithInclude);
This keeps both query objects as type IQueryable and not IEnumerable, allowing execution to happen in SQL/server-side rather than in memory.

NOT IN Condition in Linq

I have a simple scenario.I want to list out all the employees except the logged in user.
Similar SQL Condition is
select * from employee where id not in(_loggedUserId)
How can I acheive the above using LINQ.I have tried the following query but not getting the desired list
int _loggedUserId = Convert.ToInt32(Session["LoggedUserId"]);
List<int> _empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id)
.Except(_loggedUserId)
.ToList();
Except expects argument of type IEnumerable<T>, not T, so it should be something like
_empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id)
.Except(new[] {_loggedUserId})
.ToList();
Also note, this is really redundant in the case when exclusion list contains only one item and can be replaces with something like .Where(x => x != _loggedUserId)
Why not use a very simple Where condition?
_empIds = _cmn.GetEmployeeCenterWise(_loggedUserId).Where(e=>e.Id != _loggedUserId).ToList();
The title of your question is how to perform a not in query against a database using LINQ. However, as others have pointed out your specific problem is better solved by a using users.Where(user => user.Id != loggedInUserId).
But there is still an answer on how to perform a query against a database using LINQ that results in NOT IN SQL being generated:
var userIdsToFilter = new[] { ... };
var filteredUsers = users.Where(user => !userIdsToFilter.Contains(user.Id));
That should generate the desired SQL using either Entity Framework or LINQ to SQL.
Entity Framework also allows you to use Except but then you will have to project the sequence to ID's before filtering them and if you need to original rows you need to fetch them again from the filtered sequence of ID's. So my advice is use Where with a Contains in the predicate.
Use LINQ without filtering. This will make your query execute much faster:
List<int> _empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id).ToList();
Now use List.Remove() to remove the logged-in user.
_empIds.Remove(_loggedUserId);

How to do mongodb queries faster?

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.

EF: how to tackle many-to-many relation (some filtering involved)

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.

Categories

Resources