Doing multiple joins within a LINQ statement - c#

Can someone help me translate the following SQL query into a LINQ format.
SELECT a.ID,
a.HostID,
h.URL,
a.SourceURL,
a.TargetURL,
c.Value,
a.ExtFlag
FROM Link a
INNER JOIN Host h
ON h.ID = a.HostID
INNER JOIN Ref c
ON a.ResponseCode = c.SubType
AND c.Type = 'HTTP Status'
Many Thanks

I think it would be something like:
var result = from a in Context.DGApprovedLink
join h in Context.DGHost on a.HostID equals h.ID
join c in Context.DGConfig on a.ResponseCode equals c.SubType
where c.Type == "HTTP Status"
select new {
a.ID,
a.HostID,
h.URL,
a.SourceURL,
a.TargetURL,
c.Value,
a.ExtFlag };

Create Unit test class using MStest and copy the code
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace LinqTest.Test
{
public class Employee
{
public int EmpId { get; set; }
public string Name { get; set; }
public DateTime? DOB { get; set; }
public decimal Salary { get; set; }
public DateTime DOJ { get; set; }
public bool IsActive { get; set; }
}
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public double Price { get; set; }
}
public class BookOrder
{
public int OrderId { get; set; }
public int EmpId { get; set; }
public int BookId { get; set; }
public int Quantity { get; set; }
}
[TestClass]
public class Linqtest
{
List<Employee> Employees;
List<Book> Books;
List<BookOrder> Orders;
[TestInitialize]
public void InitializeData()
{
Employees = new List<Employee>();
Books = new List<Book>();
Orders = new List<BookOrder>();
Employees.Add(new Employee(){EmpId = 1, Name ="Test1" , DOB = new DateTime(1980,12,15),IsActive = true,Salary = 4500});
Employees.Add(new Employee() { EmpId = 11, Name = "Test2", DOB = new DateTime(1981, 12, 15), IsActive = true, Salary = 3500 });
Employees.Add(new Employee() { EmpId = 5, Name = "Test3", DOB = new DateTime(1970, 2, 15), IsActive = true, Salary = 5500 });
Employees.Add(new Employee() { EmpId = 8, Name = "Test4", DOB = new DateTime(1978, 1, 15), IsActive = true, Salary = 7500 });
Employees.Add(new Employee() { EmpId = 9, Name = "Test5", DOB = new DateTime(1972, 2, 5), IsActive = true, Salary = 2500 });
Employees.Add(new Employee() { EmpId = 10, Name = "Test6", DOB = new DateTime(1980, 10, 8), IsActive = false, Salary = 5500 });
Employees.Add(new Employee() { EmpId = 15, Name = "Test7", DOB = new DateTime(1983, 11, 25), IsActive = true, Salary = 3500 });
Books.Add(new Book(){BookId = 2, Price = 24.99,Title = "British Food"});
Books.Add(new Book() { BookId = 5, Price = 4.99, Title = "Holidays in UK" });
Books.Add(new Book() { BookId = 7, Price = 7.99, Title = "UK Laws" });
Orders.Add(new BookOrder(){EmpId = 1,OrderId = 1,BookId = 2,Quantity = 3});
Orders.Add(new BookOrder() { EmpId = 1, OrderId = 1, BookId = 5, Quantity = 1 });
Orders.Add(new BookOrder() { EmpId = 1, OrderId = 2, BookId = 7, Quantity = 5 });
Orders.Add(new BookOrder() { EmpId = 11, OrderId = 3, BookId = 2, Quantity = 3 });
Orders.Add(new BookOrder() { EmpId = 11, OrderId = 4, BookId = 7, Quantity = 3 });
}
[TestMethod]
public void CheckEmpCount()
{
var res = Employees
.Where(e => e.EmpId > 5)
.Where(t =>t.Salary>=5000);
Assert.AreEqual(2,res.Count());
res = Employees
.Where(e => e.EmpId > 5);
Assert.AreEqual(5,res.Count());
}
[TestMethod]
public void TestGroupBy()
{
var res = from e in Employees
group e by e.Salary;
Assert.AreEqual(5,res.Count());
var res1 = Employees.GroupBy(e => e.Salary);
Assert.AreEqual(5, res1.Count());
}
[TestMethod]
public void TestJoin()
{
var res = from o in Orders
join Employee e in Employees
on o.EmpId equals e.EmpId
where o.EmpId == 11
select o;
Assert.AreEqual(2,res.Count());
}
[TestMethod]
public void TestJoinData()
{
var res = from o in Orders
join Employee e in Employees
on o.EmpId equals e.EmpId
join Book b in Books
on o.BookId equals b.BookId
orderby e.EmpId
select new {o.OrderId, e.Name, b.Title, b.Price};
Assert.AreEqual("Test1", res.First().Name);
}
}
}

Related

linq query to get the latest record with default if empty returning list

The following is a complete code sample
static class Program {
public static List<User> Users = new List<User>();
public static List<Claim> Claims = new List<Claim>();
public static List<Valuation> Valuations = new List<Valuation>();
public static List<Status> Statuses = new List<Status>();
public static List<StatusHistory> StatusHistories = new List<StatusHistory>();
static void Main()
{
DataSeed();
var claims = GetClaims(true,0,DateTime.MinValue,DateTime.MaxValue);
Console.Write(claims);
}
static List<ClaimDto> GetClaims(bool active,int userId, DateTime fromDate, DateTime toDate){
var claimsInDb = (from status in Program.Statuses
join statusHistory in Program.StatusHistories on status.Id equals statusHistory.StatusId
join claim in Program.Claims on statusHistory.ClaimId equals claim.Id
join valuation in Program.Valuations on claim.ActiveEvaluationId equals valuation.Id
join user in Program.Users on claim.AssignedTo equals user.Id into joined
from joinedUser in joined.DefaultIfEmpty()
where status.Active & active
? status.Name != "Complete"
: status.Name == "Complete"
& statusHistory.CreatedDt > fromDate & statusHistory.CreatedDt < toDate
& userId == 0
? joinedUser.Id != -1
: joinedUser.Id == userId
group new ClaimDto
{
Id = claim.Id,
AssignedToUserFullName = joinedUser.FullName,
CreatedDt = claim.CreatedDt,
Status = status.Name,
Version = valuation.Version
}
by new
{
claim.Id
}
into grouped
//where active
// ? grouped.Key.status.Name != "Complete"
// : grouped.Key.status.Name == "Complete"
// & grouped.Key.statusHistory.CreatedDt > fromDate& grouped.Key.statusHistory.CreatedDt < toDate
// & userId == 0
// ? grouped.Key.joinedUser.Id != 0
// : grouped.Key.joinedUser.Id == userId
//orderby grouped.Key.statusHistory.CreatedDt descending
select grouped.FirstOrDefault()
).ToList();
return claimsInDb;
}
static void DataSeed(){
//dataSeed
Users.Add(new User
{
Id = 1,
FullName = "Dmitry Post",
});
Claims.AddRange(new List<Claim>
{
new Claim
{
Id = 1,
AssignedTo = 1,
CreatedDt = DateTime.Now,
ActiveEvaluationId = 1
},
new Claim
{
Id = 2,
AssignedTo = 1,
CreatedDt = DateTime.Now,
ActiveEvaluationId = 2
}
});
Valuations.AddRange(new List<Valuation>
{
new Valuation
{
Id = 1,
Version = 1,
ClaimId = 1
},
new Valuation
{
Id = 2,
Version = 1,
ClaimId = 2
}
});
Statuses.AddRange(new List<Status>
{
new Status
{
Id = 1,
Name = "New",
Active = true,
},
new Status
{
Id = 2,
Name = "In Progress",
Active = true,
},
new Status
{
Id = 3,
Name = "Complete",
Active = true,
}
});
StatusHistories.AddRange(new List<StatusHistory>
{
new StatusHistory
{
Id = 1,
ClaimId = 1,
CreatedDt = DateTime.Now.AddDays(-10),
StatusId = 1
},
new StatusHistory
{
Id = 2,
ClaimId = 1,
CreatedDt = DateTime.Now.AddDays(-1),
StatusId = 2
},
new StatusHistory
{
Id = 3,
ClaimId = 2,
CreatedDt = DateTime.Now.AddDays(-10),
StatusId = 1
},
new StatusHistory
{
Id = 4,
ClaimId = 2,
CreatedDt = DateTime.Now.AddDays(-9),
StatusId = 2
},
new StatusHistory
{
Id = 5,
ClaimId = 2,
CreatedDt = DateTime.Now.AddDays(-8),
StatusId = 3
},
});
}
}
//objects
class ClaimDto{
public int Id {get;set;}
public DateTime CreatedDt {get;set;}
public string Status {get;set;}
public int Version {get;set;}
public string AssignedToUserFullName {get;set;}
}
class Status{
public int Id {get;set;}
public string Name {get;set;}
public bool Active {get;set;}
}
class StatusHistory{
public int Id {get;set;}
public int StatusId {get;set;}
public int ClaimId {get;set;}
public DateTime CreatedDt {get;set;}
}
class Claim{
public int Id {get;set;}
public int ActiveEvaluationId {get;set;}
public DateTime CreatedDt {get;set;}
public int AssignedTo {get;set;}
}
class Valuation{
public int Id {get;set;}
public int Version {get;set;}
public int ClaimId {get;set;}
}
class User{
public int Id {get;set;}
public string FullName {get;set;}
}
My desired result is when active = true, userId = 0, fromDate = DateTime.MinValue, toDate = DateTime.MaxValue
To return 1 Record:
One because there is one claim that has status history of complete.
When active = false, userId = 0, fromDate = DateTime.MinValue, toDate = DateTime.MaxValue
To return 1 Record:
One because there is one claim that has no status histories of complete.
When active = false, userId = 1, fromDate = DateTime.Now.AddDays(-5), toDate = DateTime.MaxValue
To return 1 Record:
One because the first claims latest status history CreateDt is within the date range and the claim is assigned to user with that Id
Problem solved by adding property to Valuation class for the currentStatusHistory id and adjusting the join accordingly.
static class Program {
public static List<User> Users = new List<User>();
public static List<Claim> Claims = new List<Claim>();
public static List<Valuation> Valuations = new List<Valuation>();
public static List<Status> Statuses = new List<Status>();
public static List<StatusHistory> StatusHistories = new List<StatusHistory>();
static void Main()
{
DataSeed();
var claims = GetClaims(true,0,DateTime.MinValue,DateTime.MaxValue);
Console.Write(claims);
}
static List<ClaimDto> GetClaims(bool active,int userId, DateTime fromDate, DateTime toDate){
var claimsInDb = (from status in Program.Statuses
join statusHistory in context.StatusHistories on status.StatusId equals statusHistory.StatusId
join valuation in context.Valuations on statusHistory.StatusHistoryId equals valuation.StatusHistoryId
join claim in context.Claims on statusHistory.ClaimId equals claim.ClaimId
join user in context.Users on claim.AssignedTo equals user.UserId into joined
from joinedUser in joined.DefaultIfEmpty()
where status.Active & active
? status.Name != "Complete"
: status.Name == "Complete"
& statusHistory.CreatedDt > fromDate & statusHistory.CreatedDt < toDate
& userId == 0
? joinedUser.Id != -1
: joinedUser.Id == userId
group new ClaimDto
{
Id = claim.Id,
AssignedToUserFullName = joinedUser.FullName,
CreatedDt = claim.CreatedDt,
Status = status.Name,
Version = valuation.Version
}
by new
{
claim.Id
}
into grouped
select grouped.FirstOrDefault()
).ToList();
return claimsInDb;
}
static void DataSeed(){
//dataSeed
Users.Add(new User
{
Id = 1,
FullName = "Dmitry Post",
});
Claims.AddRange(new List<Claim>
{
new Claim
{
Id = 1,
AssignedTo = 1,
CreatedDt = DateTime.Now,
ActiveEvaluationId = 1
},
new Claim
{
Id = 2,
AssignedTo = 1,
CreatedDt = DateTime.Now,
ActiveEvaluationId = 2
}
});
Valuations.AddRange(new List<Valuation>
{
new Valuation
{
Id = 1,
Version = 1,
ClaimId = 1,
StatusHistoryId = 2,
},
new Valuation
{
Id = 2,
Version = 1,
ClaimId = 2,
StatusHistoryId = 5,
}
});
Statuses.AddRange(new List<Status>
{
new Status
{
Id = 1,
Name = "New",
Active = true,
},
new Status
{
Id = 2,
Name = "In Progress",
Active = true,
},
new Status
{
Id = 3,
Name = "Complete",
Active = true,
}
});
StatusHistories.AddRange(new List<StatusHistory>
{
new StatusHistory
{
Id = 1,
ClaimId = 1,
CreatedDt = DateTime.Now.AddDays(-10),
StatusId = 1
},
new StatusHistory
{
Id = 2,
ClaimId = 1,
CreatedDt = DateTime.Now.AddDays(-1),
StatusId = 2
},
new StatusHistory
{
Id = 3,
ClaimId = 2,
CreatedDt = DateTime.Now.AddDays(-10),
StatusId = 1
},
new StatusHistory
{
Id = 4,
ClaimId = 2,
CreatedDt = DateTime.Now.AddDays(-9),
StatusId = 2
},
new StatusHistory
{
Id = 5,
ClaimId = 2,
CreatedDt = DateTime.Now.AddDays(-8),
StatusId = 3
},
});
}
}
//objects
class ClaimDto{
public int Id {get;set;}
public DateTime CreatedDt {get;set;}
public string Status {get;set;}
public int Version {get;set;}
public string AssignedToUserFullName {get;set;}
}
class Status{
public int Id {get;set;}
public string Name {get;set;}
public bool Active {get;set;}
}
class StatusHistory{
public int Id {get;set;}
public int StatusId {get;set;}
public int ClaimId {get;set;}
public DateTime CreatedDt {get;set;}
}
class Claim{
public int Id {get;set;}
public int ActiveEvaluationId {get;set;}
public DateTime CreatedDt {get;set;}
public int AssignedTo {get;set;}
}
class Valuation{
public int Id {get;set;}
public int Version {get;set;}
public int ClaimId {get;set;}
public int StatusHistoryId {get;set;}
}
class User{
public int Id {get;set;}
public string FullName {get;set;}
}

Recursive query in EntityFramework throws NotSupportedException

I want to build a recursive query in EntityFramework 6.2.0 to get an employee and all his "direct" (one level) and all other subordinates down all the hierarchy.
My point was to use List<IQueryable<T>> to build a whole query and then run it just one time with one rount-trip to the database.
Here is my attempt to do it:
private static List<IQueryable<Employee>> queryables = new List<IQueryable<Employee>>();
static void Main(string[] args)
{
using (var db = new EmployeeContext())
{
IQueryable<Employee> managers = db.Employee.Where(x => x.Id == 1);
GetSlaves(managers);
// the System.NotSupportedException occurs in this line
IQueryable<Employee> employees = queryables.Aggregate(Queryable.Union);
// but throws here
var res = (
from e in employees
join d in db.EmployeeDoc on e.Id equals d.EmployeeId
select new { e.Id, e.EmployeeName, d.DocNumber }).ToList();
}
Console.ReadLine();
}
static void GetSlaves(IQueryable<Employee> managers)
{
if (managers != null)
{
queryables.Add(managers);
foreach (var m in managers)
{
Console.WriteLine($"{m.Id} {m.EmployeeName} {m.Position}");
GetSlaves(m.Slaves.AsQueryable());
}
}
}
But I get an System.NotSupportedException: 'Unable to create a constant value of type 'EF6.Employee'. Only primitive types or enumeration types are supported in this context.'
The above C# code was an attempt to replace the following SQL code:
declare #managerId int = 1
;with Employees(Id, EmployeeName)
as
(
select e.Id, e.EmployeeName from Employee as e
where Id = #managerId
union all
select e.Id, e.EmployeeName from Employee as e
inner join Employees as em on e.ManagerId = em.Id
)
select e.Id, e.EmployeeName, d.DocNumber
from Employees e
inner join EmployeeDocuments d on e.Id = d.EmployeeId
UPDATED:
Here is a SQL script that I use:
create table Employee
(
Id int not null identity(1,1) primary key,
EmployeeName varchar(20),
Position varchar(30),
ManagerId int constraint FK_Employee foreign key references Employee(Id)
)
insert into Employee (EmployeeName, Position, ManagerId) values
('John', 'CEO', NULL),
('Marry', 'Head of sales division', 1),
('Mike', 'Head of HR division', 1),
('Jack', 'Sales manager', 2),
('Olivia', 'Sales manager', 2),
('Sophia', 'Sales manager', 2),
('Nadya', 'HR manager', 3),
('Tim', 'HR manager', 3),
('Jim', 'Salesman', 4),
('Sergey', 'Salesman', 4),
('Dmitry', 'Salesman', 5),
('Irina', 'Salesman', 5),
('William', 'Assistant', 8)
select * from Employee
Create table EmployeeDocuments
(
Id int not null identity(1,1) primary key,
DocNumber varchar(20),
EmployeeId int not null constraint FK_Docs_Employee foreign key references Employee(Id)
)
insert into EmployeeDocuments (DocNumber, EmployeeId) values
('1/2019-01-15', 1), ('3/2019-02-25', 3), ('4/2019-01-31', 4), ('9/2019-02-28', 9)
select * from EmployeeDocuments
Here is Employee class:
[Table("Employee")]
public partial class Employee
{
public Employee()
{
Slaves = new HashSet<Employee>();
}
public int Id { get; set; }
[StringLength(20)]
public string EmployeeName { get; set; }
[StringLength(30)]
public string Position { get; set; }
public int? ManagerId { get; set; }
public virtual ICollection<Employee> Slaves { get; set; }
public virtual Employee Manager { get; set; }
public virtual ICollection<EmployeeDoc> EmployeeDocs { get; set; }
}
The error is due to the query being static. Not sure why you need a query when the code is very simple. See below
class Program
{
static void Main(string[] args)
{
Employee.GetSlaves();
}
}
public partial class Employee
{
public static List<Employee> employees = new List<Employee>() {
new Employee() {Id = 1, EmployeeName = "John", Position = "CEO", ManagerId = null},
new Employee() {Id = 2,EmployeeName = "Marry", Position = "Head of sales division", ManagerId = 1},
new Employee() {Id = 3,EmployeeName = "Mike", Position = "Head of HR division", ManagerId = 1},
new Employee() {Id = 4,EmployeeName = "Jack", Position = "Sales manager", ManagerId = 2},
new Employee() {Id = 5,EmployeeName = "Olivia", Position = "Sales manager", ManagerId = 2},
new Employee() {Id = 6,EmployeeName = "Sophia", Position = "Sales manager", ManagerId = 2},
new Employee() {Id = 7,EmployeeName = "Nadya", Position = "HR manager", ManagerId = 3},
new Employee() {Id = 8,EmployeeName = "Tim", Position = "HR manager", ManagerId = 3},
new Employee() {Id = 9,EmployeeName = "Jim", Position = "Salesman", ManagerId = 4},
new Employee() {Id = 10,EmployeeName = "Sergey", Position = "Salesman", ManagerId = 4},
new Employee() {Id = 11,EmployeeName = "Dmitry", Position = "Salesman", ManagerId = 5},
new Employee() {Id = 12,EmployeeName = "Irina", Position = "Salesman", ManagerId = 5},
new Employee() {Id = 13,EmployeeName = "William", Position = "Assistant", ManagerId = 8}
};
public static void GetSlaves()
{
Employee ceo = employees.Where(x => x.ManagerId == null).First();
GetSlavesRecursive(ceo);
}
public int Id { get; set; }
public string EmployeeName { get; set; }
public string Position { get; set; }
public int? ManagerId { get; set; }
public virtual ICollection<Employee> Slaves { get; set; }
public virtual Employee Manager { get; set; }
//public virtual ICollection<EmployeeDoc> EmployeeDocs { get; set; }
static void GetSlavesRecursive(Employee manager)
{
manager.Slaves = employees.Where(x => x.ManagerId == manager.Id).ToList();
foreach (Employee slave in manager.Slaves)
{
GetSlavesRecursive(slave);
}
}
}

LINQ group list and combine collections

Given these classes:
public class Employee
{
public int EmployeeId { get; set; }
public int GroupId { get; set; }
public List<Plans> Plans { get; set; }
}
public class Plan
{
public int PlanYearId { get; set; }
public string Name { get; set; }
}
And given a setup like so:
var employees = new List<Employee> {
new Employee {
EmployeeId = 1,
GroupId = 1,
Plans = new List<Plan> {
new Plan {
PlanReferenceId = 1111,
Name = "Benefit 1"
}}};
new Employee {
EmployeeId = 1,
GroupId = 1,
Plans = new List<Plan> {
new Plan {
PlanReferenceId= 2222,
Name = "Benefit 2"
},
new Plan {
PlanReferenceId= 2222,
Name = "Benefit 3"
}}}};
How can I use LINQ to group these employees by both EmployeeId and GroupId and then combine the two List<Plan> properties so that i would end up with something like this:
var employee = new Employee
{
EmployeeId = 1,
GroupId = 1,
Plans = new List<Plan> {
new Plan {
PlanReferenceId = 1111,
Name = "Benefit 1"
},
new Plan {
PlanReferenceId = 2222,
Name = "Benefit 2"
},
new Plan {
PlanReferenceId = 2222,
Name = "Benefit 3"
}
}
}
Just use combination of GroupBy and SelectMany:
var result = employees
.GroupBy(e => new { e.EmployeeId, e.GroupId })
.Select(g => new Employee
{
EmployeeId = g.Key.EmployeeId,
GroupId = g.Key.GroupId,
Plans = g.SelectMany(e => e.Plans).ToList()
}).ToList();

How to Use Effeciently Where Clause or Select in LINQ Parallel in Large Dataset

I'm having approx 250,000 records as marked as Boss, each Boss has 2 to 10 Staff. Daily I need to get the details of the Staff. Approx there are 1,000,000 staff. I'm using Linq to get the Unique list of Staff who are worked in daily basis. Consider the following C# LINQ and Models
void Main()
{
List<Boss> BossList = new List<Boss>()
{
new Boss()
{
EmpID = 101,
Name = "Harry",
Department = "Development",
Gender = "Male",
Employees = new List<Person>()
{
new Person() {EmpID = 102, Name = "Peter", Department = "Development",Gender = "Male"},
new Person() {EmpID = 103, Name = "Emma Watson", Department = "Development",Gender = "Female"},
}
},
new Boss()
{
EmpID = 104,
Name = "Raj",
Department = "Development",
Gender = "Male",
Employees = new List<Person>()
{
new Person() {EmpID = 105, Name = "Kaliya", Department = "Development",Gender = "Male"},
new Person() {EmpID = 103, Name = "Emma Watson", Department = "Development",Gender = "Female"},
}
},
..... ~ 250,000 Records ......
};
List<Person> staffList = BossList
.SelectMany(x =>
new[] { new Person { Name = x.Name, Department = x.Department, Gender = x.Gender, EmpID = x.EmpID } }
.Concat(x.Employees))
.GroupBy(x => x.EmpID) //Group by employee ID
.Select(g => g.First()) //And select a single instance for each unique employee
.ToList();
}
public class Person
{
public int EmpID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
}
public class Boss
{
public int EmpID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
public List<Person> Employees { get; set; }
}
In the above LINQ I'm getting the List of Distinct Employees or Staff, the list contains more than 1,000,000 records. From the Obtained List I need to search "Raj"
staffList.Where(m => m.Name.ToLowerInvariant().Contains("Raj".ToLowerInvariant()));
For this operation, it took more than 3 to 5 minutes to get the result.
How could I make it more efficient. Kindly assist me...
If you change Boss to inherit from Person ( public class Boss : Person ) not only do you not need to duplicate your properties in Person and Boss, you don't have to create all new Person instances for each Boss, because a Boss is already a Person:
IEnumerable<Person> staff = BossList
.Concat(BossList
.SelectMany(x => x.Employees)
)
.DistinctBy(p => p.EmpId)
.ToList()
Where DistinctByis defined as
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
var seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
Also, in your comparison, you're converting every Name to lowercase and doing the comparison - that's a lot of string creation that you don't need. Instead, try something like
staffList.Where(m => m.Name.Equals("Raj", StringComparison.InvariantCultureIgnoreCase));
Also, be aware that your use of Contains would also match names like Rajamussenand mirajii - possibly not what you were expecting.
Would it work for you to change staffList to a Dictionary? A better search algorithm as those from Dictionary and SortedList would get you the most improvement.
I've tested the code below and it runs in just a few seconds.
private static void Main()
{
List<Boss> BossList = new List<Boss>();
var b1 = new Boss()
{
EmpID = 101,
Name = "Harry",
Department = "Development",
Gender = "Male",
Employees = new List<Person>()
{
new Person() {EmpID = 102, Name = "Peter", Department = "Development", Gender = "Male"},
new Person() {EmpID = 103, Name = "Emma Watson", Department = "Development", Gender = "Female"},
}
};
var b2 = new Boss()
{
EmpID = 104,
Name = "Raj",
Department = "Development",
Gender = "Male",
Employees = new List<Person>()
{
new Person() {EmpID = 105, Name = "Kaliya", Department = "Development", Gender = "Male"},
new Person() {EmpID = 103, Name = "Emma Watson", Department = "Development", Gender = "Female"},
}
};
Random r = new Random();
var genders = new [] {"Male", "Female"};
for (int i = 0; i < 1500000; i++)
{
b1.Employees.Add(new Person { Name = "Name" + i, Department = "Department" + i, Gender = genders[r.Next(0, 1)], EmpID = 200 + i });
b2.Employees.Add(new Person { Name = "Nam" + i, Department = "Department" + i, Gender = genders[r.Next(0, 1)], EmpID = 1000201 + i });
}
BossList.Add(b1);
BossList.Add(b2);
Stopwatch sw = new Stopwatch();
sw.Start();
var emps = BossList
.SelectMany(x =>
new[] {new Person {Name = x.Name, Department = x.Department, Gender = x.Gender, EmpID = x.EmpID}}
.Concat(x.Employees))
.GroupBy(x => x.EmpID) //Group by employee ID
.Select(g => g.First());
var staffList = emps.ToList();
var staffDict = emps.ToDictionary(p => p.Name.ToLowerInvariant() + p.EmpID);
var staffSortedList = new SortedList<string, Person>(staffDict);
Console.WriteLine("Time to load staffList = " + sw.ElapsedMilliseconds + "ms");
var rajKeyText = "Raj".ToLowerInvariant();
sw.Reset();
sw.Start();
var rajs1 = staffList.AsParallel().Where(p => p.Name.ToLowerInvariant().Contains(rajKeyText)).ToList();
Console.WriteLine("Time to find Raj = " + sw.ElapsedMilliseconds + "ms");
sw.Reset();
sw.Start();
var rajs2 = staffDict.AsParallel().Where(kvp => kvp.Key.Contains(rajKeyText)).ToList();
Console.WriteLine("Time to find Raj = " + sw.ElapsedMilliseconds + "ms");
sw.Reset();
sw.Start();
var rajs3 = staffSortedList.AsParallel().Where(kvp => kvp.Key.Contains(rajKeyText)).ToList();
Console.WriteLine("Time to find Raj = " + sw.ElapsedMilliseconds + "ms");
Console.ReadLine();
}
public class Person
{
public int EmpID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
}
public class Boss
{
public int EmpID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Gender { get; set; }
public List<Person> Employees { get; set; }
}
}
Output1:
Output2 (using .AsParallel() on searches):
In other words, if you can't use some faster data structure, up can speed your search up just by changing form
staffList.Where(m => m.Name.ToLowerInvariant().Contains("Raj".ToLowerInvariant()));
to
staffList.AsParallel().Where(m => m.Name.ToLowerInvariant().Contains("Raj".ToLowerInvariant()));

Display only selected columns

suppose that I have this List of Employees representing a table of employees.
public class Employee
{
public string Name { get; set; }
public string Department { get; set; }
public string Function { get; set; }
public decimal Salary { get; set; }
public DateTime EntryDate { get; set; }
public static List<Employee> GetEmployeesList()
{
return new List<Employee>() {
new Employee() { EntryDate = new DateTime(2011, 05, 01), Name = "Fons", Department = "Finance", Function = "Trader", Salary = 6500 },
new Employee() { EntryDate = new DateTime(2013, 05, 02), Name = "Mary", Department = "Finance", Function = "BusinessAnalyst", Salary = 2500 },
new Employee() { EntryDate = new DateTime(2012, 04, 03), Name = "Alex", Department = "Finance", Function = "Trader", Salary = 2100 },
new Employee() { EntryDate = new DateTime(2013, 05, 04), Name = "Jim", Department = "R&D", Function = "Trainer", Salary = 3300 },
new Employee() { EntryDate = new DateTime(2010, 06, 05), Name = "Ellen", Department = "Dev", Function = "Developer", Salary = 2500 },
new Employee() { EntryDate = new DateTime(2000, 09, 06), Name = "Mike", Department = "Dev", Function = "Consultant", Salary = 5100 },
new Employee() { EntryDate = new DateTime(1999, 03, 07), Name = "Jack", Department = "R&D", Function = "Developer", Salary = 6100 },
new Employee() { EntryDate = new DateTime(1989, 01, 08), Name = "Demy", Department = "Dev", Function = "Consultant", Salary = 3300 }};
}
}
I want to be able to select only desired columns to be displayed.
Someting like :
public static List<Employee> SelectColumnsFromTable(List<Employee> employees, int[] selectedColumns)
{
// only select colums 1, 3 and 4
}
I have seen that it is possible with SQL and GridView, but in my case, the result will be printed on the console.
Is it possible using C# and Linq ?
As I understand the question, it is important to select specific properties of a class based on their index. If the relevant indices are provided to you by the user, you can use reflection to access the properties dynamically. The key points are Type.GetProperties and PropertyInfo.GetValue. I've put together a small sample to demonstrate:
using System;
using System.Collections.Generic;
using System.Linq;
public class Employee
{
public int Id {get; set;}
public string FirstName { get; set;}
public string LastName {get; set;}
}
public class Test
{
private static string[] GetColumnValues(Employee emp, params int[] cols)
{
var props = emp.GetType().GetProperties();
var values = new List<string>();
foreach(var i in cols)
{
if (i >= 0 && i < props.Length)
{
object value = props[i].GetValue(emp, null);
values.Add(value == null ? string.Empty : value.ToString());
}
}
return values.ToArray();
}
public static void Main()
{
var emp = new Employee() { Id = 1, FirstName = "John", LastName = "Smith" };
var values = GetColumnValues(emp, 0, 2);
Console.WriteLine(string.Join("\t", values));
}
}
Please note that referencing the properties by their index might not be very deterministic of you change the implementation later on. So selecting by the property's name might be more stable. Also, the column selector function GetColumnValues does not return Employees, but the values as a string array so you can use it in String.Join. You can use the function in Linq:
var rows = from x in listOfEmps select GetColumnValues(x, 0, 2);
foreach(var row in rows)
Console.WriteLine(string.Join("\t", row));
var items = (from i in v.db.DatabaseName
orderby i.EmpID descending
select new {i.Name, i.Function,i.Salary}).ToList();
var list = dt.AsEnumerable()
.Where(row => (int)row["demoid"] > 5)//your condition here
.Select(row => new
{
demoid = Convert.ToInt32(row["demoid"]),
demoname = row["demoname"] != null ?
row["demoname"].ToString() :
String.Empty
}).ToList();
Or you can define class:
public class myClass
{
public int demoid;
public string demoname;
}
and then:
List<myClass> list = dt.AsEnumerable()
.Where(row => (int)row["demoid"] > 5)
.Select(row => new myClass
{
demoid = Convert.ToInt32(row["demoid"]),
demoname = row["demoname"] != null ?
row["demoname"].ToString() :
String.Empty
}).ToList<myClass>();
this is selecting a particular value to a list. However you can use IList<myClass> classcollection= new List<myClass>(); and then add the particular list to class1 based on condition.
Note: here class collection can hold multiple list as u want the columns 1,3,4

Categories