i currently have the following LINQ statement:
using (MYEntities ctx = CommonMY.GetMYContext())
{
List<datUser> lstC = (from cObj in ctx.datUser
join fs in ctx.datFS on cObj.UserID equals fs.datUser.UserID
where userOrg.Contains(fs.userOrg.OrgName)
select cObj).ToList();
foreach (datUser c in lstC)
{
Claim x = new Claim
{
UserID= c.userID,
FirstName = c.FirstName,
LastName = c.LastName,
MiddleName = c.MiddleName,
};
}
}
right now it returns all users, but it duplicates them if they have more then 1 org associated with them.
how can i ensure that it only returns distinct UserIDs?
each user can have multiple orgs, but i really just need to return users that have at least 1 org from the userOrg list.
Right before your ToList, put in .Distinct().
In response to #DJ BURB, you should probably use the Distinct overload that takes in an IEqualityComparer to best be sure that you're doing it based off of the unique id of each record.
Look at this blog post for an example.
use group by.
syntax:
var result= from p in <any collection> group p by p.<property/attribute> into grps
select new
{
Key=grps.Key,
Value=grps
}
You will have to call Distinct(), there is no linq query equivalent of that command.
Related
I have 2 classes user(count-10k),address(count-1million). these are like one to many.
i am trying to map the address for users.
Using List(takes few minutes):
List<User> us = usrs.Select(u => new User { id = u.id ,email=u.email,name=u.name,addresses=adrs.Where(a=>a.userid==u.id).ToList()}).ToList();
the above works but its very slow
i changed it to use dictionary and its fast.
Using Dictionary(takes few seconds):
var dusrs = usrs.ToDictionary(usr => usr.id);
var daddrs = adrs.ToDictionary(adr => Tuple.Create(adr.id,adr.userid));
foreach (var addr in daddrs)
{
var usr = dusrs[addr.Value.userid];
if (usr.addresses == null)
{
usr.addresses = new List<Address>();
}
usr.addresses.Add(addr.Value);
}
is there any way i can write better query using list rather than dictionary?
I am just trying to see if i can write better linq using lists
thanks...
vamsee
Assuming you are keeping users and addresses in Lists for some reason, you can use a join in LINQ which will combine the two lists and use a hashed data structure internally to put them together:
var us2 = (from u in usrs
join a in adrs on u.id equals a.userid into aj
select new User { id = u.id, email = u.email, name = u.name, addresses = aj.Select(a => a).ToList() }).ToList();
Alternatively, you can convert the addresses into a Lookup and use that, but it would probably be best to just keep the addresses in a Lookup or create them in a Lookup initially if possible:
var addressLookup = adrs.ToLookup(a => a.userid);
List<User> us = usrs.Select(u => new User { id = u.id, email=u.email, name=u.name, addresses=addressLookup[u.id].ToList() }).ToList();
In my test cases which is faster seems to depend on how many users versus addresses match.
In the following code:
var finalArticles =
from domainArticle in articlesFoundInDomain
join articleCategoryVersion in dbc.ArticlesCategoriesVersions
on domainArticle.ArticleID equals articleCategoryVersion.ArticleID
join articleCategory in dbc.ArticleCategories
on articleCategoryVersion.CategoryID equals articleCategory.CategoryID
where articleCategory.ParentID == 52
group articleCategory by articleCategory.CategoryID
into newArticleCategoryGroup
I understand that the group clause should be returning an IEnumerable where k is the Key, in this case CategoryID.
I think I'm misunderstanding Linq at this point because I assume that for each 'k' there should be a list of articles in 'v', but I don't understand the mechanisms or terminology or something. When I try to project this statement into a new anonymous object I don't seem to get any articles... where are they?
Edit:
Okay so I've got a piece of code that is working, but unfortunately it's hitting the SQL server multiple times:
var articlesAssociatedWithKnowledgeTypes =
from categories in dbc.ArticleCategories
join categoryVersions in dbc.ArticlesCategoriesVersions
on categories.CategoryID equals categoryVersions.CategoryID
join articles in articlesFoundInGivenDomain
on categoryVersions.ArticleID equals articles.ArticleID
where categories.ParentID == 52 && articles.Version == categoryVersions.Version
select new
{
ArticleID = articles.ArticleID,
ArticleTitle = articles.Title,
ArticleVersion = articles.Version,
CategoryID = categories.CategoryID,
CategoryName = categories.Name
} into knowledgeTypesFlat
group knowledgeTypesFlat by new { knowledgeTypesFlat.CategoryID, knowledgeTypesFlat.CategoryName } into knowledgeTypesNested
select new
{
CategoryID = knowledgeTypesNested.Key.CategoryID,
CategoryName = knowledgeTypesNested.Key.CategoryName,
Articles = knowledgeTypesNested.ToList()
};
I thought the ToList() on Articles would sort that out but it doesn't. But, the code works although I'm not sure if this is optimal?
The grouping returns an enumeration of IGroupings. IGrouping<K, V> itself implements IEnumerable<V>. Think of each group as an enumerable of all the members of that group plus an extra property Key
In your first query you are showing a group by and the second one is a group join, both return different results. The group by returns an IEnumerable<IGrouping<TKey, TElement>>. To get the result you're expecting you could group by CategoryId and CategoryName and project as I show below:
var finalArticles =
from domainArticle in articlesFoundInDomain
join articleCategoryVersion in dbc.ArticlesCategoriesVersions
on domainArticle.ArticleID equals articleCategoryVersion.ArticleID
join articleCategory in dbc.ArticleCategories
on articleCategoryVersion.CategoryID equals articleCategory.CategoryID
where articleCategory.ParentID == 52
group articleCategory by new{ articleCategory.CategoryID,articleCategory.CategoryName}
into g
select new {CatId=g.Key.CategoryID, CatName=g.Key.CategoryName,Articles =g.ToList() };
When you need the grouped elements you can call ToList or ToArray as I did above
Your finalArticles query results in a IEnumerable<IGrouping<int, Article>> (assuming CategoryID is int and your articles are of type Article).
These IGrouping<int, Article> provides a Key property of type int (your CategoryID and also the IEnumerable<Article> representing the sequence of articles for that CategoryID.
You can turn this for example into a Dictionary<int, List<Article>> mapping CategoryIDs to the lists of articles:
var dictionary = finalArticles.ToDictionary(group => group.Key, group => group.ToList());
or to a list of categories containing articles:
var categories = finalArticles.Select(group => new {
CategoryID = group.Key,
Articles = group.ToList()}).ToList();
Update after your comment:
var finalArticles =
from domainArticle in articlesFoundInDomain
join articleCategoryVersion in dbc.ArticlesCategoriesVersions
on domainArticle.ArticleID equals articleCategoryVersion.ArticleID
join articleCategory in dbc.ArticleCategories
on articleCategoryVersion.CategoryID equals articleCategory.CategoryID
where articleCategory.ParentID == 52
group articleCategory by new {articleCategory.CategoryID, articleCategory.Name}
into newArticleCategoryGroup
select new
{
CategoryID = newArticleCategoryGroup.Key.CategoryID,
CategoryName = newArticleCategoryGroup.Key.Name,
Articles = newArticleCateGroup.ToList()
}
I am new to Linq to Entity and here is my test scenario:
There are Users
Users have Photo Albums. An album belongs to only one User
Albums have Photos. A Photo belongs to only one Album
Photos have Tags. A Tag may belong to many Photos
I want to write a method which displays Count and Name of Tags used in Albums of a particular User using Linq.
Here is the method that I wrote and it works fine
public static void TestStatistics(int userId)
{
// select AlbumTitle, TagName, TagCount where UserId = userId
var results = from photo in dbSet.PhotoSet
join album in dbSet.AlbumSet on photo.AlbumId equals album.Id into albumSet
from alb in albumSet
where alb.UserId == userId
join photoTag in dbSet.PhotoTagSet on photo.Id equals photoTag.PhotoId into photoTagSet
from pt in photoTagSet
join tag in dbSet.TagSet on pt.TagId equals tag.Id
group new { alb, tag } by new { alb.Title, tag.Name }
into resultSet
orderby resultSet.Key.Name
select new
{
AlbumTitle = resultSet.Key.Title,
TagName = resultSet.Key.Name,
TagCount = resultSet.Count()
};
foreach (var item in results)
{
Console.WriteLine(item.AlbumTitle + "\t" + item.TagName + "\t" + item.TagCount);
}
}
And this is the standart T-SQL query which does the same
SELECT a.Title AS AlbumTitle, t.Name AS TagName , COUNT(t.Name) AS TagCount
FROM TblPhoto p, TblAlbum a, TblTag t, TblPhotoTag pt
WHERE p.Id = pt.PhotoId AND t.Id = pt.TagId AND p.AlbumId = a.Id AND a.UserId = 1
GROUP BY a.Title, t.Name
ORDER BY t.Name
It is pretty obvious that standard T-SQL query is much simpler than the Linq query.
I know Linq does not supposed to be simpler than T-SQL but this complexity difference makes me think that I am doing something terribly wrong. Besides the SQL query generated by Linq is extremly complex.
Is there any way to make the Linq query simpler?
UPDATE 1:
I made it a little simpler without using joins but using a approach like used in T-SQL. Actually it is now as simple as T-SQL. Still no navigation properties and no relations on db.
var results = from photo in dbSet.PhotoSet
from album in dbSet.AlbumSet
from photoTag in dbSet.PhotoTagSet
from tag in dbSet.TagSet
where photo.AlbumId == album.Id && photo.Id == photoTag.PhotoId &&
tag.Id == photoTag.TagId && album.UserId == userId
group new { album, tag } by new { album.Title, tag.Name } into resultSet
orderby resultSet.Key.Name
select new {
AlbumTitle = resultSet.Key.Title,
TagName = resultSet.Key.Name,
TagCount = resultSet.Count()
};
If every photo has at least one tag , then try
var results = (from r in PhotoTag
where r.Photo.Album.UserID == userId
group r by new { r.Photo.Album.Title, r.Tag.Name } into resultsSet
orderby resultsSet.Key.Name
select new
{
AlbumTitle = resultsSet.Key.Title ,
TagName = resultsSet.Key.Name ,
TagCount = resultsSet.Count()
}
);
First things first, you need to setup foreignkeys in your database then rebuild EF and it will 'know' (i.e. navigation properties) about the relationships, which then allows you to omit all of your joins and use something along the lines of the following:
List<AlbumTag> query = (from ps in dbSet.PhotoSet
where ps.Album.UserId = userId
group new { album, tag } by new { ps.Album.Title, ps.PhotoTag.Tag.Name } into resultSet
orderby resultSet.Key.Name
select new AlbumTag()
{
AlbumTitle = resultSet.Key.Title,
TagName = resultSet.Key.Name,
TagCount = resultSet.Count()
}).ToList();
Having a really hard time trying to figure out a "where in" equivalent in nHibernate.
I am using nHibernate 3. What's the best way to do something like this:
SELECT DISTINCT
U.ID as CID
FROM User U
WHERE
CID IN (
SELECT RID
FROM ResellerSites rs
INNER JOIN Locations l ON l.ID = rs.LID
WHERE l.ID = 14
)
I found an examples somewhere that says I need to have something like this:
var criteria = session.CreateCriteria(typeof(User));
var resellerSites = new[] { new ResellerSite { Id = 1 }, new ResellerSite { Id = 2 } };
criteria.Add(new InExpression("ResellerSite", resellerSites));
This is what I have so far:
var resellerSites = session.CreateCriteria<ResellerSite>("p")
.CreateCriteria("p.Locations", JoinType.InnerJoin)
.Add(Restrictions.Eq("locationID", locationId))
.List<ResellerSite>();
criteria.Add(new InExpression("ResellerSite", resellerSites));
var finalList = criteria.List<ResellerSite>();
The "criteria.Add(new InExpression" part is giving me an intellisense error, as InExpression seems to expect something like:
new[] { new ResellerSite { Id = 1 }, new ResellerSite { Id = 2 } };
Like in the first example.
Should I do a for loop like:
foreach (var site in resellerSites)
{
//somehow push into a new[] ?
}
or something like that or is there a better way?
Perhaps this entire approach is wrong?
I'd say that the typing problem comes from the double CreateCriteria and the single ToList()
Difficult to say without the table definitions, but, rereading you code, I wonder why you need the inner join with the locations.
May be you can rewrite your query as :
SELECT DISTINCT
U.ID as CID
FROM User U
INNER JOIN ResellerSites rs
on U.ID = rs.RID
and rs.LID=14
And have your code like (not tested and not optimal as you could have only one criteria query with join)
var resellerSites = session.CreateCriteria<ResellerSite>()
.Add(Expression.Eq("locationID", locationId))
.List<ResellerSite>();
criteria.Add(new InExpression("ResellerSite", resellerSites.ToArray()));
var finalList = criteria.List<ResellerSite>();
You can easily convert resellerSites to array using Linq:
criteria.Add(new InExpression("ResellerSite", resellerSites.ToArray()));
Also this question may help: Cannot use collections with InExpression
I'd like to return an object with the following signature
class AnonClass{
string Name {get;}
IEnumerable<Group> Groups {get;}
}
I have tried the following query, but g only returns a single entity, not all the joined entities
var q = from t in dc.Themes
join g in dc.Groups on t.K equals g.ThemeK
select new {t.Name, Groups = g};
return q.ToArray();
but this returns
class AnonClass{
string Name {get;}
Group Groups{get;}
}
What is the correct linq query to use?
I think you want "join into" instead of just "join":
var q = from t in dc.Themes
join g in dc.Groups on t.K equals g.ThemeK into groups
select new { t.Name, Groups=groups };
(That's completely untested, however - worth a try, but please verify carefully!)
If you have the Foreign Key set up correctly, then it should be:
var q = from t in dc.Themes
select new {t.Name, Groups = t.Groups};