Getting wrong output with linq join query - c#

I have this data:Users
UserId Name
42 Abc
43 Pqr
44 lmn
45 xyz
Mappings:
MappingId User1 User2
1 42 43
2 42 44
3 43 44
I want to get all user that is user2 which is not in user 1 and so the output will be below considering above input:
Expected Output:
UserId Name
44 lmn
This is my query:
var data = (from user in context.Users
join mappng in context.Mappings on user.UserId equals mappng.User2
where mappng.User1 != mappng.User2
select new
{
Name = user.FirstName + " " + user.LastName,
UserId = user.UserId,
}).ToList();
But getting wrong output:
UserId Name
43 Pqr
44 lmn
44 lmn
Note: There is no foreign key relationship and so no navigation property.

Here's a solution to your problem in a console application. I used where !(..) to find those not in mappings User1. I'm not sure of other alternatives to this approach. But hope it helps.
class Program
{
static void Main(string[] args)
{
List<User> users = new List<User>()
{
new User() { UserId = 42, Name = "Abc" },
new User() { UserId = 43, Name = "Pqr" },
new User() { UserId = 44, Name = "lmn" },
new User() { UserId = 45, Name = "xyz" },
};
List<UserMapping> userMappings = new List<UserMapping>()
{
new UserMapping() { MappingId = 1, User1 = 42, User2 = 43},
new UserMapping() { MappingId = 2, User1 = 42, User2 = 44},
new UserMapping() { MappingId = 3, User1 = 43, User2 = 44},
};
var data = (from u in users
join m in userMappings on u.UserId equals m.User2
where !(from mm in userMappings
select mm.User1).Contains(u.UserId)
select u).Distinct();
foreach(var entry in data)
{
Console.WriteLine(entry.UserId + " " + entry.Name);
}
Console.ReadLine();
}
}
class User
{
public int UserId { get; set; }
public string Name { get; set; }
}
class UserMapping
{
public int MappingId { get; set; }
public int User1 { get; set; }
public int User2 { get; set; }
}
Output:
44 lmn

The LINQ posted compares User1 and User2 values of the same row by using mappng.User1 != mappng.User2, which isn't the wanted query. Try using !Any() as follow :
var data = (from user in context.Users
join mappng in context.Mappings on user.UserId equals mappng.User2
where !context.Mappings.Any(m => m.User1 == user.UserId)
select new
{
Name = user.FirstName + " " + user.LastName,
UserId = user.UserId,
}).Distinct().ToList();

Try this:
var data = (from map1 in context.Mappings
join map2 in context.Mappings
on map1.User2 equals map2.User1 into subMap
from sub in subMap.DefaultIfEmpty()
where sub == null
join user in context.Users on map1.User2 equals user.UserId
select new {
Name = user.FirstName + " " + user.LastName,
user.UserId,
}).Distinct().ToList();

Could you try this.
var data = (select user from context.Users where (from m2 in context.Mappings select m2.User2).Except(from m1 in context.Mappings select m1.User1).Contains(user.UserId) select new {Name=user.Name, UserId=user.UserId}).ToList();
I don't see the user.FirstName, user.LastName in example table.
So you can revise new object by yourself if this solution is work.

Try this code snipped.
var result = context.Mappings.Where(mapping1 => !context.Mappings.Select(mapping2 => mapping2.User1).Contains(mapping1.User2))
.Select(e=> e.User2).Distinct()
.Join(context.Users, arg => arg, user=> user.UserId,(arg,user) => user)
.ToList();

Related

How can I get only records with a unique property using entity framework and linq?

public class Person
{
public string firstName;
public string lastName;
}
I want a list of all Persons with a unique first name.
Persons table
Tom Haverford
Tom Baker
Amy Pond
Amy Santiago
Trish Walker
Chidi Anagonye
The query should return
Trish, Chidi
I've tried using Distinct and a combination of GroupBy and Select, but those return Trish, Chidi, Tom, Amy.
Demo on dotnet fiddle
You can Group by then count number of duplicated items. After that, you can get the item with count value equals to 1 like below.
var arr = new []
{
new Person { firstName = "Tom", lastName = "Haverford" },
new Person { firstName = "Tom", lastName = "Baker"},
new Person { firstName = "Amy", lastName = "Pond" },
new Person { firstName = "Amy", lastName = "Santiago"},
new Person { firstName = "Trish", lastName = "Walker"},
new Person { firstName = "Chidi", lastName ="Anagonye" }
};
var result = arr.GroupBy(p => p.firstName).Select(g => new { Name = g.Key, Count = g.Count()});
foreach(var item in result.Where(p => p.Count == 1))
Console.WriteLine(item.Name);
Output
Trish
Chidi
You can use group by and count functionality together for this :
1. Get a list of all persons from DB :
var personList = (from p in db.Person select p).ToList(); // I assumed here that your db obj name is 'db' and table name is 'Person'
2. Now apply group by query to get the count of first names :
var q = from x in personList
group x by x.firstName into g
let count = g.Count()
select new {Count = count, Name = g.First().firstName };
3. Now you can get your final result like this :
var finalResult = (from p in q where p.Count == 1 select p).ToList();
Happy Coding...

Join with comma separated values in SQL Server [Linq]

I am writing a question with LINQ to join li. How do I get the result table with 3 table combinations? I have to combine the table in one line.
Any ideas?
Peole
---------------
Id | 1 Id |2
Name | David Name |Ameyy
Surname| David1 Surname |Ameyy2
Appointment
---------------
Id |19
PeopleId |1
Subject |description
Participant
---------------
Id |1 Id |2
AppointmentId |19 AppointmentId |19
PeopleId |1 PeopleId |2
Result
----------------------------------
Id | 1
Subject | Subject
Participant| David David1, Ameyy Ameyy2
Linq query;
IQueryable<AppointmentPoolModel> query = db.Randevu.Join(db.Kisi,
appointment => appointment .TALEPEDENKISI,
people=> people.ID,
(appointment , people)
=> new AppointmentPoolModel
{
Id = appointment.ID,
Subject = appointment.Subject,
Note = appointment .NOTLAR,
NameSurname = people.Name+ " " + people.Surname,
RequestedId = people.ID,
//Participan = string.Join(",", )
});
var result = query.OrderBy(appointment => randevu.AppointmentStartDate).ToList();
You can try this.
var qPeoples = Participants.Join(Peoples,
pr => pr.PeopleId,
pe => pe.Id,
(pr, pe) => new { Part = pr, People = pe });
var result = Appointments.Select(app => new
{
app.Id,
app.Subject,
Participant = String.Join(",", qPeoples.Where(q => q.Part.AppointmentId == app.Id)
.Select(s => new
{ FullName = String.Format( "{0} {1}"
, s.People.Name, s.People.Surname ) } ) )
}).ToList();
You should investigate step by step.
Here a example withtwo dedicated joins and a groupBy (which is missing in your case):
public class People
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
public class Appointment
{
public int Id { get; set; }
public string Subject { get; set; }
}
public class Participation
{
public int Id { get; set; }
public int PeopleId { get; set; }
public int AppointmentId { get; set; }
}
static void Main(string[] args)
{
// Example-Data (Would have helped in this format ;))
List<People> people = new List<People>()
{
new People() { Id = 1, Surname = "David1", Name = "David"},
new People() { Id = 2, Surname = "Ameyy2", Name = "Ameyy"}
};
List<Appointment> appointments = new List<Appointment>()
{
new Appointment() { Id = 1, Subject = "description" }
};
List<Participation> participations = new List<Participation>()
{
new Participation() { Id = 1, PeopleId = 1, AppointmentId = 1 },
new Participation() { Id = 1, PeopleId = 2, AppointmentId = 1 }
};
Console.WriteLine("***** JOIN appointment to participation *****");
// At the beginning we want to join the table 'Appointment' with the n-to-n-Table "Participation".
var AppointmentsAndParticipations = appointments.Join
(
participations, // Other table to connect
a => a.Id, // Key in 1st table
p => p.AppointmentId, // Key in 2nd table
(a, p) => new { Appointment = a, PeopleId = p.PeopleId } // build new row
);
foreach (var item in AppointmentsAndParticipations)
{
// The result should be out appointment and the peopleId. We got "Appointment.Count*Participations.Count" rows
Console.WriteLine(item.Appointment.Id.ToString().PadLeft(5) + ", " + item.Appointment.Subject.PadLeft(15) + ", " + item.PeopleId);
}
Console.WriteLine("***** JOIN people *****");
// We need to join the people which belong to the Ids in participation
var AppointmentsAndPeople = AppointmentsAndParticipations.Join
(
people, a => a.PeopleId, // Similar to 1st join...
p => p.Id,
(a, p) => new { Appointment = a.Appointment, People = p }
);
foreach (var item in AppointmentsAndPeople)
{
Console.WriteLine(item.Appointment.Id.ToString().PadLeft(5) + ", " + item.Appointment.Subject.PadLeft(15) + ", " + item.People.Name + " " + item.People.Surname);
}
Console.WriteLine("***** Group the rows *****");
// Now we want to group the rows back to Appointment-Level. We group by Appointment and People will be out elements to be sum-up
var AppointmentPools = AppointmentsAndPeople.GroupBy
(
key => key.Appointment, // Select the 'column' which shall be the keys
group => group.People, // Select the 'column' which will be converted to a single value (like count, sum, max ..or in your case string.join())
(key, group) => new // Build the output object..
{
Id = key.Id,
Subject = key.Subject,
Participants = string.Join(", ", group.Select(s => s.Name + " " + s.Surname))
}
);
foreach (var item in AppointmentPools)
{
Console.WriteLine("Id: " + item.Id + ", Subject: " + item.Subject + ", Participants: " + item.Participants);
}
}

How to join Enum with LINQ Query

I have user table (Default ApplicationUser Table from IdentityUser by ASP.CORE)
and I have added additional field for RoleType. There is also an Enum I added to specify Role Definition.
public enum Roles
{
Administrator = 1,
Headquarters = 2,
Branch = 3,
Driver = 4,
Client = 5
}
Now I want to show all the users in a view as a table along with role description.
I am unable to make LINQ query with Enum & User table using LINQ join.
To get the list of Roles from the enum use:
var roles = Enum.GetValues(typeof(Roles)).Cast<Roles>()
.Select(r => new { Value = (int)r, Name = r.ToString() }).ToList();
you can then use this in your Linq query, for example:
var roles = Enum.GetValues(typeof(Roles)).Cast<Roles>()
.Select(r => new { Value = (int)r, Name = r.ToString() }).ToList();
var users = from u in ApplicationUser
join r in roles on u.Role equals r.Value
select new {Name = u.Name, RoleId = u.Role, RoleDescription = r.Name} ;
A simpler way without the Enum.GetValues is:
var users = from u in ApplicationUser
select new {Name = u.Name, RoleId = u.Role, RoleDescription = (Roles)r.Role.ToString()} ;
var xx = from u in _context.Users
.Select(x => new ApplicationUserList
{ Firstname = x.Firstname,
RoleType = ((Roles)x.RoleId).ToString()
});
// This join is performed in memory
var results =
from e in Enum.GetValues(typeof(Roles)).Cast<Roles>()
join r in ApplicationUser on e equals r.Roles into rs
from r in rs.DefaultIfEmpty()
select new { Roles = e, Count = r?.Count ?? 0};
If I understand your question, you should first convert enum to dictionary an Join between what you need, here is an example:
static void Main(string[] args)
{
ApplicationUser a = new ApplicationUser();
a.userName = "a";
a.role = 1;
ApplicationUser b = new ApplicationUser();
b.userName = "b";
b.role = 3;
List<ApplicationUser> alist=new List<ApplicationUser>();
alist.Add(a);
alist.Add(b);
Dictionary<int, string> DicRoles = new Dictionary<int, string>();
var vals = Enum.GetValues(typeof(Roles));
foreach (var val in vals)
{
DicRoles.Add((int)val, val.ToString());
}
var result = from t in alist
join x in DicRoles on t.role equals x.Key
select new {t.userName,x.Value };
}
public enum Roles:int
{
Administrator = 1,
Headquarters = 2,
Branch = 3,
Driver = 4,
Client = 5
}
}
public class ApplicationUser
{
public string userName { get; set; }
public int role { get; set; }
}

C# linq group by on different keys in the same entity

I've been toying with this for a while and just can't get it. I'm new to Linq, C# and these Lambda things.
What I want to do is group entities according to two properties on each entity. It's a Message entity:
Message
{
int UserId; //The user generating the message
int UserIdTo; //The receiver of the message
|...| // Other stuff
}
So, I want it so that these UserId=5, UserIdTo=6 and UserId=6, UserIdTo=5 would be in the same group.
Here's my start:
var groupList = (from m in db.Messages
where m.UserId == userId || m.UserIdTo == userId
join u in db.Users on m.UserId equals u.UserId
join w in db.Users on m.UserIdTo equals w.UserId
orderby m.MessageTimestamp descending
select new DomMessage
{
MessageId = m.MessageId,
MessageContent = m.MessageContent,
MessageTimestamp = m.MessageTimestamp,
UserId = m.UserId,
UserIdTo = m.UserIdTo,
ScreenName = u.ScreenName,
ScreenName2 = w.ScreenName
}).GroupBy(m=>m.UserId == userId)
.ToList();
This does the first bit of grouping by UserId, but I'm stuck on trying to extend this so that where any UserId value in the resulting group equals the UserIdTo somewhere else add that to this group?
EDIT: I need the result to go to a List because there is other stuff I need to do with it...
Thanks!
Try this:
var payload = new[]
{
new{ To = 1, From = 2, Message = "msj1" },
new{ To = 1, From = 2, Message = "msj2" },
new{ To = 2, From = 1, Message = "msj3" },
new{ To = 4, From = 1, Message = "msj4" },
new{ To = 1, From = 3, Message = "msj5" }
};
var groupped = payload.Select(x => new { Key = Math.Min(x.To, x.From) + "_" + Math.Max(x.To, x.From), Envelope = x }).GroupBy(y => y.Key).ToList();
foreach (var item in groupped)
{
Console.WriteLine(String.Format(#"Group: {0}, messages:", item.Key));
foreach (var element in item)
{
Console.WriteLine(String.Format(#"From: {0} To: {1} Message: {2}", element.Envelope.From, element.Envelope.To, element.Envelope.Message));
}
}
Try the following GroupBy expression:
.GroupBy(m => Math.Min(m.UserId, m.UserIdTo) + ',' + Math.Max(m.UserId, m.UserIdTo))
I think this is the easiest way:
var groupList = from a in (
from m in db.Messages
where m.UserId == userId || m.UserIdTo == userId
join u in db.Users on m.UserId equals u.UserId
join w in db.Users on m.UserIdTo equals w.UserId
select new
{
MessageId = m.MessageId,
MessageContent = m.MessageContent,
MessageTimestamp = m.MessageTimestamp,
UserId = m.UserId,
UserIdTo = m.UserIdTo,
ScreenName = u.ScreenName,
ScreenName2 = w.ScreenName
})
group a by new {
UserId = a.UserId,
UserIdTo = a.UserIdTo
} into grp
orderby grp.Max(a => a.MessageTimestamp) descending
select new
{
UserId = grp.Key.UserId
UserIdTo = grp.Key.UserIdTo,
MessageId = grp.Max(a => a.MessageId),
MessageContent = grp.Max(a => a.MessageContent),
MessageTimestamp = grp.Max(a => a.MessageTimestamp),
ScreenName = grp.Max(a => a.ScreenName),
ScreenName2 = grp.Max(a => a.ScreenName2)
}
You have to tell it what to do with the fields you are not grouping by. In this case I got the MAX value for each.

Hierarchical layering in Linq

If I have a set of employee data similar to:
var users = new[]
{
new {SupervisorId = "CEO", UserId = "CEO", UserName = "Joe"},
new {SupervisorId = "CEO", UserId = "CIO", UserName = "Mary"},
new {SupervisorId = "CIO", UserId = "XDIR", UserName = "Ed"},
new {SupervisorId = "CIO", UserId = "YDIR", UserName = "Lisa"},
new {SupervisorId = "XDIR", UserId = "AMNGR", UserName = "Steve"},
new {SupervisorId = "AMNGR", UserId = "ASUP", UserName = "Lesley"}
};
Would it be possible to use Linq to add hierarchical layers, in the sense that:
CEO = 1 (top)
CIO = 2 (2nd level)
XDIR and YDIR = 3 (3rd level)
AMNGR = 4 (etc)
ASUP = 5 (etc)
I've been able to group the employees according to SupervisorId, but not sure how to make the "level" happen.
var userGroups = from user in users
group user by user.SupervisorId into userGroup
select new
{
SupervisorId = userGroup.Key,
Level = ??????
Users = userGroup.ToList()
};
foreach (var group in userGroups)
{
Console.WriteLine("{0} - {1} - {2}", group.SupervisorId, group.Level, group.Users.Count);
}
Many thanks.
I would add a ranking to your linq "user object"
public class User{
public string SupervisorId {get;set;}
public string UserId {get;set;}
public string UserName {get;set;}
public int Level {get { return GetRank(SupervisorId ) ; } }
private int GetRank(string userId){
if(string.IsNullOrEmpty(userId)){
//Bad case, probably want to use a very large number
return -1;
}
int level = 0;
switch(userId){
case "CEO":
level = 0;
break;
//insert others here
}
}
}
Then your Linq you would add a join.
var userGroups = from user in users
join super in users on user.SupervisorId equals super.UserId
group user by user.SupervisorId into userGroup
select new
{
SupervisorId = userGroup.Key,
Level = super.Level,
Users = userGroup.ToList()
};
ILookup<string, User> subordLookup = users
.ToLookup(u => u.SupervisorId);
foreach(User user in users)
{
user.Subordinates = subordLookup[user.UserId].ToList();
}
User userHierarchy = user.Single(u => u.UserId == "CEO");
Disclaimers:
Does not handle multiple CEOs.
Preserves circular relationships.
Leaves orphans behind.
Is this what you are looking for?
var levels = new[]
{
new { Level = 1, LevelName = "CEO" },
new { Level = 2, LevelName = "CIO" },
new { Level = 3, LevelName = "XDIR" },
new { Level = 3, LevelName = "YDIR" },
new { Level = 4, LevelName = "AMNGR" },
new { Level = 5, LevelName = "ASUP" }
};
var userGroups = from user in users
join level in levels on
user.UserId equals level.LevelName
group new{ User = user, Level = level.Level } by new { SuperId = user.SupervisorId, Level = level.Level } into userGroup
select new
{
SupervisorId = userGroup.Key.SuperId,
Level = userGroup.Key.Level,
Users = userGroup.ToList()
};
Update
Heres one way to create a lookup table for each level. Its fairly and I dont know how it will scale. Obviously, you'll need to adapt it to pull the rows from your database.
Define a class to hold our lookup table
public class user{
public string SupervisorId;
public string UserId;
public int Level;
}
Then we get a unique list of UserId/SupervisorId combinations and loop through the list calculating the level for each combination by 'walking' up the tree.
var uniqueusers = (new user[]
{
new user {SupervisorId = "CEO", UserId = "CEO"},
new user {SupervisorId = "CEO", UserId = "CIO"},
new user {SupervisorId = "CIO", UserId = "XDIR"},
new user {SupervisorId = "CIO", UserId = "YDIR"},
new user {SupervisorId = "XDIR", UserId = "AMNGR"},
new user {SupervisorId = "AMNGR", UserId = "ASUP"}
}).Distinct();
foreach (var item in uniqueusers)
{
int level = 0;
user CurrentUser = item;
while (CurrentUser.UserId != CurrentUser.SupervisorId){
CurrentUser = uniqueusers.Where(c => c.UserId == CurrentUser.SupervisorId).FirstOrDefault();
level++;
}
item.Level = level;
}
Now you can use the uniqueusers as a lookup table to determine the level for your query. eg
private int GetLevel(string userId){
return uniqueusers.Where(c => c.UserId == userId).FirstOrDefault().Level;
}
You could probably even combine this into a single step with a little effort.

Categories

Resources