I have the following code:
var orders = context.Orders
.Include("Clients")
.GroupBy(i => i.Clients.ClientName)
.Select(i => i.OrderByDescending(it => it.OrderDate).FirstOrDefault());
I want to get only the last order made by each client. The basis for the code I got from here Remove duplicates in the list using linq, from Freddy's answer. (I'm including "Clients" because Orders has ClientId but not client name and the results are to be displayed in a grid including the client name which I'm getting from Clients).
This works properly.
MY QUESTION:
Is it possible to do this using an asp.net Entity Datasource control?
Is it possible to use FirstOrDefault in some way in the asp.net Entity Datasource control?
If you move the Include to the end of the query ...
var orders = context.Orders
.GroupBy(i => i.Clients.ClientName)
.Select(i => i.OrderByDescending(it => it.OrderDate).FirstOrDefault())
.Include("Clients");
... you'll get Orders with their clients included.
With the original Include the query shape changes after the Include was applied. This always makes Include ineffective. You can move it to the end of the query, because even after the grouping, the query still return Orders, so the Include is applicable.
Note however that this is a tremendous overkill. Entire Client records are queried from the database, entire Client objects are materialized and in the end you only display their names. It's much better to project the required data to a DTO that exactly contains the data you want to display. For example:
var orders = context.Orders
.GroupBy(i => i.Clients.ClientName)
.Select(i => i.OrderByDescending(it => it.OrderDate).FirstOrDefault())
.Select(o => new OrderDto
{
o.OrderNumber,
o. ... // More Order properties
Client = o.Clients.Name
});
The DTO should be a class containing these properties.
I don't know the Entity Datasource control. From what I see in the MSDN documentation it seems too restricted to even shape the query sufficiently to get the last orders of each client. And it expects an entity set, no DTOs.
Instead of calling OrderbyDescending try using the max operated as explained here
I found here that with the EntityDataSource you can use:
Select="top(1) it.[OrderDate]"
However if you want to Order by DESC the top will be executed before the Order by DESC.
If you want the Order by executed before the top, in other words to get the last Item, instead of top do this in Code behind:
protected void entityDataSource_Selecting(object sender,EntityDataSourceSelectingEventArgs e)
{
e.SelectArguments.MaximumRows = 1;
}
All of this I got from that link in the Qustion and Answer.
I found that I can use the EntityDataSource's QueryCreated event as demonstrated in Filter with EntityDatasource in ASP.NET webforms in the question and answers.
In the case of this question I wrote
protected void EntityDataSource1_QueryCreated(object sender, QueryCreatedEventArgs e)
{
var ordersQuery = e.Query.OfType<Orders>();
e.Query = ordersQuery.Include("Clients")
.GroupBy(i => i.Clients.ClientName)
.Select(i => i.OrderByDescending(it => it.OrderDate).FirstOrDefault());
}
Related
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);
I am building an application using asp.net MVC 5 and have a grid working with IPagedList.MVC version 4.5.0.0, AutoMapper and Entity Framework.
In the project I have a BusinessLayer which is what my Action talks to, as I don't want the Action method to talk to Entity Framework directly. So my BLL has the following method:
public IPagedList<ActiveContractViewModel> GetAllContracts(string regNumFilter, int page)
{
var lstcontractViewModel = new List<ActiveContractViewModel>();
using (ActiveContractRepository activeContractRepos = new ActiveContractRepository(new UnitOfWork()))
{
var activeContractList = activeContractRepos.All.OrderByDescending(x => x.Id).Include(c => c.Contractor);
if (regNumFilter.Trim().Length > 0)
{
activeContractList = activeContractRepos.All.Where(x => x.RegistrationNumber.Contains(regNumFilter)).OrderByDescending(x => x.Id).Include(c => c.Contractor);
}
foreach (var activeContract in activeContractList)
{
Mapper.CreateMap<DomainClasses.ActiveContract, ActiveContractViewModel>().ForMember(dest => dest.ContractorModel, opts => opts.MapFrom(src => new ContractorViewModel
{
Id = src.Contractor.Id,
Name = src.Contractor.Name,
ContactPerson = src.Contractor.ContactPerson,
Phone = src.Contractor.Phone,
Fax = src.Contractor.Fax,
Address = src.Contractor.Address,
VendorNumber = src.Contractor.VendorNumber,
FederalTaxId = src.Contractor.FederalTaxId
}
));
Mapper.AssertConfigurationIsValid();
lstcontractViewModel.Add(Mapper.Map<ActiveContractViewModel>(activeContract));
}
}
return lstcontractViewModel.ToPagedList(page, 20);
}
I'm mapping my ActiveContract class (from Entity Framework) to a model (ActiveContractVieWModel) it works fine, data is returned and paging works. But I noticed while debugging that the foreach loop would also go through all records, if I have 2500 records it loops through all building a large list, which is then use on the ToPageList method.
Is there a better way to get around this, so i can build my model and fill it with just the 20 records I need and have the IPagedList know the total size?
I ended up looking into IPageList.MVC some more and saw the author had posted about this:
https://github.com/troygoode/pagedlist#example-2-manual-paging
"In some cases you do not have access something capable of creating an IQueryable, such as when using .Net's built-in MembershipProvider's GetAllUsers method. This method offers paging, but not via IQueryable. Luckily PagedList still has your back (note the use of StaticPagedList):"
I switched to using the StaticPagedList and it works better now, just grabbing the number of records I want and paging works as well.
Thats because you are retrieving all items. In your LINQ queries, you are only filtering by regNumFilter
var activeContractList = activeContractRepos
.All
.OrderByDescending(x => x.Id)
.Include(c => c.Contractor);
activeContractList = activeContractRepos
.All
.Where(x => x.RegistrationNumber.Contains(regNumFilter))
.OrderByDescending(x => x.Id)
.Include(c => c.Contractor);
To retrieve a specific number of rows(In you case 20 items per page), use Skip() and Take() before you iterate the results.
Sample code:
var activeContractList = activeContractList
.Skip(20 * page)
.Take(20);
foreach (var activeContract in activeContractList)
{
....
}
I m not using automapper but valueinjecter.
What i do is "mapping" IList<TModel> to IList<TViewModel> then you wont have the code. it will be more clean.
PagedList is not aware of TEntity, it just does paging. Dont forget paging is lazy.
With Valueinjecter I have following code:
https://github.com/fatagun/NetCollab/blob/master/NetCollab.Web/Mappers/Mapper.cs
I think what you need is AutoMapper Queryable-Extensions.
When ever you use the IEnumerable<T> interface the underline Entity query is materialized.
So the query will be run against your whole table well before ToPagedList "takes" and "skips" some pages. Instead you will have to go with the IQueryable<T> variations.
When using an ORM such as NHibernate or Entity Framework with AutoMapper's standard Mapper.Map functions, you may notice that the ORM will query all the fields of all the objects within a graph when AutoMapper is attempting to map the results to a destination type
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'm trying to fetch first element from the collection like this
List<Entity> data = session.Query<Entity>()
.Fetch(x => x.Photos.First())
.ToList();
and I'm getting this error.
A fetch request must be a simple member access expression; '[100002]' is a SubQueryExpression instead. Parameter name: relatedObjectSelector.
Now I'm using .Fetch(x => x.Photos.First()) cause I know that first element will always be populated, and it is. I do need just first element from the collection to reduce loading time, so this is exact solution I need, but I'm getting this error.
Fetch will fetch the entire collection, you can't tell it to just fetch the first element using "fetch", however you can probably get the desired effect using projections, or something like:
var subQ = QueryOver.Of<Entity>()
.SelectList(x => x.SelectMin(y => y.Photos)); // sub query selecting min photo id from entity
List<Photo> data = session.QueryOver<Photo>()
.Fetch(x => x.Entity).Eager // eager load the entity
.WithSubquery.WhereProperty(x => x.Id).In(subQ) // restrict the photo to those that are the first ones for the entity
.List() // execute query from database
.ToList(); // convert to List
Although I'm not sure why you'd want a List instead of IList.. generally it's preferable to use the interface
List<Entity> data = session.Query<Entity>()
.Fetch(x=>x.Photos.FirstOrDefualt())//here You need get only first element
.ToList();
i have a table called orders and i have a column called Last Update (and an order object with a LastUpdate property). I want to construct a query using nhibernate to get the last 50 rows so i don't go to the database and get everything and then have to filter results in my application.
is this possible in nhibernate. I am trying to use the LINQ api
Here's the LINQ version of this query.
var orders = session.Query<Order>()
.OrderByDescending(x => x.LastUpdate)
.Take(50);
Here's the screen shot of the code sample...
Here's the screen shot from NHibernate Profiler...
If you are using a Criteria then use SetMaxResults(50) and do a descending sort on the date time.
You can use SetMaxResults(50), although depending on which 50 rows you want (latest? first? last?) you'll probably also need to do a SortBy expression as well.
var orders = session.Query<Linq>()
.OrderByDescending(x => x.LastUpdate)
.Take(50);
In general case suggesing LastUdate can be nullable using Linq2SQL you may write extension method to your IQueriable:
public static partial class FooTable
{
public static IQueryable<FooTable> LastUpdated(this IQueryable<FooTable> queryable, int count)
{
return queryable.Where(x => (x.LastUdate != null))
.OrderByDescending(x => x.LastUdate)
.Take(count);
}
}