I would like to append a set of conditional where clauses onto the end of an ObjectSet. However, the clauses do not get executed and instead the original query is run, for example:
using (Entities context = new Entities()){
var q = context.AuditLogs;
q.Where(o => o.WebsiteId == 1);
}
The where clause is not executed and the full result set is returned
I could instead use IQueryAble as in:
var q = context.AuditLogs.AsQueryable();
q = q.Where(o => o.WebsiteId == 1);
However this loses me the power of being able to use .Include to eager load my related entities.
No, it won't. at any point before executing the query, you would still be able to cast it back to ObjectQuery<T> and invoke methods like Include on it:
var query = context.AuditLogs.AsQueryable();
query = query.Where(o => o.WebsiteId == 1);
var auditLog = ((ObjectQuery<AuditLog>)query).Include("yourNavPropertyName")
.ToList();
If your intention is to build up a criteria incrementally, then the other option would be to leverage EntitySQL with QueryBuilder methods:
var query = context.AuditLogs.Where("it.WebsiteId = 1");
query = query.Where("...");
var auditLog = query.Include("yourNavPropertyName")
.ToList();
Just some good old fashioned linq would suffice here. Assuming you had a property named SiteOwner you could accomplish what your trying to do with the below query
using (Entities context = new Entities()){
var webSites = from sites in context.AuditLogs.Include("SiteOwner")
where sites.WebSiteId == 1
select sites;
}
Related
I am trying to get data from linq in asp.net core. I have a table with a Position with a FacultyID field, how do I get it from the Position table with an existing userid. My query
var claimsIdentity = _httpContextAccessor.HttpContext.User.Identity as ClaimsIdentity;
var userId = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier)?.Value.ToString();
var data = _context.Positions.Where(p => p.UserID.ToString() == userId).Select(x => x.FacultyID).???;
What can I add after the mark? to get the data. Thank you so much
There are several things you can do. An example in your case would be:
var data = _context.Positions.Where(p => p.UserID.ToString() == userId).Select(x => x.FacultyID).FirstOrDefault();
If you expect more than 1 results, then you would do:
var data = _context.Positions.Where(p => p.UserID.ToString() == userId).Select(x => x.FacultyID).ToList();
You have to be aware of the difference between a query and the result of a query.
The query does not represent the data itself, it represents the potential to fetch some data.
If you look closely to the LINQ methods, you will find there are two groups: the LINQ methods that return IQueryable<...> and the others.
The IQueryable methods don't execute the query. These functions are called lazy, they use deferred execution. You can find these terms in the remarks section of every LINQ method.
As long as you concatenate IQueryable LINQ methods, the query is not executed. It is not costly to concatenate LINQ methods in separate statements.
The query is executed as soon as you start enumerating the query. At its lowest level this is done using GetEnumerator and MoveNext / Current:
IQueryable<Customer> customers = ...; // Query not executed yet!
// execute the query and process the fetched data
using (IEnumerator<Customer> enumerator = customers.GetEnumerator())
{
while(enumerator.MoveNext())
{
// there is a Customer, it is in property Current:
Customer customer = enumerator.Current;
this.ProcessFetchedCustomer(customer);
}
}
This code, or something very similar is done when you use foreach, or one of the LINQ methods that don't return IQueryable<...>, like ToList, ToDictionary, FirstOrDefault, Sum, Any, ...
var data = dbContext.Positions
.Where(p => p.UserID.ToString() == userId)
.Select(x => x.FacultyID);
If you use your debugger, you will see that data is an IQueryable<Position>. You'll have to use one of the other LINQ methods to execute the query.
To get all Positions in the query:
List<Position> fetchedPositions result = data.ToList();
If you expect only one position:
Position fetchedPosition = data.FirstOrDefault();
If you want to know if there is any position at all:
if (positionAvailable = data.Any())
{
...
}
Be aware: if you use the IQueryable, the data will be fetched again from the DbContext. So if you want to do all three statements efficiently these, make sure you don't use the original data three times:
List<Position> fetchedPositions result = data.ToList();
Position firstPosition = fetchedPostion.FirstOrDefault();
if (firstPosition != null)
{
ProcessPosition(firstPosition);
}
I got the following sql statement that I want to implement with entity framework with linq (lambda expression). Here is the SQL:
select *
from tbl_ExampleStoneCatalog
join tbl_ExampleStoneCategory
on tbl_ExampleStoneCatalog.fk_ESC = tbl_ExampleStoneCategory.pk_ESC
join tbl_ExampleStones
on tbl_ExampleStoneCatalog.fk_ES = tbl_ExampleStones.pk_ES
join tbl_ExampleReviewStoneCatalog
on tbl_ExampleStones.pk_ES = tbl_ExampleReviewStoneCatalog.fk_ES
where .fk_StoneCategory = '%someParameter%'
I tried to use the .include() which brings me to this:
var res = (await this._exampleStoneCatalog.Query()
.include(esc => esc.ExampleStoneCategory)
.include(es => es.ExampleStones)
.include(es => es.ExampleStones.ExampleReviewStoneCatalog))
.Where(w => w.ExampleStones.ExampleReviewStoneCatalog.Any(
a => a.StoneCategoryID.Equals(%someParameter%)));
Unfortunately the code stated above won't deliver me the desired result. Furthermore there is a nested Where condition in it => ExampleStones.ExampleReviewStoneCatalog.StoneCategoryID. From what I understand after some research is, that this is not solvable easily with .include().
Is there other ways to filter in nested queries using the lambda expression?
If seems like a many-to-many relationship. I always find it easiest to begin with the connecting table here.
var res = _tbl_B.Repository.Where(b => b.c.Value == "whatever" && b.a.Value == "whatever").Select(b => b.a);
I have found a work around for this problem. The main challenge here is to filter in a nested SQL query. I could not find a solution with .include(). Especially my current work environment in which we are useing repository pattern wouldn't allow me to filter within includes like:
var res = await this._exampleStoneCatalog.Query().include(x => x.ExampleStones.ExampleReviewStoneCatalog.Where(w => w.StoneCategoryID.Equals(%SomeParameter%))).SelectAsync();
Hence I come to the following solution with using linq to sql.
My solution:
var exampleStoneCatalogEnum = await this._exampleStoneCatalog.Query().SelectAsync();
var exampleStoneCategoryEnum = await this._exampleStoneCategoryRepository.Query().SelectAsync();
var exampleStonesEnum = await this.exampleStonesRepository.Query().SelectAsync();
var exampleReviewStoneCatalogEnum = await this.exampleReviewStoneCatalogRepository.Query().SelectAsync();
var result = from exampleStoneCatalog in exampleStoneCatalogEnum
join exampleStoneCategory in exampleStoneCategoryEnum on exampleStoneCatalog.Id equals exampleStoneCategory.Id
join exampleStones in exampleStonesEnum on exampleStoneCatalog.Id equals exampleStones.Id
join exampleReviewStoneCatalog in exampleReviewStoneCatalogEnum on exampleStones.Id equals exampleReviewStoneCatalog.Id
where exampleReviewStoneCatalog.StoneCategoryID.Equals(revCategory)
select exampleStoneCatalog;
return result;
as you can see I first get the required data of each table and join them in my result including the where condition in the end. This returns the desired result.
I'm trying to build a query using Dynamic Linq and a where string statement calculated by myself. For example:
List<Publication> results = new List<Publication>();
// Just an example, previously calculated dynamically
string filterQuery = "(Id = 1 and Number = 2)";
IQueryable<Publication> query = db.Publications.Include(i => i.Product);
query = query.Where(filterQuery);
results = query.OrderBy(orderQuery).ToList();
This is working great and I get a List of Publications with Products. Now... the question is. How can I make a string statement to get results based on a relation to Product using Dynamic Linq and a string statement?
Something like:
string filterQuery = "(Id = 1 and Number = 2 and Products.Id = 1)"
After a lot of research and trying things, this is an easy and friendly way using the same Dynamic Linq library:
List<Publication> results = new List<Publication>();
// Just an example, previously calculated dynamically
string filterQuery = "(Id = 1 and Number = 2)";
string filterQueryChildren = "Products.Any(Id == 1)"
IQueryable<Publication> query = db.Publications.Include(i => i.Product).Where(filterQueryChildren);
query = query.Where(filterQuery);
results = query.OrderBy(orderQuery).ToList();
I am not sure why you're doing this, but the following should work:
List<Publication> results = new List<Publication>();
// Just an example, previously calculated dynamically
string filterQuery1 = "(Id = 1)"
string filterQuery2 = "(Id = 1 and Number = 2)";
IQueryable<Publication> query = db.Publications.
Where(filterQuery1).
Include(i => i.Product);
query = query.Where(filterQuery2);
results = query.OrderBy(orderQuery).ToList();
To make dynamic LINQ query, you need a library that support it or doing it yourself with Expression Tree
See: Entity Framework Dynamic Query Library
Disclaimer: I'm the owner of the project Eval-Expression.NET
This library allows you to evaluate, compile, and execute code at runtime.
The library also contains extension method for dynamic LINQ
Wiki: Eval Dynamic LINQ
Example
// using Z.Expressions; // Don't forget to include this.
// The filterQuery must use the C# syntax
string filterQuery = "x.Id == 1 && x.Number = 2";
IQueryable<Publication> query = db.Publications.Include(i => i.Product);
query = query.Where(x => filterQuery);
EDIT: Answer sub question
Great but how can I filter by Product?
Entity Framework doesn't support filter in Include method.
However, EF+ do
Disclaimer: I'm the owner of Entity Framework Plus
See: EF+ Query IncludeFilter
By combining both libraries, you can achieve your desired result:
// Extension method must be registered, otherwise the library cannot be aware of which extension method exists!
EvalManager.DefaultContext.RegisterExtensionMethod(typeof (QueryIncludeFilterExtensions));
string where1 = "x.Id == 1 && x.Number == 2";
string where2 = "y.Id == 3";
var left2 = ctx.Publications
.Where(x => where1)
.Execute<IQueryable<Publication>>("IncludeFilter(x => x.Product.Where(y => " + where2 + "))")
.ToList();
If you want to try this solution, make sure you download the latest version of Eval-Expression.NET. We just have fixed few min ago an issue we found by trying your scenario.
EDIT2: Answer sub question
Here is what I recommend you.
Try the query without our library then try it dynamically after.
That's easier to find the compilation error or what you want to do really.
By example, we probably didn't understand the initial problem correctly and suggested IncludeFilter which you don't want. I assumed you wanted to filter multiple products. However, it looks you only have one product per publication, so the Where clause method obviously doesn't exist.
Maybe this is more what you are looking for:
string where1 = "x.Id == 1 && x.Number == 2 && x.Product.Id == 3";
var left2 = ctx.Publications
.Include(x => x.Product)
.Where(where1)
.ToList();
So in short, try the query by hardcoding it (without dynamic) then you will know how to use it dynamically after.
Is it possible to sort in Entity Framework by sql function from database?
I mean something like:
var xx = DbContext.Set<Article>()
.Where(x=>x.Name.Contains("X")).OrderBy("[dbo].[MySQLFunction]");
var xx = DbContext.Set<Article>()
.Where(x=>x.Name.Contains("X"))
.Select(x=> new
{
Article = x,
orderItem = SqlFunction.//Any function you want to use.
// Or may you want to use DbFunctions
})
.OrderBy(x=>x.orderItem);
Here is more information about DbFunctions and SqlFunctions
Update:
By the way, if you mean to use your custom SQLFunction then I advise you to make a computed column in your table and make this column use this SQLFunction then you could OrderBy or filter against this field using normal Linq queries
The only option I found - is something like bad workaround, and I guess this is limited to simple queries.
var objectContext = ((IObjectContextAdapter)DbContext).ObjectContext;
var query = (System.Data.Objects.ObjectQuery)objectContext.CreateObjectSet<User>().Where(u => u.Id != Guid.Empty);
var initialQueryString = query.ToTraceString();
var resultQueryString = initialQueryString + " order by [dbo].[MySQLFunction]";
//careful here, if you use MS SQL you need to use SqlParameter instead of NpgsqlParameter
var paramValues = new List<NpgsqlParameter>();
foreach (var param in query.Parameters)
{
paramValues.Add(new NpgsqlParameter(param.Name, param.Value));
}
var result = objectContext.ExecuteStoreQuery<User>(resultQueryString, paramValues.Cast<object>().ToArray()).ToList();
Is there a way to get the whole count when using the Take operator?
You can do both.
IEnumerable<T> query = ...complicated query;
int c = query.Count();
query = query.Take(n);
Just execute the count before the take. this will cause the query to be executed twice, but i believe that that is unavoidable.
if this is in a Linq2SQL context, as your comment implies then this will in fact query the database twice. As far as lazy loading goes though it will depend on how the result of the query is actually used.
For example: if you have two tables say Product and ProductVersion where each Product has multiple ProductVersions associated via a foreign key.
if this is your query:
var query = db.Products.Where(p => complicated condition).OrderBy(p => p.Name).ThenBy(...).Select(p => p);
where you are just selecting Products but after executing the query:
var results = query.ToList();//forces query execution
results[0].ProductVersions;//<-- Lazy loading occurs
if you reference any foreign key or related object that was not part of the original query then it will be lazy loaded in. In your case, the count will not cause any lazy loading because it is simply returning an int. but depending on what you actually do with the result of the Take() you may or may not have Lazy loading occur. Sometimes it can be difficult to tell if you have LazyLoading ocurring, to check you should log your queries using the DataContext.Log property.
The easiest way would be to just do a Count of the query, and then do Take:
var q = ...;
var count = q.Count();
var result = q.Take(...);
It is possible to do this in a single Linq-to-SQL query (where only one SQL statement will be executed). The generated SQL does look unpleasant though, so your performance may vary.
If this is your query:
IQueryable<Person> yourQuery = People
.Where(x => /* complicated query .. */);
You can append the following to it:
var result = yourQuery
.GroupBy (x => true) // This will match all of the rows from your query ..
.Select (g => new {
// .. so 'g', the group, will then contain all of the rows from your query.
CountAll = g.Count(),
TakeFive = g.Take(5),
// We could also query for a max value.
MaxAgeFromAll = g.Max(x => x.PersonAge)
})
.FirstOrDefault();
Which will let you access your data like so:
// Check that result is not null before access.
// If there are no records to find, then 'result' will return null (because of the grouping)
if(result != null) {
var count = result.CountAll;
var firstFiveRows = result.TakeFive;
var maxPersonAge = result.MaxAgeFromAll;
}