I have query like this for TheEntity type:
var source = await _repository.Queryable.AsNoTracking()
.Where(Condition1())
.Where(Condition2(params))
.GroupBy(GroupByFunction)
.Select(SelectFunction)
.OrderBy(o => o.Field1)
.ToAsyncEnumerable().ToList();
This query select all records which fulfill conditions: Condition1, Condition2, but does not group them as I expected.
SelectFunction and GroupByFunction looks like below:
private readonly Expression<Func<IGrouping<TheEntityGroupByData, TheEntity>, TheEntitySelectData>> SelectFunction =
e => new TheEntitySelectData()
{
Field1 = e.Key.Field1,
Field2 = e.Key.Field2,
...
FieldN = e.Key.FieldN,
Field(N+1) = e.Sum(x=>x.Field(N+1)),
...
FieldK = e.Sum(x=>x.FieldK),
};
private readonly Expression<Func<TheEntity, TheEntityGroupByData>> GroupByFunction =
e => new TheEntityByData()
{
Field1 = e.Field1,
Field2 = e.Field2,
...
FieldN = e.Key.FieldN,
};
TheEntityGroupByData, TheEntitySelectData are helper DTO/PO_Os types.
I intendent to fire grouping on database rather than server, but this behavior does not work even in server memory.
I use .Net Core 2.0.5 and EntityFrameworkCore 2.0.2.
My question is where is the problem in this approach?
edit - What I mean query not work as I expected is that: If I will have two the same records in db (same by grouping key) that fulfill Condition1 and Condition2, query will return 2 instead of one records.
Filtering conditions looks like below:
private static Expression<Func<TheEntity, bool>> Condition1()
{
return (e) => e.Field1 == SOME_CONSTANT;
}
private static Expression<Func<TheEntity, bool>> Condition2(RequestParam param)
{
Expression<Func<TheEntity, bool>> whereSth;
if (param.some.HasValue)
whereSth = (e) => e.Field2 <= param.some.Value;
else
whereSth = (e) => true;
return whereSth;
}
Related
I have RavenDB set up which is queried through a WebApi layer. The RavenDb layer returns an IQueriable onto which OData filters are applied in the WebApi layer. Every Employee object that is saved in the RavenDB has a Version property associated with it (value of DateTime.UtcNow.Ticks while saving the document). Recently I was working on a requirement where I can save same Employee multiple times over the period of time (as separate entities, differing in their property values but with same Id), but I only want to fetch the latest one based on its Version value.
In order to achieve this I used MapReduce as described below :
public class Employee_Version : AbstractIndexCreationTask<Employee>
{
public Employee_Version()
{
Map = employees => from employee in employees
select new Employee
{
FirstName = employee.FirstName,
LastName = employee.LastName,
Departments = employee.Departments,
Id = employee.Id,
Version = employee.Version,
ManagerId = employee.ManagerId,
EmployeeId=employee.EmployeeId
};
Reduce = results => from result in results
group result by result.ManagerId
into g
select new
{
ManagerId = g.OrderByDescending(d => d.Version).First().ManagerId,
Departments = g.OrderByDescending(d => d.Version).First().Departments,
FirstName = g.OrderByDescending(d => d.Version).First().FirstName,
LastName = g.OrderByDescending(d => d.Version).First().LastName,
Version = g.OrderByDescending(d => d.Version).First().Version,
Id = g.OrderByDescending(d => d.Version).First().Id,
EmployeeId = g.OrderByDescending(d => d.Version).First().EmployeeId
};
}
}
Raven Repository Code :
public IQueryable<Employee> GetEmployees(Expression<Func<Employee, bool>> expression)
{
using (var session = DocumentStore.OpenSession())
{
return session.Query<Employee, Employee_Version>().Statistics(out querysStatistics).Where(expression),
}
}
Web Api Layer Code :
Expression<Func<Employee, bool>> managerIdFilter = e => e.ManagerId == 123;
var employeeQueryable = _employeeRepository.GetEmployees(managerIdFilter);
var queryable = modelOptions.ApplyTo(employeeQueryable.Queryable, new ODataQuerySettings
{
EnableConstantParameterization = false,
HandleNullPropagation = HandleNullPropagationOption.False
});
When I query it like :
http://localhost/employee/list?$top=1
I get following exception :
Inner ExcpetionUrl: \"/databases/documents/indexes/Document/Version?&query=ManagerId%3A123&pageSize=1&sort=__document_id&SortHint-__document_id=String\"\ \ \ \ System.ArgumentException: The field '__document_id' is not indexed, cannot sort on fields that are not indexed
The same query works fine if no OData filter is used.
Given a list of objects as follows:
Instance
- InstanceID
Version
- VersionID
- List<Instance> Instances
Activity
- ActivityID
- List<Version> Versions
I want to produce a list like this:
Activity
- ActivityID
- List<Instance> Instances
currently stuck at:
(from activity in activities
select new {
activity.ActivityID,
VersionGroup = (from version in activity.Versions
group version by version.ActivityID into versionGroup
select versionGroup)
})
Just not sure how to get to the instance level.
(from activity in activities
select new { activity.ActivityID,
Instances = activity.ActivityVersions.SelectMany(v => v.ActivityInstances).AsEnumerable() });
You can use the SelectMany method to flatten a sublist:
var result = activities.Select(a => new
{
a.ActivityId,
Instances = a.Versions.SelectMany(v => v.Instances)
.GroupBy(i => i.InstanceID)
.Select(grp => grp.First())
.ToList()
});
You can relpace the GroupBy logic with a Distinct with a custom IEqualityComparer<Instance>:
var result = activities.Select(a => new
{
a.ActivityId,
Instances = a.Versions.SelectMany(v => v.Instances)
.Distinct(new InstanceComparer())
.ToList()
});
class InstanceComparer : IEqualityComparer<Instance>
{
public bool Equals(Instance x, Instance y)
{
return x.InstanceID == y.InstanceID;
}
public int GetHashCode(Instance obj)
{
return obj.InstanceID.GetHashCode();
}
}
I haven't done the null check but they are trivial. This is, of course, assuming this is LINQ to Object as there is no tags that says otherwise.
I have method which accept expression for Linq Where clause. Sometimes I would like to ignore Where clause and do not use it.
I have tried to pass null to the method like this
GetUsersView(null)
but got exception. How correctly do this?
private IQueryable<UserView> GetUsersView(Expression<Func<User, bool>> expression)
{
return _userRepository.GetAll().
Where(expression).
Select(p => new UserView
{
Id = p.Id,
Active = p.Orders.Any(c => c.Active && (c.TransactionType == TransactionType.Order || c.TransactionType == TransactionType.Subscription)),
DateStamp = p.DateStamp,
Email = p.Email,
FirstName = p.FirstName,
LastName = p.LastName,
Message = p.Message,
UsersManager = p.Orders.Select(o => o.Product).Any(w => w.UsersManager && w.Active)
});
}
Passing nulls to methods is a horrible idea. Passing u => true is not very readable either. Create two methods instead - one which has parameter, and other, which don't have. Also I see your method have two responsibilities - it filters users, and converts them to UserViews. I think filtering users by predicate should occur in repository.
You can also create extension method IQueryable<UserView> ToViews(this IQueryable<User> source).
public static IQueryable<UserView> ToViews(this IQueryable<User> source)
{
return source.Select(u => new UserView
{
Id = u.Id,
Active = u.Orders.Any(o => o.Active &&
(o.TransactionType == TransactionType.Order ||
o.TransactionType == TransactionType.Subscription)),
DateStamp = u.DateStamp,
Email = u.Email,
FirstName = u.FirstName,
LastName = u.LastName,
Message = u.Message,
UsersManager = u.Orders.Select(o => o.Product)
.Any(p => p.UsersManager && p.Active)
});
}
In this case code will look like:
private IQueryable<UserView> GetUserViews()
{
return _userRepository.GetAll().ToViews();
}
private IQueryable<UserView> GetUserViews(Expression<Func<User, bool>> predicate)
{
// move filtering to repository
return _userRepository.GetAll(predicate).ToViews();
}
Try using
GetUsersView(u=>true);
or if you would prefer not to type the expression all the time, you can create an overloaded function that provides a default expression.
IQueryable<UserView> GetUsersView()
{
return GetUsersView(u=>true);
}
I'm communicating with Windows Azure through a service I created (hosted locally). It currently retrieves data from my word database successfully but I'm having trouble filtering it.
This is the code I use to retrieve records:
private void GetWords()
{
DataServiceQuery<Word> query = (DataServiceQuery<Word>)(from g in dataEntity.Words select g);
query.BeginExecute(ar =>
{
DataLoad(ar);
}, query);
}
private void DataLoad(IAsyncResult result)
{
DataServiceQuery<Word> query = result.AsyncState as DataServiceQuery<Word>;
wordData = query.EndExecute(result).ToList();
Dispatcher.BeginInvoke(() =>
{
PopulateList();
});
}
I can successfully use:
DataServiceQuery<Word> query = (DataServiceQuery<Word>)(from g in dataEntity.Words where g.ID == 2 select g);
but I can't use:
DataServiceQuery<Word> query = (DataServiceQuery<Word>)(from g in dataEntity.Words where g.Name == "Plant" select g);
but I am unable to do string comparisons like 'g.Name == "Plant"'. So apparently it works with ints but not strings. The query runs but nothing is returned (it never actually finishes).
Any ideas what's going on?
I have two tables, movies and categories, and I want to get an ordered list by categoryID first and then by Name.
The movie table has three columns ID, Name and CategoryID.
The category table has two columns ID and Name.
I tried something like the following, but it didn't work.
var movies = _db.Movies.OrderBy( m => { m.CategoryID, m.Name })
This should work for you:
var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name)
Using non-lambda, query-syntax LINQ, you can do this:
var movies = from row in _db.Movies
orderby row.Category, row.Name
select row;
[EDIT to address comment] To control the sort order, use the keywords ascending (which is the default and therefore not particularly useful) or descending, like so:
var movies = from row in _db.Movies
orderby row.Category descending, row.Name
select row;
Add "new":
var movies = _db.Movies.OrderBy( m => new { m.CategoryID, m.Name })
That works on my box. It does return something that can be used to sort. It returns an object with two values.
Similar, but different to sorting by a combined column, as follows.
var movies = _db.Movies.OrderBy( m => (m.CategoryID.ToString() + m.Name))
Use the following line on your DataContext to log the SQL activity on the DataContext to the console - then you can see exactly what your LINQ statements are requesting from the database:
_db.Log = Console.Out
The following LINQ statements:
var movies = from row in _db.Movies
orderby row.CategoryID, row.Name
select row;
AND
var movies = _db.Movies.OrderBy(m => m.CategoryID).ThenBy(m => m.Name);
produce the following SQL:
SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].CategoryID, [t0].[Name]
Whereas, repeating an OrderBy in LINQ, appears to reverse the resulting SQL output:
var movies = from row in _db.Movies
orderby row.CategoryID
orderby row.Name
select row;
AND
var movies = _db.Movies.OrderBy(m => m.CategoryID).OrderBy(m => m.Name);
produce the following SQL (Name and CategoryId are switched):
SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].[Name], [t0].CategoryID
I have created some extension methods (below) so you don't have to worry if an IQueryable is already ordered or not. If you want to order by multiple properties just do it as follows:
// We do not have to care if the queryable is already sorted or not.
// The order of the Smart* calls defines the order priority
queryable.SmartOrderBy(i => i.Property1).SmartOrderByDescending(i => i.Property2);
This is especially helpful if you create the ordering dynamically, f.e. from a list of properties to sort.
public static class IQueryableExtension
{
public static bool IsOrdered<T>(this IQueryable<T> queryable) {
if(queryable == null) {
throw new ArgumentNullException("queryable");
}
return queryable.Expression.Type == typeof(IOrderedQueryable<T>);
}
public static IQueryable<T> SmartOrderBy<T, TKey>(this IQueryable<T> queryable, Expression<Func<T, TKey>> keySelector) {
if(queryable.IsOrdered()) {
var orderedQuery = queryable as IOrderedQueryable<T>;
return orderedQuery.ThenBy(keySelector);
} else {
return queryable.OrderBy(keySelector);
}
}
public static IQueryable<T> SmartOrderByDescending<T, TKey>(this IQueryable<T> queryable, Expression<Func<T, TKey>> keySelector) {
if(queryable.IsOrdered()) {
var orderedQuery = queryable as IOrderedQueryable<T>;
return orderedQuery.ThenByDescending(keySelector);
} else {
return queryable.OrderByDescending(keySelector);
}
}
}
There is at least one more way to do this using LINQ, although not the easiest.
You can do it by using the OrberBy() method that uses an IComparer. First you need to
implement an IComparer for the Movie class like this:
public class MovieComparer : IComparer<Movie>
{
public int Compare(Movie x, Movie y)
{
if (x.CategoryId == y.CategoryId)
{
return x.Name.CompareTo(y.Name);
}
else
{
return x.CategoryId.CompareTo(y.CategoryId);
}
}
}
Then you can order the movies with the following syntax:
var movies = _db.Movies.OrderBy(item => item, new MovieComparer());
If you need to switch the ordering to descending for one of the items just switch the x and y inside the Compare()
method of the MovieComparer accordingly.
If use generic repository
> lstModule = _ModuleRepository.GetAll().OrderBy(x => new { x.Level,
> x.Rank}).ToList();
else
> _db.Module.Where(x=> ......).OrderBy(x => new { x.Level, x.Rank}).ToList();