What is the best way to go about joining two querys in linq? At the moment my code does not join them together.
var userprofilemodel1 =
(from u in db.UserProfiles
where u.UserId == 1
orderby u.FirstName, u.LastName
select new UserListViewModel
{
UserId = u.UserId,
UserName = u.UserName,
FirstName = u.FirstName,
LastName = u.LastName,
DocumentCount = u.Documents.Count
}).ToPagedList(page, 10);
var userprofilemodel2 =
(from u in db.UserProfiles
where u.UserId == 18
orderby u.FirstName, u.LastName
select new UserListViewModel
{
UserId = u.UserId,
UserName = u.UserName,
FirstName = u.FirstName,
LastName = u.LastName,
DocumentCount = u.Documents.Count
}).ToPagedList(page, 10);
userprofilemodel1.Concat(userprofilemodel2);
It seems like this should do it:
var userprofilemodel1 =
(from u in db.UserProfiles
where u.UserId == 1 || u.UserId == 18
orderby u.FirstName, u.LastName
select new UserListViewModel
{
UserId = u.UserId,
UserName = u.UserName,
FirstName = u.FirstName,
LastName = u.LastName,
DocumentCount = u.Documents.Count
}).ToPagedList(page, 10);
If you want/need to leave it as two separate queries but are trying to concatenate the results, then you need to realize that Concat returns a concatenated list - it does not modify either of the existing lists:
var mergedModel = userprofilemodel1.Concat(userprofilemodel2);
Related
I have this Linq to entity query that returns a list of visitors with joins.
I want a similar query to return a single record without the query being a List collection, but changing it to a basic select with FirstOrDefaultAsync gets the error "A query body must end with a select clause or a group clause"
public async Task<List<VisitorDetail>> GetOneVisitor(int visitorId)
{
var query = await (from v in _context.Visitors
where v.Id == visitorId
join d in _context.Categories on v.VisitCategoryId equals d.Id
join c in _context.Counters on v.AssignedCounter equals c.Host into counterGroup
from cg in counterGroup.DefaultIfEmpty()
select new
{
FirstName = v.FirstName,
LastName = v.LastName,
CounterDescription = cg.Description,
VisitCategory = d.Description
}).ToListAsync();
List<VisitorDetail> visitors = new();
foreach (var p in query)
{
visitors.Add(new VisitorDetail
{
FirstName = p.FirstName,
LastName = p.LastName,
CounterDescription = p.CounterDescription,
CategoryDescription = p.VisitCategory
});
}
return visitors;
}
If you want to keep DRY with LINQ to Entities, return IQueryable for your common queries and invoke materialization only when it is needed.
Your method can be rewritten in the following way:
public IQueryable<VisitorDetail> GetVisitorDetails(int visitorId)
{
var query =
from v in _context.Visitors
where v.Id == visitorId
join d in _context.Categories on v.VisitCategoryId equals d.Id
join c in _context.Counters on v.AssignedCounter equals c.Host into counterGroup
from cg in counterGroup.DefaultIfEmpty()
select new VisitorDetail
{
FirstName = v.FirstName,
LastName = v.LastName,
CounterDescription = cg.Description,
CategoryDescription = d.Description
};
return query;
}
var many = await GetVisitorDetails(visitorId).ToListAsync();
var one = await GetVisitorDetails(visitorId).FirstOrDefaultAsync();
Ii just want to make search functionality with linq with multiple ColumnNames that stored to session variable. I'm using one method:
public void FillGrid(string CommandName,string ColumnName, string SearchText)
That has three string variable that stores session value.
Now I just want to pass ColumnName with this query:
var query1 = (from p in db.Posts
join c in db.Categories on p.Category_id equals c.Id
join u in db.Users on p.User_id equals u.Id
where (p.ToUser_id == user_id || p.ToUser_id == null) && p.User_id != user_id
orderby p.Sent_Datetime descending
select new
{
Id = p.Id,
Title = p.Title,
Publisher = u.First_name + " " + u.Last_name,
ToUser = p.ToUser_id,
PublishDate = p.Sent_Datetime,
IsFile = p.IsFileAttached,
CategoryName = c.Category_name,
status_name = (from s in db.Status where (s.Id == p.status_id) select s.status_name).FirstOrDefault(),
Group_name = (from g in db.Groups where (g.Id == p.group_id) select g.Group_name).FirstOrDefault(),
FileSize = p.TotalFileSize,
ColumnName = Sesssion["ColumnName"].ToString()
}).Where(q => q.ColumnName.Contains(SearchText));
However, ColumnName does not give any text or it may be not part of this query i have to manually give column name because.
for multiple column i have, so i can not use this statement like:
.Where(q => q.Tile.Contains(SearchText));
this query works fine with single column. but there is multiple column i have so i have to set q.ColumnName from outer side.
I would do an extension method for that kind of things, building an expression for your predicate.
public static class Helper
{
public static IQueryable<T> FilterForColumn<T>(this IQueryable<T> queryable, string colName, string searchText)
{
if (colName != null && searchText != null)
{
var parameter = Expression.Parameter(typeof(T), "m");
var propertyExpression = Expression.Property(parameter, colName);
var searchExpression = Expression.Constant(searchText);
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var body = Expression.Call(propertyExpression, containsMethod, searchExpression);
var predicate = Expression.Lambda<Func<T, bool>>(body, new[] { parameter });
return queryable.Where(predicate);
}
else
{
return queryable;
}
}
}
usage in your case
var query1 = (from p in db.Posts
join c in db.Categories on p.Category_id equals c.Id
join u in db.Users on p.User_id equals u.Id
where (p.ToUser_id == user_id || p.ToUser_id == null) && p.User_id != user_id
orderby p.Sent_Datetime descending
select new
{
Id = p.Id,
Title = p.Title,
Publisher = u.First_name + " " + u.Last_name,
ToUser = p.ToUser_id,
PublishDate = p.Sent_Datetime,
IsFile = p.IsFileAttached,
CategoryName = c.Category_name,
status_name = (from s in db.Status where (s.Id == p.status_id) select s.status_name).FirstOrDefault(),
Group_name = (from g in db.Groups where (g.Id == p.group_id) select g.Group_name).FirstOrDefault(),
FileSize = p.TotalFileSize,
}).FilterForColumn(Sesssion["ColumnName"].ToString(), SearchText);
I'm trying to translate an SQL query into LINQ, but after numerous attempts, I'm still not there... Can anyone help ?
This is my working SQL statement
SELECT Users.email, SUM(Skills.level) AS SkillLevel
FROM Skills INNER JOIN
SkillsPerUser ON Skills.pk_skill_id = SkillsPerUser.fk_skill_id INNER JOIN
Users ON SkillsPerUser.fk_user_id = Users.pk_userid
GROUP BY Users.email
ORDER BY SkillLevel DESC
This is what I came up with so far, but it lacks a sum() where I hard coded the number 3, that should be the sum of Skills.level:
var allSkillsPerUser = from u in dc.Users
join spu in dc.SkillsPerUsers on u.pk_userid equals spu.fk_user_id
join s in dc.Skills on spu.fk_skill_id equals s.pk_skill_id
select new { Email = u.email, Level = s.level } into su
group su by su.Email into gsu
select new { Email = gsu.Key, SkillLevel = gsu.Sum(su => su.Level) };
ViewBag.spu = allSkillsPerUser.ToList();
The view bag gives the following error (Email can't be found, while in the variables below you can see that they do exist...):
context.Skills
.Join(context.SkillsPerUser, s => s.pk_skill_id, spu => spu.fk_skill_id, (s, spu) => new { Skill = s, SkillToUser = spu })
.Join(context.Users, sspu => sspu.SkillToUser.fk_userId, u => u.pk_userid, (sspu, u) => new { Email = u.Email, SkillLevel = sspu.Skill.level })
.GroupBy(su => su.Email)
.Select(g => new { Email = g.Key, SkillLevel= g.Sum(su => su.Level) })
.OrderByDescending(g => g.SkillLevel)
It's a bit simpler if you have navigation properties set up on your entities:
context.SkillsPerUser
.Select(spu => new { Email = spu.User.email, Level = spu.Skill.level }) // guessing at the navigation property names here
.GroupBy(su => su.Email)
.Select(g => new { Email = g.Key, SkillLevel = g.Sum(su => su.Level) })
.OrderByDescending(g => g.SkillLevel)
Or, using the query syntax
from u in dc.Users
join spu in dc.SkillsPerUsers on u.pk_userid equals spu.fk_user_id
join s in dc.Skills on spu.fk_skill_id equals s.pk_skill_id
select new { Email = u.email, Level = s.level } into su
group su by su.Email into gsu
select new { Email = gsu.Key, SkillLevel = gsu.Sum(su => su.Level) }
To order by the sum you can do this:
from u in dc.Users
join spu in dc.SkillsPerUsers on u.pk_userid equals spu.fk_user_id
join s in dc.Skills on spu.fk_skill_id equals s.pk_skill_id
select new { Email = u.email, Level = s.level } into su
group su by su.Email into gsu
select new { Email = gsu.Key, SkillLevel = gsu.Sum(su => su.Level) } into userSkills
orderby userSkills.SkillLevel descending
I have a query like this:
var q =
from u in db.User
select new
{
userId = u.UserId,
userName = o.Name,
userAvatar = o.AvatarCode
};
Then, I have to transform AvatarCode to AvatarPath using custom static method Image.GetPath.
It's possible to make this in the following way:
var q =
(from u in db.User
select new
{
userId = u.UserId,
userName = o.Name,
userAvatar = o.AvatarCode
})
.AsEnumerable()
.Select(new
{
userId = u.UserId,
userName = o.Name,
userAvatar = Image.GetPath(o.AvatarCode)
};
But if the number of object fields is large then it's an overkill to duplicate all fields in the second Select.
Are there any alternatives?
For example, some approach to mark methods that should be executed after query execution:
var q =
from u in db.User
select new
{
userId = u.UserId,
userName = o.Name,
userAvatar = Linq.ExecuteLater(Image.GetPath(o.AvatarCode))
};
There is not any such method but if the problem is only number of fields which must be specified again in the second select you can do something like this:
var q =
(from u in db.User
select new
{
userId = u.UserId,
userName = u.Name,
userAvatar = u.AvatarCode
})
.AsEnumerable()
.Select(u => new
{
User = u,
Path = Image.GetPath(u.AvatarCode)
});
SELECT StudentHistoryId, StudentId, Grade, CreatedAt, ModifiedAt, ModifiedBy, Active
FROM TABLENAME TN
INNER JOIN ( SELECT StudentId, MAX(ModifiedAt) AS ModifiedAt FROM TABLENAME GROUP BY StudentId) M
ON TN.StudentId = M.StudentId AND TN.ModifiedAt = M.ModifiedAt
Here's a direct translaton:
var subquery = from tn in dc.TABLENAME
group tn by tn.StudentId into g
select new { StudentId = g.Key, ModifiedAt = g.Max(x => x.ModifiedAt) };
var query = from tn in dc.TABLENAME
join m in subquery
on new { tn.StudentId, tn.ModifiedAt }
equals new { m.StudentId, m.ModifiedAt }
select new
{
tn.StudentHistoryId,
tn.StudentId,
tn.Grade,
tn.CreatedAt,
tn.ModifiedAt,
tn.ModifiedBy,
tn.Active
};