Hi I'm using linq to entity in my application. I need to get distinct records based on one column value "Name"
So I have a table similar like you can see below:
(User)
ID
Name
Country
DateCreated
I need to select all this items but uniques based on Name (unique). Is it possible to accomplish using linq, if so please show me how.
var items = (from i in user select new {i.id, i.name, i.country, i.datecreated}).Distinct();
The Distinct() method doesn't perform well because it doesn't send the DISTINCT SQL predicate to the database. Use group instead:
var distinctResult = from c in result
group c by c.Id into uniqueIds
select uniqueIds.FirstOrDefault();
LINQ's group actually creates subgroups of entities keyed by the property you indicate:
Smith
John
Mary
Ed
Jones
Jerry
Bob
Sally
The syntax above returns the keys, resulting in a distinct list. More information here:
http://imar.spaanjaars.com/546/using-grouping-instead-of-distinct-in-entity-framework-to-optimize-performance
The purely LINQ way that occurs is to group by name, select distinct groups by key, then select based on that.
from i in user
group new {i.ID, i.Country, i.DateRecord} by i.Name into byNmGp
select byNmGp.First();
Edit: Entity Framework is of course a very popular linq provider, but it doesn't handle First() well here, though the logically equivalent (in this case) FirstOrDefault() will work fine. I prefer First() when not forced into FirstOrDefault() by EF's limitations, because its meaning better matches what is sought here.
Another way is to define a helper class:
private class MyRecord : IEquatable<MyRecord>
{
public int ID;
public string Name;
public string Country;
public DateTime DateCreated;
public bool Equals(MyRecord other)
{
return Name.Equals(other.Name);
}
public override bool Equals(object obj)
{
return obj is MyRecord && Equals((MyRecord)obj);
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
/*...*/
var items = (from i in user select new MyRecord {i.ID, i.Name, i.Country, i.DateRecord}).Distinct();
This simply defines distinct differently. Performance will differ by whether the query provider can interpret that definition of equality or not. Convenience will differ based on whether you've similar LINQ queries doing much the same thing or not.
You can use something like this:
var distinctReports = reports.Select(c => c.CompanyCode)
.Distinct()
.Select(c => reports.FirstOrDefault(r => r.CompanyCode == c))
.ToList();
Here's another variation I ended up using which was based off the response from Svetlana. Shows an example of populating a GridView control with unique values. Thanks!
dataGridView_AnalyzeTestSuites.DataSource = (
from tr in _db.TestResults
where tr.TaskId == taskId
select new { TestSuiteName = tr.Test.TestSuite.Name }
).Distinct().ToList();
Hi here is how you can select distinct records with inner join. Hope it helps
var distinctrecords =
(entity.Table.Join(entity.Table2, x => x.Column, y => y.Column, (x, y) => new {x, y})
.Select(#t => new {#t.x.Column2, #t.y.Column3}))
.GroupBy(t => t.Column2)
.Select(g => g.FirstOrDefault());
Related
I have an issue of using group by in LINQ to SQL statement.
The cod I have is
var combinedItems = (from article in articles
join author in authors
on article.AuthorId equals author.Id into tempAuthors
from tempAuthor in tempAuthors.DefaultIfEmpty()
select new { article , author = tempAuthor});
var groups1 = (from combinedItem in combinedItems
group combinedItem by combinedItem.article into g
select g.Key).ToList();
var groups2 = (from combinedItem in combinedItems
group combinedItem by combinedItem.article.Id into g
select g.Key).ToList();
I tried to group in two different ways. The first way, I group by an object and the second way I just group by a field in one of the objects.
When I run groups1, I got an error saying need to evaluate in client side, while when I use groups2, it works all good. Can I ask what could be wrong? If I want to group by object, is there any way to do it?
In case you want to group by object, as you've not overridden Equals and GetHashCode() in your Article class or implemented IEqualityComparer<Article> you're just getting the default comparison, which checks if the references are equal. So what you need is something like this:
class GroupItemComparer : IEqualityComparer<Article>
{
public bool Equals(Article x, Article y)
{
return x.Id == y.Id &&
x.Name == y.Name;
}
public int GetHashCode(Article obj)
{
return obj.Id.GetHashCode() ^
obj.Name.GetHashCode();
}
}
And then you need to change your query to lambda expression:
var groups1 = combinedItems.GroupBy(c => c.article , new GroupItemComparer())
.Select(c => c.Key).ToList();
In case you got any exception regarding translation your method to SQL, you can use AsEnumerable or ToList methods before your GroupBy method, with this methods after data is loaded, any further operation is performed using Linq to Objects, on the data already in memory.
As others have pointed out, the GroupBy is using reference equality by default, and you could get around it by specifying one or more properties to group by. But why is that an error?
The whole point of the query is to translate your Linq query into SQL. Since object reference equality on the client can't be easily translated to SQL, the translator doesn't support it and gives you an error.
When you provide one or more properties to group by, the provider can translate that to SQL (e.g. GROUP BY article.Id), and thus the second method works without error.
I have got 2 tables, Student and CourseTaken. I need to write a LINQ code that displays all CourseTaken, that has Active student status set as true.
I wrote part of the LINQ statement that will display all CourseTaken for a particular Id. How can I further filter it by showing the coursetaken for Active students? (S_ID in CourseTaken contains the student Id.)
List<CourseTaken> courseTakenList =
await dbcont
.CourseTaken
.Where(c => c.CId == courseId)
.ToListAsync();
public class Student
{
public int Id;
public string Name;
public string School;
public bool Active;
}
public class CourseTaken
{
public int CId;
public string CourseName;
public int S_Id;
}
Note: I need to use LINQ and Lambda expressions.
This will give you a list of all courses that has an active student, this assumes you have a navigation property from courses to student called Students
var result = dbcont.CourseTaken.Where(c => c.Students.Any(s => s.Active));
If this is not correct, i think you need to explain your structure better, whether this is Entity framework and you have the appropriate navigation property, and some example data
Update
No, I don't have navigation properties in place. Is there another way
I could get this done ?
Well you probably should, as you are going to have to query the database twice now.
var ids = dbcont.Students.Where(s => s.Active)
.Select(x => x.id)
.ToList();
var result = dbcont.CourseTaken.Where(c => ids.Contains(c.S_Id));
Lastly, take a look at a few entity framework tutorials, your column naming is a little weird, and you really need to hook this up in the spirit of EF. with navigation properties
It sounds to me that you need this query:
from ct in dbcont.CourseTaken
where ct.CId == courseId
join s in dbcont.Student.Where(s => s.Active) on ct.S_Id equals s.Id into gsc
where gsc.Any()
select ct
This is only returning a CourseTaken once, regardless of how many active students are taking the course, as long as their is at least one, of course.
int[] StudentsId =( from s in dbcont.Students
where s.Active ==true
select s.Id).ToArray<int>();
List<CourseTaken> courseTakenList = dbcont.CourseTaken.
Where(c=> StudentsId.Contains(c.S_Id) )
.ToList();
var result =
(from C in db.CourseTakens
join S in db.Students.Where(s => s.Active == true) on C.S_Id equals S.Id
select C
).ToList();
This can get only CourseTaken data. You can add Student data to select clause.
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.
I have a LINQ query that populates a list of designers.
Since I am using the filters below my sorting is not functioning the way I want it to.
My question is, given the code below, how can I best sort this List after the fact or sort while querying?
I have tried to sort the list after the fact using the following script but I am receiving a compiler error:
List<TBLDESIGNER> designers = new List<TBLDESIGNER>();
designers = 'calls my procedure below and comes back with an unsorted list of designers'
designers.Sort((x, y) => string.Compare(x.FIRST_NAME, y.LAST_NAME));
My query is as follows:
List<TBLDESIGNER> designer = null;
using (SOAE strikeOffContext = new SOAE())
{
//Invoke the query
designer = AdminDelegates.selectDesignerDesigns.Invoke(strikeOffContext).ByActive(active).ByAdmin(admin).ToList();
}
Delegate:
public static Func<SOAE, IQueryable<TBLDESIGNER>> selectDesignerDesigns =
CompiledQuery.Compile<SOAE, IQueryable<TBLDESIGNER>>(
(designer) => from c in designer.TBLDESIGNER.Include("TBLDESIGN")
orderby c.FIRST_NAME ascending
select c);
Filter ByActive:
public static IQueryable<TBLDESIGNER> ByActive(this IQueryable<TBLDESIGNER> qry, bool active)
{
//Return the filtered IQueryable object
return from c in qry
where c.ACTIVE == active
select c;
}
Filter ByAdmin:
public static IQueryable<TBLDESIGNER> ByAdmin(this IQueryable<TBLDESIGNER> qry, bool admin)
{
//Return the filtered IQueryable object
return from c in qry
where c.SITE_ADMIN == admin
select c;
}
Thanks in advance,
Billy
There's a lot going on in your post, but I'm choosing to answer the asked question:
Linq - How to sort a list
Use the declarative OrderBy and ThenBy methods.
designers = designers
.OrderBy(td => td.FirstName)
.ThenBy(td => td.LastName)
.ToList();
I'm wondering if its possible to join together IEnumerable's.
Basically I have a bunch of users and need to get their content from the database so I can search and page through it.
I'm using LINQ to SQL, my code at the moment it:
public IEnumerable<content> allcontent;
//Get users friends
IEnumerable<relationship> friends = from f in db.relationships
where f.userId == int.Parse(userId)
select f;
IEnumerable<relationship> freindData = friends.ToList();
foreach (relationship r in freindData)
{
IEnumerable<content> content = from c in db.contents
where c.userId == r.userId
orderby c.contentDate descending
select c;
// This is where I need to merge everything together
}
I hope that make some sense!
Matt
If I understand correctly what you are trying to do, why don't you try doing:
var result = from r in db.relationships
from c in db.contents
where r.userId == int.Parse(userId)
where c.userId == r.UserId
orderby c.contentDate descending
select new {
Relationship = r,
Content = c
}
This will give you an IEnumerable<T> where T is an anonymous type that has fields Relationship and Content.
If you know your users will have less than 2100 friends, you could send the keys from the data you already loaded back into the database easily:
List<int> friendIds = friendData
.Select(r => r.UserId)
.Distinct()
.ToList();
List<content> result = db.contents
.Where(c => friendIds.Contains(c.userId))
.ToList();
What happens here is that Linq translates each Id into a parameter and then builds an IN clause to do the filtering. 2100 is the maximum number of parameters that SQL server will accept... if you have more than 2100 friends, you'll have to break the ID list up and combine (Concat) the result lists.
Or, if you want a more literal answer to your question - Concat is a method that combines 2 IEnumerables together by creating a new IEnumerable which returns the items from the first and then the items from the second.
IEnumerable<content> results = Enumerable.Empty<content>();
foreach (relationship r in friendData)
{
IEnumerable<content> content = GetData(r);
results = results.Concat(content);
}
If you're doing an INNER join, look at the .Intersect() extension method.
Which things are you merging?
There are two main options you could use: .SelectMany(...) or .Concat(...)