Proper way to Categorize by a Property - c#

I have made a C# LINQ query that categorizes (or groups) by a property and I am almost sure there is a better way. My project is full of these queries so I am really interested on how to achieve this the proper way.
This is how my query look like:
var g = _repository.GetEmployees();
var result =
g.GroupBy(x => x.City, (key, group) => group.First())
.Select(x => new {
city = x.City,
employees = g
.Where(y=>y.EmployeeID == x.EmployeeID)
.Select(y=> new {
fullname = y.FirstName + " " + y.LastName,
title = y.Title
})
.OrderBy(y=>y.fullname)
})
.OrderBy(x => x.city);
Sample of JSON output:
[
{
"city":"Barcelona",
"employees":[
{
"fullname":"Foo Bar",
"title":"Help Desk Technician"
},
{
"fullname":"Lorem Ipsum",
"title":"Information Technology Director"
}
]
},
{
"city":"London",
"employees":[
{
"fullname":"Le Query",
"title":"Information Technology Manager"
},
{
"fullname":"Please Help",
"title":"Management Information Systems Director"
}
]
}
]
The result is fine. What is the best way to achieve it?

It sounds like you just want:
var result = g.GroupBy(x => x.City, (key, group) => new {
city = key,
employees = group.Select(emp => new {
fullname = emp.FirstName + " " + emp.LastName,
title = emp.Title
})
});
In other words, you're just providing a projection for each group, which is "an anonymous type with the city, and all the employees for that city".

Related

c# - Update unit test method with Moq

I am using the Moq framework for my unit test.
This is my TestMethod:
[TestFixture]
public class UpdateContactTests
{
private static readonly object[] TestValidContact =
{
new object[]
{
"Prueba", "nearlinx#gmail.com", "86456245",
new LookUpItem()
{
LookUpItemId = Guid.NewGuid(), Category = "ContactType", CategoryId = 1, Value = "Bulling Contact"
},
new Company()
{
Id = Guid.NewGuid(), Name = "Company", Email = "company#gmail.com", WebSite = "company.com",
Nda = false, Msa = false, Phone = "84876817"
}
}
};
[TestCaseSource(nameof(TestValidContact))]
[Test]
public void UpdateContact_ValidValues_UpdateContact(string name, string email, string phone, LookUpItem lookUpItem, Company company)
{
//arrange
var id = Guid.NewGuid();
var data =
new[]
{
new Contact { Id = Guid.NewGuid(), Name = "Test1", Email = "nearlinx#gmail.com", Phone = "86456245",
ContactType =
new LookUpItem()
{
LookUpItemId = Guid.NewGuid(), Category = "ContactType", CategoryId = 1, Value = "Bulling Contact"
},
Company =
new Company()
{
Id = Guid.NewGuid(), Name = "Company", Email = "company#gmail.com", WebSite = "company.com",
Nda = false, Msa = false, Phone = "84876817"
}},
new Contact { Id = id, Name = "Test2", Email = "nearlinx#gmail.com", Phone = "86456245",
ContactType =
new LookUpItem()
{
LookUpItemId = Guid.NewGuid(), Category = "ContactType", CategoryId = 1, Value = "Bulling Contact"
},
Company =
new Company()
{
Id = Guid.NewGuid(), Name = "Company", Email = "company#gmail.com", WebSite = "company.com",
Nda = false, Msa = false, Phone = "84876817"
}},
};
var contact = new Contact()
{
Id = id,
Name = name,
Email = email,
Phone = phone,
ContactType = lookUpItem,
Company = company
};
var _unitOfWork = new Mock<IUnitOfWork>();
_unitOfWork.Setup(mock => mock.Contact.Get(null, null, null)).Returns(data);
_unitOfWork.Setup(mock => mock.Company.Get(null, null, null)).Returns(new List<Company>());
_unitOfWork.Setup(mock => mock.LookUpItem.Get(null, null, null)).Returns(new List<LookUpItem>());
var contactService = new ContactService(_unitOfWork.Object);
//act
contactService.UpdateContact(contact);
//assert
Assert.That(data.First(m => m.Id == id).Name, Is.EqualTo(contact.Name));
_unitOfWork.Verify(mock => mock.Contact.Update(It.IsAny<Contact>()), Times.Once);
}
}
My problem is that when I run the test a NullReferenceException is thrown, I suppose it is because the list that has the object that I want to modify is not being assigned
I've never really used Moq and I don't know much about unit tests either, so what am I really doing wrong
Edit:
i change one of the setup because the one called when the update is done was another
instead of
_unitOfWork.Setup(mock => mock.Contact.Get(null, null, null)).Returns(data);
is
_unitOfWork.Setup(mock => mock.Contact.Where(c => c.Id == contact.Id,null,null).FirstOrDefault()).Returns(() => data.Where(c => c.Id == contact.Id));
Without seeing the rest
_unitOfWork.Setup(mock => mock.Contact.Get(null, null, null))
the mock.Contact returns null
best thing is to debug the test, breakpoint the first line and step through to find exactly where it is throwing the null exception
I found the solution
I was doing the set like this
_unitOfWork.Setup(mock => mock.Contact.Where(c => c.Id == contact.Id,null,null).FirstOrDefault()).Returns(() => data.Where(c => c.Id == contact.Id));
while in the update method the condition was this
_unitOfWork.Setup(mock => mock.Contact.Where(c => c.Id == contact.Id).FirstOrDefault()).Returns(() => data.Where(c => c.Id == contact.Id));
changing the condition in the method fixed the problem

How to Select only Id's with linq when there is List in List

I want to select only id's from List of objects in another List:
https://dotnetfiddle.net/Leu1AD
I need to use only linq Select or SelectMany:
var obj = offices.Select(p => new {Id = p.Id, Employess = p.Employess}).ToList();
Currently I get following result:
[
{
"Id":1,
"Employess":[
{
"Id":1,
"FirstName":"a",
"LastName":"b"
},
{
"Id":2,
"FirstName":"c",
"LastName":"d"
}
]
},
{
"Id":2,
"Employess":[
{
"Id":3,
"FirstName":"e",
"LastName":"f"
},
{
"Id":4,
"FirstName":"g",
"LastName":"h"
}
]
}
]
But I need this result:
[
{
"Id":1,
"Employess":[
{
"Id":1
},
{
"Id":2
}
]
},
{
"Id":2,
"Employess":[
{
"Id":3
},
{
"Id":4
}
]
}
]
Do you have any ideas how to do that?
One method to get the result to be the format you want is along the lines of
var obj = offices.Select(p => new {Id = p.Id, Employess = p.Employess.Select(y=> new {y.Id})}).ToList();
ends up as
[{"Id":1,"Employess":[{"Id":1},{"Id":2}]},{"Id":2,"Employess":[{"Id":3},{"Id":4}]}]
You need a second Select that only selects the Employee Id.
var obj = offices.Select(o => new {Id = o.Id, Employess = o.Employess.Select(e => new { Id = e.Id })});
alter your line
var obj = offices.Select(p => new {Id = p.Id, Employess = p.Employess}).ToList();
to
var obj = offices.Select(p => new {Id = p.Id, Employess = p.Employess.Select(x=>new{Id=x.Id})}).ToList();
To have the expected result just replace:
Employess = p.Employess
With
Employess = p.Employess.Select(e => new { e.Id })
Finally you have this LINQ statement:
var obj = offices.Select(p => new {Id = p.Id, Employess = p.Employess.Select(e => new { e.Id })}).ToList();

Retrieving all records with Lambda StartsWith()

I have the following ActionResult and retrieve records that starting with "query" parameter's value. However, when quesry value is empty or null, the methods returns no record while I want to retrieve all of them. So, do I have to use a if clause and create different lambda clauses, or is it possible to check query parameter and retrieve all of the records by using StartsWith?
public ActionResult StudentLookup(string query)
{
var students = repository.Students.Select(m => new StudentViewModel
{
Id = m.Id,
Name = m.Name
})
.Where(m => m.Name.StartsWith(query));
return Json(students, JsonRequestBehavior.AllowGet);
}
Well, two options:
Conditionally apply the Where clause:
IQuerable<StudentModel> students = repository.Students.Select(m => new StudentViewModel
{
Id = m.Id,
Name = m.Name
});
if (!string.IsNullOrEmpty(query))
{
students= students.Where(m => m.Name.StartsWith(query));
}
return Json(students, JsonRequestBehavior.AllowGet);
Put the check in the Where clause itself:
var students = repository.Students.Select(m => new StudentViewModel
{
Id = m.Id,
Name = m.Name
})
.Where(m => string.IsNullOrEmpty(query) || m.Name.StartsWith(query));
return Json(students, JsonRequestBehavior.AllowGet);

Cannot implicitly convert type 'System.Linq.IOrderedEnumerable<System.Web.Mvc.SelectListItem>' to 'System.Web.Mvc.SelectList'

What am I missing in the following code? I want to sort the list by Text
public SelectList MyList { get; set; }
MyList = new SelectList(new SelectListItem[] {
new SelectListItem {
Text = "Select Customer", Value = "", Selected = false
}
}.Concat(myList.GetAll().Select(x => new SelectListItem { Text = x.Customer.Name + " - " + x.Name, Value = x.ID.ToString() })), "Value", "Text", "0");
MyList = MyList .OrderBy(x => x.Text);
I am having the error on the last line when compiling.
Cannot implicitly convert type
'System.Linq.IOrderedEnumerable' to
'System.Web.Mvc.SelectList'. An explicit conversion exists (are you
missing a cast?)
You are trying to sort the SelectList object as it was a list. Sort the source for the SelectList. You probably want to exclude the first one from the sorting:
MyList = new SelectList(
new SelectListItem[] {
new SelectListItem {
Text = "Select Customer", Value = "", Selected = false
}
}.Concat(
myList.GetAll()
.Select(x => new SelectListItem { Text = x.Customer.Name + " - " + x.Name, Value = x.ID.ToString() })
.OrderBy(x => x.Text)
), "Value", "Text", "0");
Just move the OrderBy inside the constructor argument. Actually, you probably want to keep the first element at the start of the list so the OrderBy should be applied inside the Concat. Also, I have refactored the code to make it somewhat easier to understand what is going on:
var items = myList
.GetAll()
.Select(
x => new SelectListItem {
Text = x.Customer.Name + " - " + x.Name,
Value = x.ID.ToString()
}
)
.OrderBy(x => x.Text);
var itemsWithInitialItem = new SelectListItem[] {
new SelectListItem {
Text = "Select Customer", Value = "", Selected = false
}
}
.Concat(items);
MyList = new SelectList(itemsWithInitialItem, "Value", "Text", "0");

ASP.NET MVC 3 Cascading combobox doesn't work

I have two comboboxes. First comobobox I populate in such way and it works fine:
#Html.DropDownListFor(
x => x.Town,
new SelectList(Model.Towns, "Value", "Text"),
"-- Select town --")
public IEnumerable<SelectListItem> Towns
{
get
{
List<DataRow> TownsListDB = OracleSelect("select * from Towns");
List<SelectListItem> townsItems = new List<SelectListItem>();
foreach (DataRow rw in TownsListDB)
{
townsItems.Add(new SelectListItem { Value = rw[0].ToString(),
Text = rw[1].ToString() });
}
return townsItems;
}
}
And depends on the town, I want to show a list of hospitals:
#Html.DropDownListFor(
x => x.Hospital,
Enumerable.Empty<SelectListItem>(),
"-- Select hospital --")
My jQuery code is:
$('#Town').change(function() {
var selectedTown = $(this).val();
if (selectedTown != null && selectedTown != '') {
$.getJSON('#Url.Action("Hospitals")', { town: selectedTown },
function (hospitals) {
var hospitalsSelect = $('#Hospital');
hospitalsSelect.empty();
$.each(hospitals, function(i, hospital) {
hospitalsSelect.append($('<option/>', {
value: hospital.value,
text: hospital.text
}));
});
});
}
});
and C#:
public ActionResult Hospitals(string town)
{
var modelHospital = new MedicalViewModel();
List<DataRow> HospitalsListDB = modelHospital.OracleSelect
("select * from Hospitals hh where hh.TownID = " + town);
List<SelectListItem> hospitalsItems = new List<SelectListItem>();
foreach (DataRow rw in HospitalsListDB)
{
//example:
//rw[0]=101111
//rw[1]=Dublin
hospitalsItems.Add(new SelectListItem { Value = rw[0].ToString(),
Text = rw[1].ToString() });
}
return Json(
hospitalsItems,
JsonRequestBehavior.AllowGet);
return Json(hospitalsItems, JsonRequestBehavior.AllowGet);
}
But it doesn't work. If I use this code as a Return result, then it's ok:
return Json(Enumerable.Range(1, 6).Select(x => new { value = x, text = x }),
JsonRequestBehavior.AllowGet
);
Why combobox doesn't work with my List result from DB?
Use this code :
return Json(hospitalsItems.ToList(), JsonRequestBehavior.AllowGet);
Instead of this in last line
return Json(hospitalsItems, JsonRequestBehavior.AllowGet);
I found out what was the problem. It sounds funny I just needed capital letters in my jQuery code: code select.append($('', { value:hospital.Value, text: hospital.Text }

Categories