I have 3 tables that holds the locations, users, with their relevant Ids.
What I'm trying to achieve is to allow an admin to be able to search for
users in a particular location.
So in short, I'm trying to allow Sam who is the Admin to locate users only in his location(restricted to his location), so in this example, Mark who is a regular user is located in HeadLocation and Location1 and can be found by Sam.
User Table
|---------------------------------|
| UserId | Role | FName |
|---------------------------------|
| 1 | Admin | Sam |
OfficeUser Table
|---------------------------------------|
| OfficeUserId | OfficeId | UserId |
|---------------------------------------|
| 55 | 1 | 1 |
Office Table
|----------------------------------------------|
| OfficeId | HeadOfficeId | LocNames |
|----------------------------------------------|
| 1 | null | HeadLocation |
|----------------------------------------------|
Code
[HttpGet]
public IActionResult SearchUsers(string searchUser, string id)
{
//userId from claims
var userId = User.FindFirst(ClaimTypes.NameIdentifier).Value;
//gets userId for users with role of admin
var admin = _context.User.FirstOrDefault(a => a.Role == "Admin")?.UserId.ToString();
if (adminExists && admin == userId)
{
//This is where I'm unclear...
var search = from a in _context.OfficeUser
join b in _context.Office
on a.OfficeId equals a.OfficeId
select new
{
a.UserId,
//trying to get names per locations
};
if (search == null)
{
return NotFound();
}
if (!String.IsNullOrEmpty(searchUser))
{
search = search.Where(a => a.FName.Contains(searchUser));
}
else
{
return NotFound("No Users Found");
}
}
return Ok(search);
}
This will be the generalized view of your query. There can some typing mistakes as I have typed this in here, not in visual studio editor.
[HttpGet]
public IActionResult SearchUsers(string searchUser, string id)
{
var loggedInUserId = User.FindFirst(ClaimTypes.NameIdentifier).Value;
var isLoggedInUserAdmin = _context.User.Any(u => u.UserId == loggedInUserId && u.Role == "Admin");
if (isLoggedInUserAdmin)
{
var adminLocations = _context.OfficeUser.Where(ou => ou.UserId == userId).Select(ou => ou.Office.LocNames).ToList();
var searchQuery = _context.OfficeUsers.Where(ou => adminLocations.Contains(ou.Office.LocNames)).Select(ou => ou.User);
if (!String.IsNullOrEmpty(searchUser))
{
searchQuery = searchQuery.Where(q => q.FName.Contains(searchUser));
}
var adminLocUserList = searchQuery.ToList();
return Ok(userList)
}
var userList = _context.OfficeUsers.Where(ou.User.FName.Contains(searchUser)).Select(ou => ou.User).ToList();
return Ok(userList);
}
Related
I'm working in ASP.NET Core. I have a problem with querying rows which have same User_id and Definition_id, if there are any like that, I need ID of the row.
+----+---------+---------------+
| Id | User-id | Definition-id |
+----+---------+---------------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 2 | 2 |
| 4 | 3 | 1 |
| 5 | 4 | 1 |
| 6 | 4 | 1 |
| 7 | 5 | 2 |
| 8 | 6 | 1 |
+----+---------+---------------+
I need to query table like this, to return { 5, 6 } to me, because of them having same user AND definition ids.
I've tried Groupby for both values, but I can't get the IQueryable or IGrouping to give me the id of specific row.
I'd imagine it to work like that, but it ain't.
var placementsWithDuplicates =
from p in _context.Placements
group p by new { p.User_id, p.Definition_id } into what
select new
{
Id = what.Id,
User = what.User_id,
Defi = what.Definition_id,
};
foreach (var p in placementsWithDuplicates)
{
issues.Add(new IssueModel()
{
Type = "Praxe ID[" + p.Id + "]",
Name = "User id [" + p.User + "], Definition id [" + p.Defi + "]",
Detail = "So user shouldnt have data for more definitons!"
});
};
Thanks to Satish Hirpara for best answer, it needed a little update so I post the thing that ended up working well:
var B = from p in _context.Placements
group p by new { p.User_id, p.Definition_id } into what
where what.Count() > 1
select new
{
User_id = what.Key.User_id,
Definition_id = what.Key.Definition_id
};
var placementsWithDuplicates = from A in _context.Placements
join b in B on new { A.User_id, A.Definition_id } equals new { b.User_id, b.Definition_id }
select A;
Please find below SQL query:
SELECT A.*
FROM Placements A
INNER JOIN
(SELECT User_id, Definition_id FROM Placements
GROUP BY User_Id, Definition_id
HAVING COUNT(*) > 1) B
ON A.User_id = B.User_id AND A.Defination_id =
B.Defination_id
You can create a temp table to avoid join of sub query.
If you want linq query then I tried to create it from above query, please find it below:
--- sub query
var B = from p in Placements
group p by new { p.User_id, p.Definition_id } into what
where what.count() > 1
select new
{ User_id = what.User_id,
Definition_id =what.Definition_id
};
--- join
Var result = from A in Placements
Join B ON A.User_id = B.User_id
AND A.Defination_id = B.Defination_id
Select A
Please try this one.
Try This
var placementsWithDuplicates = from p in _context.Placements.Where(m => m.User_id == m.Definition_id)
select new {
Id = p.Id,
User = p.User_id,
Defi = p.Definition_id,
};
// this is same as the top one
var placementsWithDuplicates = from p in _context.Placements where p.User_id == p.Definition_id
select new {
Id = p.Id,
User = p.User_id,
Defi = p.Definition_id,
};
foreach (var p in placementsWithDuplicates)
{
issues.Add(new IssueModel()
{
Type = "Praxe ID[" + p.Id + "]",
Name = "User id [" + p.User + "], Definition id [" + p.Defi + "]",
Detail = "So user shouldnt have data for more definitons!"
});
};
I have the following list and I need to transform it to get this result
original list
+----+--------------------+
| key| email |
+====+====================+
| 1 | one#gmail.com |
| 2 | two#gmail.com |
| 1 | three#gmail.com |
| 1 | four#gmail.com |
| 2 | five#gmail.com |
| 2 | six#gmail.com |
+----+----------------+-----
New list the mails concatenated with ;
+----+---------------------------------------------------+
| key| email |
+====+===================================================+
| 1 | one#gmail.com;three#gmail.com;four#gmail.com |
| 2 | two#gmail.com;five#gmail.com;six#gmail.com |
+----+---------------------------------------------------+
How can i accomplish this task with c# Linq? thanks
Try in this way:
x.GroupBy(t => new {t.Id})
.Select(t => new {
Id = t.Key.Id,
email = String.Join(", ", t.Select(p => p.Email).ToArray())
}
Supposing that you have a class similar to this:
public class myEmails
{
public int Key { get; set; }
public string Email { get; set; }
}
and a List<myEmails> that could be like this:
List<myEmails> emails = new List<myEmails>();
You could GroupBy the Key value and then Join all strings (Emails) related to that Key, creating a new Dictionary<int, string> that contains all the Emails grouped by the given Key:
var emailsDictionary = emails
.GroupBy(eml => eml.Key)
.ToDictionary(grp => grp.Key, grp => string.Join(" ", grp.Select(eml => eml.Email)));
Test the result with:
emailsDictionary.ToList().ForEach(dict => Console.WriteLine($"{dict.Key}: {dict.Value}"));
It should give you:
1: one#gmail.com three#gmail.com four#gmail.com
2: two#gmail.com five#gmail.com six#gmail.com
As a note, if you're interested, you could also use Aggregate instead of Join, so this:
grp => string.Join(" ", grp.Select(eml => eml.Email))
could also be expressed as:
grp => grp.Aggregate("", (s, eml) => (eml.Email + " " + s))
I have a datatable which contains multiple hierarchies with different heights which I need to split.
eg.
|---------------------|------------------|
| Account | Hierarchy Account|
|---------------------|------------------|
| 1 | |
|---------------------|------------------|
| 2 | 1 |
|---------------------|------------------|
| 3 | 1 |
|---------------------|------------------|
| 4 | 2 |
|---------------------|------------------|
| 5 | 3 |
|---------------------|------------------|
| 6 | |
|---------------------|------------------
| 7 | 6 |
|---------------------|------------------|
| 8 | 6 |
|---------------------|------------------|
Below is what I have tried so far.
private List<DataTable> SplitDataTablesOnHierarchy(DataTable dataTable)
{
List<DataTable> dataTablesList = new List<DataTable>();
List<string> listTemp = new List<string>();
var HierarchyAccounts = dataTable.AsEnumerable().Where(m => m.Field<string>("Hierarchy Account Number") == "");
foreach(var topAccount in TopAccounts )
{
//Check if account exists in Hierarchy Account Number
var topAccountExists = dataTable.AsEnumerable().Any(m => m.Field<string>("Hierarchy Account Number") == topAccount.Field<string>("Account Number"));
if (topAccountExists == true)
{
DataTable newDataTable = dataTable.Clone();
newDataTable.ImportRow(payerAccount);
dataTablesList.Add(newDataTable);
}
//Top Accounts found and added to tempList
}
//CreateDataTable with Top Accounts
foreach(DataTable dTable in dataTablesList)
{
bool bottomHierarchyReached = true;
var TempSearch = dTable.Rows;
while(bottomHierarchyReached)
{
foreach(DataRow account in TempSearch)
{
var rows = dataTable.AsEnumerable().Where(m => m.Field<string>("Hierarchy Account Number") == account.Field<string>("Account Number")).CopyToDataTable();
if(rows.Rows.Count == 0)
{
bottomHierarchyReached = false;
break;
}
else
{
TempSearch = rows.Rows;
dTable.Rows.Add(rows.Rows);
}
}
}
}
return dataTablesList;
}
My thought process above was to first find the highest accounts in the hierarchy, create new datatables with those accounts and then drill down and add the following levels to the relevant datatable recursively since I do not know the height of each hierarchy.
Found a solution by creating a tempList which keeps the all of the lower levels while searching through the level above.
Once the loop through the SearchList is done we assign the tempList to it.
And then search through the next level of the hierarchy.
foreach (DataTable dTable in dataTablesList)
{
bool bottomHierarchyReached = true;
var SearchList = dTable.AsEnumerable().Select(p=> new { HierarchyAccount = p.Field<string>("Hierarchy Account Number"),
Account = p.Field<string>("Account Number")
}).ToList();
var tempList = SearchList.ToList();
tempList.Clear();
while (bottomHierarchyReached)
{
tempList.Clear();
foreach (var account in SearchList)
{
var rows = dataTable.AsEnumerable().Where(m => m.Field<string>("Hierarchy Account Number") == account.Account);
if(rows.Count() == 0)
{
bottomHierarchyReached = false;
break;
}
else
{
tempList.AddRange(rows.AsEnumerable().Select(p => new {
HierarchyAccount = p.Field<string>("Hierarchy Account Number"),
Account = p.Field<string>("Account Number")
}).ToList());
foreach(var row in rows)
{
dTable.ImportRow(row);
}
}
}
SearchList = tempList.ToList();
}
}
I'm stuck rewriting this SQL in Lambda:
SELECT TOP 1000 COUNT(LoginDateTime)
,[LoginDateTime]
,[Culture]
FROM [LearningApp].[dbo].[UserActivities]
group by LoginDateTime, Culture
Result:
+-----+---------------------------+----------+
| | LoginDateTime | Culture |
+-----+---------------------------+----------+
| 1 | 2016-07-14 12:21:23.307 | de |
| 4 | 2016-07-13 12:21:23.307 | en |
| 2 | 2016-07-14 12:21:23.307 | en |
+-----+---------------------------+----------+
And my code:
public List<UserActivityResponseContract> GetUserActivity()
{
var userActivityResponseContracts = new List<UserActivityResponseContract>();
var userActivitiesList = _baseCommands.GetAll<UserActivity>()
.Select(x => new
{
x.LoginDateTime,
x.Culture
})
.GroupBy(x => new { x.LoginDateTime, x.Culture});
foreach (var userActivity in userActivitiesList)
{
userActivityResponseContracts.Add(new UserActivityResponseContract
{
ActivityDate = userActivity.Key.LoginDateTime.ToShortDateString(),
NumberOfTimesLoggedIn = userActivity.Count(),
Culture = userActivity.Key.Culture
});
}
return userActivityResponseContracts;
}
It doesn't seem very difficult but I am a bit stuck.
Method Syntax:
var result = _baseCommands.GetAll<UserActivity>()
.GroupBy(x => new { x.LoginDateTime, x.Culture})
.Select (x => new UserActivityResponseContract
{
ActivityDate = x.Key.LoginDateTime.ToShortDateString(),
Culture = x.Key.Culture,
NumberOfTimesLoggedIn = x.Count()
})
.Take(1000).ToList();
You can also use an overload of GroupBy that enables you to pass the select function as a second parameter
Query Syntax:
var result = (from x in _baseCommands.GetAll<UserActivity>()
group x by new { x.LoginDateTime, x.Culture} into g
select new UserActivityResponseContract
{
ActivityDate = g.Key.LoginDateTime.ToShortDateString(),
Culture = g.Key.Culture,
NumberOfTimesLoggedIn = g.Count()
}).Take(1000).ToList();
To GroupBy just the Date part of this DateTime do: x.LoginDateTime.Date
I need to get a list of players and every team that said player is associated to and add it to my ViewModel.
ViewModel
public class PlayersViewModel
{
public long PlayerID { get; set; }
public string PlayerName { get; set; }
public List<long> TeamID { get; set; } //Players can be assigned to 1 or more teams
}
I have a few different datatables going on:
PlayersInTeams (linking table)
+------------+---------+
| PlayerID | TeamID |
+------------+---------+
| 1 | 10001 |
| 1 | 10002 |
| 2 | 10002 |
| 3 | 10001 |
+------------+---------+
Players
+------------+---------+-----------+
| PlayerID | ForeName| Surname |
+------------+---------+-----------+
| 1 | John | Doe |
| 2 | Pete | Noe |
| 3 | Evan | Soe |
+------------+---------+-----------+
So for the above example tables, Player 1 - John Doe should have an array of 2 teamIDs in the ViewModel [10001, 10002].
Aim
I'm trying to have a List<PlayersViewModel> with a collection of TeamIDs.
Code
public List<PlayersViewModel> GetPlayers()
{
var playersInTeam = new PlayersInTeamsBLL();
var pit = playersInTeam.GetPlayersInTeams();
var playerDetail = Players;
var list = from p in pit
join team in Teams on p.TeamID equals team.TeamID // Only get teams that related to club
join pd in playerDetail on p.PlayerID equals pd.PlayerID //Link a player to
where pd.IsArchived == false
select new PlayersViewModel { TeamID = team.TeamID, PlayerID = p.PlayerID, PlayerName = pd.Forename + " " + pd.Surname};
return list.ToList();
}
I'm getting null PlayerIDs and obviously my TeamID isn't getting populated.
Any suggestions / solutions?
Try something like this:
var viewModels = playerDetail.Select(p => new PlayersViewModel()
{
PlayerID = p.PlayerID,
PlayerName = String.Format("{0} {1}", p.ForeName, p.Surname),
TeamID = pid.Where(pidElement => pidElement.PlayerID == p.PlayerID)
.Select(pidElement => pidElement.TeamID).ToList()
}).ToList();
In general it seems to me you're trying to tackle the problem from the wrong end. You want a list of players first, and then their teams second, not a list of player-team associations first, and player details second, so you should start with the playerDetail object (I assume it's an IEnumerable of all Player objects).
On a side note: consider that you can add a getter to your Player class which would give you the full name without the need to always concatenate the name and surname. Something like:
public string FullName
{
get { return String.Format("{0} {1}", this.FirstName, this.Surname); }
}
var results = yourContext.Players
.Select(p => new PlayersViewModel
{
PlayerID = p.PlayerID,
PlayerName = p.PlayerName,
TeamID = context.PlayersInTeams.Where(x => x.PlayerID == p.PlayerID)
}).ToList();
You need group them using group by like this:
var teamsFinal = from p in pit
join pd in playerDetail on p.PlayerID equals pd.PlayerID
where pd.IsArchived == false
group new {p,pd} by new
{
pd.PlayerID ,
pd.ForeName,
pd.SurName
} into g
select new PlayersViewModel
{
TeamID = g.Select(x => x.p.TeamID).ToList(),
PlayerID = g.Key.PlayerID,
PlayerName = g.Key.ForeName + " " + g.Key.SurName
};
Check this working Fiddle example