The following linq query groups people in a city by their professions.
However the only way I've been able to get it to work is to hardcode the cities.
Each Person has a City. How can I modify the below query so that it groups by all the distinct cities that are returned in my query?
profession = people
.GroupBy(p => p.Profession).
Select(group =>
new
{
name = group.Key,
data =
new List<int>()
{
group.Count(p => p.City == "Adelaide"),
group.Count(p => p.City == "Brisbane"),
group.Count(p => p.City == "Canberra"),
group.Count(p => p.City == "Darwin"),
group.Count(p => p.City == "Melbourne"),
group.Count(p => p.City == "Perth"),
group.Count(p => p.City == "Sydney"),
},
})
Ie, if my data set was:
Person {id:1, city: "Paris" }
Person {id:2, city: "Paris" }
Person {id:3, city: "London" }
Then the resulting query would be:
profession = people
.GroupBy(p => p.Profession).
Select(group =>
new
{
name = group.Key,
data =
new List<int>()
{
group.Count(p => p.City == "Paris"),
group.Count(p => p.City == "London"),
},
})
This yields the same output as your hardcoded list, but I'm not sure if it's what you want (hint: giving us your expected output will help a lot in that regard).
It simply creates a distinct list of the cities, then builds a predicate delegate that counts the occurrence of each city, and adds that predicate to a list. When the actual grouping is performed, each predicate in the list is invoked with the result of the grouping.
The notation Func<IGrouping<string, Person>, int> simply says "this is a delegate that takes an IGrouping<string, Person> object (the result of calling GroupBy) and returns an int (the result of calling Count)".
class Program
{
static void Main(string[] args)
{
var people = new List<Person>()
{
new Person(1, "Paris", "carpenter"),
new Person(2, "Paris", "bricklayer"),
new Person(3, "London", "video game critic"),
};
var distinctCities = people.Select(p => p.City).Distinct();
var groupPredicates = new List<Func<IGrouping<string, Person>, int>>();
foreach (var city in distinctCities)
{
groupPredicates.Add(g => g.Count(p => p.City == city));
}
var professions = people
.GroupBy(p => p.Profession)
.Select(g =>
new
{
name = g.Key,
data = groupPredicates.Select(gp => gp(g)),
});
foreach (var profession in professions)
{
Console.WriteLine(profession.name + " =>");
foreach (var count in profession.data)
{
Console.WriteLine(" " + count);
}
}
Console.ReadKey(true);
}
}
struct Person
{
public int Id { get; set; }
public string City { get; set; }
public string Profession { get; set; }
public Person(int id, string city, string profession)
{
Id = id;
City = city;
Profession = profession;
}
}
Related
Here is the controller code i was trying for 2 different filters.!
index method--
public ActionResult Index(string sortOrder, string currentFilter, string searchString, int? page,string Course)
{
ViewBag.CurrentSort = sortOrder;
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
ViewBag.CurrentFilter = searchString;
//ViewBag.Courses = cd.tblStudents.Select(x => x.Course).ToList();
var students = from s in cd.tblStudents
select s;
ViewBag.Courses = students.Select(s => s.Course).Distinct();
ViewBag.Gender = students.Select(g => g.Gender).Distinct();
ViewBag.Levels = students.Select(g => g.Level).Distinct();
if (!String.IsNullOrEmpty(searchString))
{
students = students.Where(s => s.FirstName.Contains(searchString)
|| s.Lastname.Contains(searchString));
}
switch (sortOrder)
{
case "name_desc":
students = students.OrderByDescending(s => s.FirstName);
break;
case "Date":
students = students.OrderBy(s => s.DOJ);
break;
case "date_desc":
students = students.OrderByDescending(s => s.DOJ);
break;
default:
students = students.OrderBy(s => s.FirstName);
break;
}
return View(students.ToList().ToPagedList(page ?? 1, 8));
}
methods for filters mvc these filters are for getting gender of a student and also level he is studying in.
Code below can get only 1 filter.
public ActionResult GetStudentsByclass(string CourseName, int? page)
{
ViewBag.Courses = cd.tblStudents.Select(x => x.Course).Distinct();
ViewBag.Gender = cd.tblStudents.Select(g => g.Gender).Distinct();
ViewBag.Levels = cd.tblStudents.Select(g => g.Level).Distinct();
ViewBag.CurrentFilter = CourseName;
var prods = cd.tblStudents.Where(x => x.Course == CourseName).ToList();
return View("Index", prods.ToPagedList(page ?? 1, 8));
}
public ActionResult GetStudentsByGender(string Gender, int? page)
{
ViewBag.Courses = cd.tblStudents.Select(x => x.Course).Distinct();
ViewBag.Gender = cd.tblStudents.Select(g => g.Gender).Distinct();
ViewBag.Levels = cd.tblStudents.Select(g => g.Level).Distinct();
ViewBag.CurrentFilter = Gender;
var prods = cd.tblStudents.Where(x => x.Gender == Gender).ToList();
return View("Index", prods.ToPagedList(page ?? 1, 8));
}
--cshtml--- code--
<!-- single sidebar -->
<div class="aa-sidebar-widget">
<h3>Branches</h3>
<ul class="aa-catg-nav">
#foreach (var item in ViewBag.Courses)
{
<li>#item</li>
</ul>
</div>
<!-- single sidebar -->
<div class="aa-sidebar-widget">
<h3>Gender</h3>
<ul class="aa-catg-nav">
#foreach (var item in ViewBag.Gender)
{
<li>#item</li>}
</ul>
</div>
How to apply multiple filters at a single time from the above code.what should i add or change?
How can i check
"Female student of level senior" with filters
This is where i am doing the Query i am getting null values on using dynamic query
var responsedata = await _elasticClient.SearchAsync<Acquirer>(s => s
.Query(q => q.ConstantScore(cs => cs.Filter(
f =>
f.Term("MERCHANTNO.keyword", MerchantNo)
)))
);
This is my model
public class Acquirer
{
public string MERCHANTNO { get; set; }
}
This is where index mapping is done
client.Map<Acquirer>(m =>
{
var putMappingDescriptor = m.Index(Indices.Index("acquiringdata")).AutoMap();
return putMappingDescriptor;
});
I am getting the exact number of records but i am getting the null values
This Error can be Resolved by using this
[PropertyName("MERCHANTNO")]
I am going to update schooltypeid which is in the intermediates table, here is my repository class.
public async Task UpdateSchoolsAsync(SchoolUpdateVm schoolUpdateVm)
{
if (_GpsContext != null)
{
var schoolsObj = _GpsContext.School.FirstOrDefault(x => x.ID == Guid.Parse(schoolUpdateVm.id));
var schoolTypeObj = _GpsContext.SchoolsSchoolTypes.FirstOrDefault(x => x.SchoolTypeId == Guid.Parse(schoolUpdateVm.schoolTypeId));
Schools schools = new Schools();
{
schoolsObj.Name = schoolUpdateVm.name;
schoolsObj.Email = schoolUpdateVm.email;
schoolsObj.Phone = schoolUpdateVm.phone;
schoolsObj.Description = schoolUpdateVm.description;
schoolsObj.StateID = Guid.Parse(schoolUpdateVm.stateID);
schoolsObj.CountryId = Guid.Parse(schoolUpdateVm.countryId);
schoolTypeObj.SchoolTypeId = Guid.Parse(schoolUpdateVm.schoolTypeId); //here i can`t update schoolYype
}
_GpsContext.School.Update(schoolsObj);
await _GpsContext.SaveChangesAsync();
}
}
This is my School table in entity framework:
public partial class Schools
{
public Guid ID { get; set; }
public string Name { get; set; }
// Navigations
public ICollection<SchoolsSchoolType> SchoolsSchoolTypes { get; set; }// this is my junction table
}
This is my SchoolsSchoolTypes table:(This is intermediates table)
I think your model in the question is not complete. It lacks the "joining" entity.
That said, if you have a "pure" joining entity, with no additional attributes besides the key (made up of foreign keys), you should add the type to the "SchoolsSchoolTypes" collection in the "Schools" class. The code to add the entity should be something like this:
var schoolsObj = _GpsContext.School.Include(s => s.SchoolsSchoolTypes ).FirstOrDefault(x => x.ID == Guid.Parse(schoolUpdateVm.id)); //Include types to verify isn't already added
if (schoolsObj == null) throw new Exception("School not found");
if(schoolsObj.SchoolsSchoolTypes.Any(st=>st.SchoolTypeId == schoolUpdateVm.schoolTypeId) throw new Exception("School already has this type");
var schoolTypeObj = _GpsContext.SchoolsSchoolTypes.FirstOrDefault(x => x.SchoolTypeId == Guid.Parse(schoolUpdateVm.schoolTypeId));
if (schoolsObj == null) throw new Exception("School type not found");
schoolsObj.SchoolsSchoolTypes.Add(schoolTypeObj);
await _GpsContext.SaveChangesAsync();
If the "joining entity" has additional attributes (I suspect this is the case), then you have to create the new joining entity before adding it to the collection:
var schoolsObj = _GpsContext.School.Include(s => s.SchoolsSchoolTypes ).FirstOrDefault(x => x.ID == Guid.Parse(schoolUpdateVm.id)); //Include types to verify isn't already added
if (schoolsObj == null) throw new Exception("School not found");
if(schoolsObj.SchoolsSchoolTypes.Any(st=>st.SchoolTypeId == schoolUpdateVm.schoolTypeId) throw new Exception("School already has this type");
var schoolTypeObj = _GpsContext.SchoolTypes.FirstOrDefault(x => x.SchoolTypeId == Guid.Parse(schoolUpdateVm.schoolTypeId));
if (schoolsObj == null) throw new Exception("School type not found");
var newSchollType = new SchoolsSchoolTypes()
{
SchollId = Guid.Parse(schoolUpdateVm.id),
SchoolTypeId = Guid.Parse(schoolUpdateVm.schoolTypeId),
OtherProperty = "OtherPropertyValue"
}
schoolsObj.SchoolsSchoolTypes.Add(newSchollType);
await _GpsContext.SaveChangesAsync();
I have a books database, which has an ICollection of authors. I want to return the author object based on the AuthorId using LINQ.
Book db
int BookId
string Name { get; set; }
public ICollection<Author> Authors { get; set; }
Author db
int AuthorId
string Name
ICollection<Quote> Quotes { get; set; }
ICollection<Penname> Pennames { get; set; } - Edit: Added for clarity
I have tried:
var test = _context.Book.Include(x => x.Authors).Include("Authors.Quotes")
.Select(y => y.Authors)
Which gave me:
EntityQueryable<ICollection<Authors>>
[0] {HashSet<Author>} [0]{Author} [1]{Author} [3]{Author}
[1] {HashSet<Author>} [0]{Author} [1]{Author}
[2] {HashSet<Author>} [0]{Author} [1]{Author}
I just can't figure out how to iterate though the Authors in the Authors list. Something like the below:
var id = 2
var test = _context.Book.Include(x => x.Authors).Include("Authors.Quotes")
.Select(y => y.Authors.Select(x => x.Author).Where(x => x.AuthorId == id))
If I ever do a major update I might use elastic...
Update #Marko Papic:
Thanks. Weirdly if I use the below to get a list of books with authors, I get the quotes and pennames lists populated as I expect
var test = _context.Book.Include(x => x.Authors)
.ThenInclude(x => x.Quotes)
.Include(x => x.Authors)
.ThenInclude(x => x.Pennames)
However if I use SelectMany, then the quotes and pennames end up as null
var test = _context.Book.Include(x => x.Authors)
.ThenInclude(x => x.Quotes)
.Include(x => x.Authors)
.ThenInclude(x => x.Pennames)
.SelectMany(x => x.Authors).Where(x => x.AuthorId == id);
Author myauthor
int AuthorId = 2
string Name = "Bob"
ICollection<Quote> Quotes = null
ICollection<Penname> Pennames = null
You can use SelectMany:
var test = _context.Book.Include(x => x.Authors).ThenInclude(x => x.Quotes)
.SelectMany(x => x.Authors).Where(x => x.AuthorId == id);
I think the includes are ignored because the result type of the query is not the same of the type of your dbset with when you start, from the documentation:
If you change the query so that it no longer returns instances of the
entity type that the query began with, then the include operators are
ignored.
I assume the relationship between Books and Authors is many to many, if that is the case then this is how I would do your query:
var query=_context.Authors.Include(a=>a.Books)
.Include(a=>a.Quotes)
.Include(a=>a.Pennames)
.Where(a=> a.AuthorId == id);
I am trying to Get some data from Database and Bind them in Drop-Down list.. But getting following error : -
public virtual List<HRM_PersonalInformations>GetAssignePerson(String OCODE, int dptID){
var query = (context.HRM_PersonalInformations.Where
(c => c.OCODE == OCODE && c.DepartmentId == dptID)
.Select(c => new {FullName = (c.FirstName + ' ' + c.LastName), c.EID})).ToList();
return query; // It indicate error line
}
After that I am trying to Bind data in dropdown list and my code is following : -
private void FillAssignPerson()
{
try
{
string OCODE = ((SessionUser)Session["SessionUser"]).OCode;
int dptId = Convert.ToInt32(ddlAssignPerDept.SelectedValue);
var row = enquiryBll.GetAssignePerson(OCODE, dptId).ToList();
//const string list = "SELECT FirstName + ' ' + LastName AS FullName, EID, FirstName, LastName " +
// "FROM HRM_PersonalInformations " +
// "ORDER BY EID ASC"
if (row.Count > 0)
{
ddlAssignPerson.Items.Clear();
ddlAssignPerson.DataSource = row;
ddlAssignPerson.DataTextField = "FullName";
ddlAssignPerson.DataValueField = "EID";
ddlAssignPerson.DataBind();
ddlAssignPerson.Items.Insert(0, new ListItem("----- Select One -----", "0"));
ddlAssignPerson.AppendDataBoundItems = false;
}
}
Is it right way ?? Can anyone help me ? Thanks for Advance ..
Well, in your projection you have:
c => new {FullName = (c.FirstName + ' ' + c.LastName), c.EID}
That's creating an instance of an anonymous typoe - not an instance of HRM_PersonalInformations. If those are the only two properties you need in HRM_PersonalInformations, you could just change it to:
c => new HRM_PersonalInformations {
FullName = (c.FirstName + ' ' + c.LastName),
EID = c.EID
}
Or judging by your query, you may not need a projection at all. You may be fine with:
return context.HRM_PersonalInformations
.Where(c => c.OCODE == OCODE && c.DepartmentId == dptID)
.ToList();
Alternatively, if you only need those properties and don't really need the items of your list to be instances of HRM_PersonalInformations, you could just change the method's signature to be:
public virtual IList GetAssignePerson(...)
and keep the body as it is. You can't explicitly write a method declaration which specifies the anonymous type, because it doesn't have a name - but List<T> implements IList, so the above will certainly compile... it will fail if you later try to cast an element in it to HRM_PersonalInformations though.
EDIT: If the string concatenation is a problem, you might need to do that client-side. For example:
public IList GetAssignePerson(String OCODE, int dptID) {
return context.HRM_PersonalInformations
.Where(c => c.OCODE == OCODE && c.DepartmentId == dptID)
.Select(c => new { c.FirstName, c.LastName, c.EID })
.AsEnumerable() // Do the rest client-side
.Select(c => new {
FullName = c.FirstName + " " + c.LastName,
c.EID
})
.ToList();
}
This methods awaits you HRM_PersonalInformations , why are you return anonymous type ?
It is useful to go back to the expected type
Try this code
.Select(c => new HRM_PersonalInformations()
{
FullName = c.FirstName
// set other prop
});
this is a simple sample,
If you want to customize data business process or UI process you need a new model for example You want to display full name in Dropdown so ,
Add new folder MyCustomizeModels
Add new class DropdownLookUpDecimal in MyCustomizeModels
this DropdownLookUpDecimal class.
public class DropdownLookUpDecimal
{
public decimal Value { get; set; }
public string Text { get; set; }
}
You can use this class all Dropdown in the project.
public List<DropdownLookUpDecimal>GetAssignePerson(string oCode, int dptID)
{
var query = context.HRM_PersonalInformations.Where
(c => c.OCODE == oCode&& c.DepartmentId == dptID)
.Select(c => new DropdownLookUpDecimal
{
Text = c.FirstName + ' ' + c.LastName,
Value = c.EID
});
return query.ToList();
}
Just create new .cs and fill the list with the class and return List.
I hope it helped