I want to select a distinct list.
The following code is not working:
public IQueryable<BusinessObjects.Order> GetByBusinessId(Guid Id)
{
rentalEntities db = DataContextFactory.CreateContext();
List<Rental.BusinessObjects.Order> transformedList = new List<BusinessObjects.Order>();
foreach (Rental.DataObjects.EntityModel.Order item in db.Orders.Where(x => x.BusinessID == BusinessId).ToList())
{
transformedList.Add(OrderMappers.ToBusinessObject(item));
}
return( transformedList.AsQueryable()).Distinct();
}
Try this:
return Rental.DataObjects.EntityModel.Order item in db.Orders
.Where(x => x.BusinessID == BusinessId)
.Distinct()
.Select(item => OrderMappers.ToBusinessObject(item));
This should move the distinct operation to the underlying database call as it's applied before the query is materialized - this is more efficient as the duplicate rows aren't retrieved from the database server. If for some reason you don't want to do that, then check your equals implementation (as mentioned by Sorin)
You may want to check how your business objects implement Equals(), my guess is they are are different even if they have (let's say) the same ID.
You might like to try the DistinctBy() extension method from the MoreLinq library. This lets you easily control the exact semantics of how two objects are compared for distinctness. For instance:
return transformedList.AsQueryable().DistinctBy(orderBO => orderBO.OrderId);
http://morelinq.googlecode.com/files/morelinq-1.0-beta.zip
Related
epublic ActionResult ExistingPolicies()
{
if (Session["UserId"]==null)
{
return RedirectToAction("Login");
}
using(PMSDBContext dbo=new PMSDBContext())
{
List<Policy> viewpolicy = new List<Policy>();
var userid = Session["UserId"];
List<AddPolicy> policy= dbo.AddPolicies.Where(c => c.MobileNumber ==
(string)userid).ToList();
foreach(AddPolicy p in policy)
{
viewpolicy=dbo.Policies.Where(c => c.PolicyId ==p.PolicyId).ToList();
}
Session["Count"] = policy.Count;
return View(viewpolicy);
}
}
Here the policy list clearly has 2 items.But when I iterate through foreach,the viewpolicy list only takes the last item as its value.If break is used,it takes only the first item.How to store both items in viewpolicy list??
Regards
Surya.
You can iterate through policies and add them by one to list with Add, but I would say that often (not always, though) better option would be to just retrieve the whole list from DB in one query. Without knowing your entities you can do at least something like that:
List<AddPolicy> policy = ...
viewpolicy = dbo.Policies
.Where(c => policy.Select(p => p.PolicyId).Contains(c.PolicyId))
.ToList();
But if you have correctly set up entities relations, you should be able to do something like this:
var viewpolicy = dbo.AddPolicies
.Where(c => c.MobileNumber == (string)userid)
.Select(p => p.Policy) //guessing name here, also can be .SelectMany(p => p.Policy)
.ToList();
Of course; instead of adding to the list, you replace it with a whole new one on each pass of the loop:
viewpolicy=dbo.Policies.Where(c => c.PolicyId ==p.PolicyId).ToList()
This code above will search all the policies for the policy with that ID, turn it into a new List and assign to the viewpolicy variable. You never actually add anything to a list with this way, you just make new lists all the time and overwrite the old one with the latest list
Perhaps you need something like this:
viewpolicy.Add(dbo.Policies.Single(c => c.PolicyId ==p.PolicyId));
This has a list, finds one policy by its ID number (for which there should be only one policy, right? It's an ID so I figured it's unique..) and adds it to the list
You could use a Where and skip the loop entirely if you wanted:
viewpolicy=dbo.Policies.Where(c => policy.Any(p => c.PolicyId == p.PolicyId)).ToList();
Do not do this in a loop, it doesn't need it. It works by asking LINQ to do the looping for you. It should be converted to an IN query and run by the DB, so generally more performant than dragging the policies out one by one (via id). If the ORM didn't understand how to make it into SQL you can simplify things for it by extracting the ids to an int collection:
viewpolicy=dbo.Policies.Where(c => policy.Select(p => p.PolicyId).Any(id => c.PolicyId == id)).ToList();
Final point, I recommend you name your "collections of things" with a plural. You have a List<Policy> viewpolicy - this is a list that contains multiple policies so really we should call it viewPolicies. Same for the list of AddPolicy. It makes code read more nicely if things that are collections/lists/arrays are named in the plural
Something like:
viewpolicy.AddRange(dbo.Policies.Where(c => c.PolicyId ==p.PolicyId));
I'm having trouble understanding .Select and .Where statements. What I want to do is select a specific column with "where" criteria based on another column.
For example, what I have is this:
var engineers = db.engineers;
var managers = db.ManagersToEngineers;
List<ManagerToEngineer> matchedManager = null;
Engineer matchedEngineer = null;
if (this.User.Identity.IsAuthenticated)
{
var userEmail = this.User.Identity.Name;
matchedEngineer = engineers.Where(x => x.email == userEmail).FirstOrDefault();
matchedManager = managers.Select(x => x.ManagerId).Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
}
if (matchedEngineer != null)
{
ViewBag.EngineerId = new SelectList(new List<Engineer> { matchedEngineer }, "PersonId", "FullName");
ViewBag.ManagerId = new SelectList(matchedManager, "PersonId", "FullName");
}
What I'm trying to do above is select from a table that matches Managers to Engineers and select a list of managers based on the engineer's id. This isn't working and when I go like:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
I don't get any errors but I'm not selecting the right column. In fact the moment I'm not sure what I'm selecting. Plus I get the error:
Non-static method requires a target.
if you want to to select the manager, then you need to use FirstOrDefault() as you used one line above, but if it is expected to have multiple managers returned, then you will need List<Manager>, try like:
Update:
so matchedManager is already List<T>, in the case it should be like:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
when you put Select(x=>x.ManagerId) after the Where() now it will return Collection of int not Collection of that type, and as Where() is self descriptive, it filters the collection as in sql, and Select() projects the collection on the column you specify:
List<int> managerIds = managers.Where(x => x.EngineerId == matchedEngineer.PersonId)
.Select(x=>x.ManagerId).ToList();
The easiest way to remember what the methods do is to remember that this is being translated to SQL.
A .Where() method will filter the rows returned.
A .Select() method will filter the columns returned.
However, there are a few ways to do that with the way you should have your objects set up.
First, you could get the Engineer, and access its Managers:
var engineer = context.Engineers.Find(engineerId);
return engineer.Managers;
However, that will first pull the Engineer out of the database, and then go back for all of the Managers. The other way would be to go directly through the Managers.
return context.Managers.Where(manager => manager.EngineerId == engineerId).ToList();
Although, by the look of the code in your question, you may have a cross-reference table (many to many relationship) between Managers and Engineers. In that case, my second example probably wouldn't work. In that case, I would use the first example.
You want to filter data by matching person Id and then selecting manager Id, you need to do following:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).Select(x => x.ManagerId).ToList();
In your case, you are selecting the ManagerId first and so you have list of ints, instead of managers from which you can filter data
Update:
You also need to check matchedEngineer is not null before retrieving the associated manager. This might be cause of your error
You use "Select" lambda expression to get the field you want, you use "where" to filter results
I have a List of Document objects. The Document class has many properties but only two are relevant here, DocumentLinkId and UploadedOnDate.
What I want to do is filter the list down so there are no two Document objects with the same DocumentLinkId. When there is more than one Document object with a particular DocumentLinkId I want to keep the one with the latest UploadedOnDate.
My initial inclination was to do something like this:
myDocumentsList.Distinct(d => d.DocumentLinkId).Max(d => d.UploadedOnDate);
But Distinct() doesn't take a predicate. Is there a way to do this with LINQ?
You can group the documents by DocumentLinkId, and for each group, select the item with the latest UploadedOnDate like this:
var result = myDocumentsList
.GroupBy(d => d.DocumentLinkId)
.Select(g => g.OrderByDescending(d => d.UploadedOnDate).First())
.ToList();
You can define an implementation of IEqualityComparer<Document>. It exists for pretty much exactly this scenario.
public class DocumentLinkIdDocumentEqualityComparer : IEqualityComparer<Document>
{
public bool Equals(Document document1, Document document2)
{
return document1.DocumentLinkId == document2.DocumentLinkId;
}
}
Then you can do this:
myDocumentsList.OrderByDescending(d => d.UploadedOnDate)
.Distinct(new DocumentLinkIdDocumentEqualityComparer())
(Had to edit this to order it first so that distinct returns the one with the most recent date.)
You're saying that for the purpose of this one Distinct comparison, let's use this comparer and act as if any two documents with the same DocumentLinkId are equal.
What's nice about that is that you don't have to modify Document to override Equals, especially since this particular equality comparison might not apply in every case. This lets you specify when you want to use a particular custom equality comparison.
You can use DistinctBy like in this question.
var query = people.DistinctBy(p => p.Id);
It will be something like:
myDocumentsList.OrderByDescending(x => x.UploadedOnDate).ToList().DistinctBy(d => d.DocumentLinkId).Max(d => d.UploadedOnDate);
for your case.
I have a situation where i display a list of products for a customer. So, there are two kinds of products. So, if customer is registerd to two products, then both the products get displayed. So, I need to display distinct rows. I did this:
var queryProducts = DbContext.CustomerProducts.Where(p => p.Customers_Id ==
customerID).ToList().Select(r => new
{
r.Id,
r.Products_Id,
ProductName = r.Product.Name,
ShortName = r.Product.ShortName,
Description = r.Product.Description,
IsActive = r.Product.IsActive
}).Distinct();
In this, customerID is the value that i get from dropdownlist. However, it still displays the same row twice. So, can you please let me know how i can display only distinct records.
The most likely reasons could be that Distinct when called with no parameter by default compares all the public properties for equality. I suspect your Id is going to be unique. Hence the Distinct is not working for you.
You can try something like
myCustomerList.GroupBy(product => product.Products_Id).Select(grp => grp.First());
I found this as answers to
How to get distinct instance from a list by Lambda or LINQ
Distinct() with lambda?
Have a look at LINQ Select Distinct with Anonymous Types
I'm guessing r.ID is varying between the two products that are the same, but you have the same Products_Id?
You can write an implementation of IEqualityComparer<CustomerProduct>. Once you've got that, then you can use this:
DbContext.CustomerProducts.Where(p => p.Customers_Id == customerId)
.ToList()
.Distinct(new MyComparer())
.Select(r => new {
// etc.
public class MyComparer : IEqualityComparer<CustomerProduct>
{
// implement **Equals** and **GetHashCode** here
}
Note, using this anonymous comparer might work better for you, but it compares all properties in the anonymous type, not just the customer ID as specified in the question.
Consider this code:
var query = from groupRole in CurrentItem.MEMGroupRoles
select groupRole.MEMRole;
this.AvailableRoles = this.allRoles.Except(query.AsEnumerable()).ToList();
In this code I take allRoles except those roles that CurrentItem already have. 2 issues:
It doesn't work because I compare on objects and those objects are different instances
I don't like 2 lines and like to improve.
Here is pseudo-code on what I really need to do right now:
var queryIds = from groupRole in CurrentItem.MEMGroupRoles
select groupRole.MEMRole.RoleId;
this.AvailableRoles = this.allRoles.Except(where RoleId query.AsEnumerable()).ToList();
How do I write query like this?
EDIT:
explanation:
allRoles contains list of MEMRole objects
CurrentItem.MEMGroupRoles contains list of MEMGroupRole objects and each MEMGroupRole contains MEMRole inside
I want to SELECT all MEMRole objects that's inside allRoles EXCEPT those MEMRoles that burries inside CurrentItem. First code snippet would work, but I need to compare MEMRole to MEMRole by MEMRole.RoleId since it's a different instances of the same database entity.
You could override Equals() and GetHashCode() if the role object is such that it would make sense to identify it with role id. If that is not the case, you could create a role comparer class that implements IEqualityComparer<>. Except() takes equality comparer as second parameter.
Here is a solution that creates a lookup for role ids and uses it to filter the roles. However, I do think that the alternatives above are better solutions for your problem.
var lookup = CurrentItem.MEMGroupRoles
.ToLookup(groupRole => groupRole.MEMRole.RoleId);
this.AvailableRoles = this.allRoles
.Where(role => !lookup.Contains(role.RoleId))
.ToList();
Following the approach you suggested:
var ids = CurrentItem.MEMGroupRoles.Select(g => g.MMERole.RoleId);
this.AvailableRoles = this.allRoles.Where(r => ids.All(i => i != r.RoleId));
Alternatively (althought I wouldn't go that road), if you must have single query, you can append both roles collections (current and all), group them by RoleId and pick groups that only have single member:
this.AvailableRoles = CurrentItem.MEMGroupRoles
.Select(g => g.MEMRole)
.Concat(this.allRoles)
.GroupBy(r => r.RoleId)
.Where(g => g.Count() == 1)
.Select(g => g.First());
This results in roles that weren't in CurrentItem.MEMGroupRoles collection. But once again, it's just ... for sport :)
Is this LINQ to SQL?
If so, use DataContext.Log property to see the actual SQL that is being passed to the database, which may help you diagnose the problem.