how to set string variable as property in subquery - c#

I have the following objects:
var authors1 = new List<Author>() {
new Author{ FirstName = "William", LastName = "Smith" },
new Author{ FirstName = "Fred", LastName = "Jones" }
};
var authors2 = new List<Author>() {
new Author{ FirstName = "Brian", LastName = "Brains" },
new Author{ FirstName = "Billy", LastName = "TheKid" }
};
var books = new List<Book>() {
new Book{ Title = "JAVA", Description = "Description Java", Authors = authors1 },
new Book{ Title = "PHP", Description = "Description PHP", Authors = authors2 },
};
I want to create a subquery filtering by author. I know that I can do something like this:
IEnumerable<Book> list = books.Where(x => x.Authors.Where(j => j.FirstName == "William").Any());
But I would like to use the authors property as string var.
var entity = "Authors"
IEnumerable<Book> list = books.Where(x => x[entity].Where(j => j.FirstName == "William").Any());
This is not working.

I don't understand why you want to do this... But here is one way you could reach what you want with reflection:
public bool HasAuthorWithName(Book book, string authName)
{
// Retrieve list of class' properties
var p = book.GetType().GetProperties();
// Get list of authors prop by name
var entity = "Authors";
var lstAuthors = p.First(x => x.Name == entity).GetValue(book, null) as List<Author>;
// Filter by author's name
if (lstAuthors != null && lstAuthors.Any())
{
return lstAuthors.Any(j => j.FirstName == authName);
}
return false;
}
Now you can call this method:
IEnumerable<Book> list = books.Where(x => HasAuthorWithName(x, "William"));

Related

why books list not filtered and both item with name N1 & N2 appears and how to prepare ObservableCollection

I have below class and their associated data, here I am trying to filter books with book name having name "N1", but it's not working and I am getting both book items in `filterList, please suggest why and what is the best way to fill out observable collection based on filter data?
var lstStudents = new List<Student>
{
new Student
{
Name = "studen1",
Standards = new List<Standard> {new Standard {Name = "std1"}, new Standard {Name = "std2"}},
Books = new List<Book> {new Book {Name = "N1", Page = "20"}, new Book {Name = "N2", Page = "30"}}
},
new Student
{
Name = "studen2",
Standards = new List<Standard> {new Standard {Name = "std1"}},
Books = new List<Book> {new Book {Name = "N1", Page = "20"}, new Book {Name = "N2", Page = "30"}}
},
new Student
{
Name = "studen3",
Standards = new List<Standard> {new Standard {Name = "std1"}},
Books = new List<Book> {new Book {Name = "N1", Page = "20"}, new Book {Name = "N2", Page = "30"}}
}
};
var filterList = lstStudents.Where(c => c.Standards.Count == 1
&& c.Standards.Any(d => d.Name == "std1")
&& c.Books.Any(d => d.Name == "N1"))
.ToList();
//why both books with Name N1 & N2 both filtered as I am filtering with name = N1?
var data = new ObservableCollection<Data>();
foreach (var item in filterList)
{
data.Add(new Data { BookName = item.Name, BookPage = item.Books[0].Page });
}
Supporting Classes are,
public class Data
{
public string StudentName { get; set; }
public string BookName { get; set; }
public string BookPage { get; set; }
}
public class Student
{
public string Name { get; set; }
public List<Standard> Standards { get; set; }
public List<Book> Books { get; set; }
}
public class Standard
{
public string Name { get; set; }
}
public class Book
{
public string Name { get; set; }
public string Page { get; set; }
}
You could :
1 - Filter Allstudents that have count of Standards equals 1 and name std1,
2 - And Flatten all books and student Name to Data object directly,
3 - Use second filter for book BookName == "N1",
4 - Put directly the result in ObservableCollection, like the following code:
List<Data> filterList = lstStudents.Where(c => c.Standards.Count == 1 && c.Standards.Any(d => d.Name == "std1"))
.SelectMany(x => x.Books.Select(y => new Data { StudentName = x.Name, BookName = y.Name, BookPage = y.Page }))
.Where(d => d.BookName == "N1")
.ToList();
var newData = new ObservableCollection<Data>(filterList);
Demo
foreach (Data data in filterList)
{
Console.WriteLine($"StudentName:{data.StudentName} BookName:{data.BookName} BookPage:{data.BookPage}");
}
Result
StudentName:studen2 BookName:N1 BookPage:20
StudentName:studen3 BookName:N1 BookPage:20
I hope this helps you out.
Your query returns students that have any "N1" book in their books list (along with the other filters).
So the books themselves are not being filtered out, just students that don't have that book.
An example of how you could return a list of "N1" books for the students that meet the initial filters is:
var filterList = lstStudents.Where(c =>
c.Standards.Count == 1
&& c.Standards.Any(d => d.Name == "std1"))
.SelectMany(s => s.Books.Select(b =>
new {
StudentName = s.Name,
BookName = b.Name,
BookPage = b.Page
})
.Where(b => b.BookName == "N1"));
Then, e.g:
foreach (var item in filterList)
{
data.Add(new Data { StudentName = item.StudentName, BookName = item.BookName, BookPage = item.Page });
}
This first of all retrieves students matching the supplied criteria then retrieves the books from those students into one list of books (the SelectMany method).
It then filters those books to return only those with the required criteria into an anonymous type with student name also.

Cannot compare elements of type 'System.Collections.Generic.ICollection`1'. Only primitive types, enumeration types and entity types are supported

Getting error while executing below code, new to LINQ and not able to find how to assign child object "StudentAddresses" values which getting as icollection type in to students model
IList<StudentViewmodel> students = null;
using (var CTX = new StudentEntities())
{
students = CTX.Students.Include("JP").Select(
s => new StudentViewmodel()
{
Id = s.StudentId,
FirstName = s.FirstName,
LastName = s.LastName,
address = new AddressViewmodel()
{
// Address1 = s.StudentAddresses.Select(t => t.Address1).ToString()
Address1 = s.StudentAddresses.OfType<AddressViewmodel>().FirstOrDefault().Address1.ToString()
}
}
).ToList<StudentViewmodel>();
}
My issue is resolved , here is updated code
IList<StudentViewmodel> students = null;using (var CTX = new StudentEntities())
{
students = CTX.Students.Include("JP").Select(
s => new StudentViewmodel()
{
Id = s.StudentId,
FirstName = s.FirstName,
LastName = s.LastName,
address = new AddressViewmodel()
{
Address1 = s.StudentAddresses.FirstOrDefault().Address1 ,
Address2 = s.StudentAddresses.FirstOrDefault().Address2,
City = s.StudentAddresses.FirstOrDefault().City,
State = s.StudentAddresses.FirstOrDefault().State
}
}
).ToList<StudentViewmodel>();
}
I think this can help you
IList<StudentViewmodel> students = null;
using (var CTX = new StudentEntities())
{
students = CTX.Students.Include("JP").ToList().Select(
s => new StudentViewmodel()
{
Id = s.StudentId,
FirstName = s.FirstName,
LastName = s.LastName,
address = new AddressViewmodel()
{
// Address1 = s.StudentAddresses.ToList().Select(t => t.Address1).ToString()
Address1 = s.StudentAddresses.OfType<AddressViewmodel>().FirstOrDefault().Address1.ToString()
}
}
).ToList<StudentViewmodel>();
}
if there is a problem for comment access property and test it again.and be sure where do you have a problem.Thanks

C# linq query All method

Have a collection
List<<KeyValuePair<string, Person>>
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int MealType { get; set; }
}
patientEffort.Add("1", new Person() { FirstName = "Raja", LastName = "Ram", MealType = 2 });
patientEffort.Add("2", new Person() { FirstName = "Vijay", LastName = "Anthony", MealType = 1 });
patientEffort.Add("2", new Person() { FirstName = "Vijay", LastName = "Anthony", MealType = 2 });
patientEffort.Add("2", new Person() { FirstName = "Vijay", LastName = "Anthony", MealType = 3 });
patientEffort.Add("3", new Person() { FirstName = "Dr", LastName = "APJ", MealType = 1 });
patientEffort.Add("3", new Person() { FirstName = "Dr", LastName = "APJ", MealType = 2 });
patientEffort.Add("3", new Person() { FirstName = "Dr", LastName = "APJ", MealType = 3 });
patientEffort.Add("3", new Person() { FirstName = "Dr", LastName = "APJ", MealType = 4 });
List<int> _listMealType = new List<int>();
If _listMealType= [2] passed then Result will be
{Key: "1", FirstName = "Raja", LastName = "Ram"}
{Key: "2", FirstName = "Vijay", LastName = "Anthony"}
{Key: "3", FirstName = "Dr", LastName = "APJ"}
If _listMealType= [1,2,3] passed then Result will be
{Key: 2, FirstName = "Vijay", LastName = "Anthony"}
{Key: 3, FirstName = "Dr", LastName = "APJ"}
If _listMealType= [1,2,3,4] passed then Result will be
{Key: "3", FirstName = "Dr", LastName = "APJ"} only
Key may be string or int that doesn't matter. May I have linq query for this scenario. I have used All method is linq but not worked.
var query = patientEffort.Where(d => _listMealType.All(x => x == d.Value.MealType)).Select(d => d.Key);
Could you please help me in solving the query issue as soon as possible.
I hope it helps:
var patients = patientEffort.GroupBy(x => x.Value.FirstName);
var result = (from patient in patients let res = patient.Select(note => note.Value.MealType).ToList() where _listMealType.Intersect(res).Count() == _listMealType.Count select patient.First()).ToList();
Here is variant without linq using:
var patients = patientEffort.GroupBy(x => x.Value.FirstName); // group patients by name
foreach (var patient in patients)
{
var res = new List<int>();
foreach (var note in patient) // collect all meal types of current patient
res.Add(note.Value.MealType);
if (_listMealType.Intersect(res).Count() == _listMealType.Count) // if intersection count equal to source meal list - it's our patient.
result.Add(patient.First()); // add information about patient. because we need only name - we can use first record in list.
}

Unit Test Value

I am trying to unit test this code,
public bool IsCityAvailable(Appointment appointment)
{
var city = _customerRepository.Find(appointment.CustomerId).City;
return _employeeRepository.Get.Count(x => x.City == city) > 0;
}
Here is my test,
[SetUp]
public void Setup()
{
MockAppointmentRepository = new Mock<IRepository<Appointment>>();
MockCustomerRepository = new Mock<IRepository<Customer>>();
MockShiftRepository = new Mock<IRepository<Shift>>();
MockEmployeeRepository = new Mock<IRepository<Employee>>();
AppointmentService = new AppointmentService(MockCustomerRepository.Object, MockAppointmentRepository.Object, MockShiftRepository.Object, MockEmployeeRepository.Object);
Customer = new Customer()
{
Address = "88 Taraview Road NE",
City = "Calgary",
Email = "charles.norris#outlook.com",
FirstName = "Charles",
LastName = "Norris",
Id = 1,
Phone = "587-888-8882",
PostalCode = "X1X 1X1",
Province = "AB"
};
Employee1 = new Employee()
{
Address = "12 Saddletowne Road NW",
City = "Calgary",
Email = "johnny.bravo#outlook.com",
FirstName = "John",
LastName = "Bravo",
Id = 2,
Phone = "403-999-2222",
PostalCode = "X1X 1X1",
Province = "AB"
};
Employee2 = new Employee()
{
Address = "12 Saddletowne Road NW",
City = "Calgary",
Email = "johnny.bravo#outlook.com",
FirstName = "John",
LastName = "Bravo",
Id = 2,
Phone = "403-999-2222",
PostalCode = "X1X 1X1",
Province = "AB"
};
Appointment = new Appointment()
{
Id = 1,
Customer = Customer,
CustomerId = Customer.Id,
Employee = Employee1,
EmployeeId = Employee1.Id,
ScheduledTime = new DateTime(2013,10,15,18,30,00)
};
}
[Test]
public void IsCityAvailableShouldReturnAvailableWhenEmployeeInSameCityFound()
{
// ARRANGE
var employees = new List<Employee> { Employee1, Employee2 };
var result = new Mock<IQueryable<Employee>>();
result.Setup(r => r.GetEnumerator()).Returns(employees.GetEnumerator());
MockCustomerRepository.Setup(x => x.Find(It.IsAny<int>())).Returns(Customer);
MockEmployeeRepository.Setup(x => x.Get).Returns(result.Object);
// ACT
AppointmentService.IsCityAvailable(Appointment);
// ASSERT
MockCustomerRepository.Verify(x => x.Find(It.IsAny<int>()), Times.Once);
MockEmployeeRepository.Verify(x => x.Get, Times.Once);
}
I have debugged and stepped through can't find the value null it is referring to, my city variable is correct, the appointment parameter is fully populated. Is there something missing I am not seeing?
This is the output error from the unit test:
System.ArgumentNullException : Value cannot be null. Parameter name:
arguments
Here is the stacktrace:
System.ArgumentNullException : Value cannot be null.
Parameter name: arguments
at System.Linq.Expressions.Expression.RequiresCanRead(Expression expression, String paramName)
at System.Linq.Expressions.Expression.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi)
at System.Linq.Expressions.Expression.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ref ReadOnlyCollection`1 arguments)
at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
at System.Linq.Queryable.Count(IQueryable`1 source, Expression`1 predicate)
at Zenwire.Services.AppointmentService.IsCityAvailable(Appointment appointment) in AppointmentService.cs: line 81
at Tests.Unit.Zenwire.Services.AppointmentServiceTests.IsCityAvailableShouldReturnAvailableWhenEmployeeInSameCityFound() in AppointmentServiceTests.cs: line 100
To test that everyhing has been called correctly:
[SetUp]
public void Setup()
{
MockAppointmentRepository = new Mock<IRepository<Appointment>>();
MockCustomerRepository = new Mock<IRepository<Customer>>();
MockShiftRepository = new Mock<IRepository<Shift>>();
MockEmployeeRepository = new Mock<IRepository<Employee>>();
AppointmentService = new AppointmentService(MockCustomerRepository.Object, MockAppointmentRepository.Object, MockShiftRepository.Object, MockEmployeeRepository.Object);
Customer = new Customer()
{
Address = "88 Taraview Road NE",
City = "Calgary",
Email = "charles.norris#outlook.com",
FirstName = "Charles",
LastName = "Norris",
Id = 1,
Phone = "587-888-8882",
PostalCode = "X1X 1X1",
Province = "AB"
};
Employee1 = new Employee()
{
Address = "12 Saddletowne Road NW",
City = "Calgary",
Email = "johnny.bravo#outlook.com",
FirstName = "John",
LastName = "Bravo",
Id = 2,
Phone = "403-999-2222",
PostalCode = "X1X 1X1",
Province = "AB"
};
Employee2 = new Employee()
{
Address = "12 Saddletowne Road NW",
City = "Calgary",
Email = "johnny.bravo#outlook.com",
FirstName = "John",
LastName = "Bravo",
Id = 2,
Phone = "403-999-2222",
PostalCode = "X1X 1X1",
Province = "AB"
};
Appointment = new Appointment()
{
Id = 1,
Customer = Customer,
CustomerId = Customer.Id,
Employee = Employee1,
EmployeeId = Employee1.Id,
ScheduledTime = new DateTime(2013,10,15,18,30,00)
};
}
[Test]
public void IsCityAvailableShouldReturnAvailableWhenEmployeeInSameCityFound()
{
// ARRANGE
MockCustomerRepository.Setup(x => x.Find(It.IsAny<int>())).Returns(Customer);
MockEmployeeRepository.Setup(x => x.Get).Returns(new List<Employee> { Employee1, Employee2 }.AsQueryable());
// ACT
AppointmentService.IsCityAvailable(Appointment);
// ASSERT
MockCustomerRepository.Verify(x => x.Find(It.IsAny<int>()), Times.Once);
MockEmployeeRepository.Verify(x => x.Get, Times.Once);
}
Test the return value:
[Test]
public void IsCityAvailableShouldReturnAvailableWhenEmployeeInSameCityFound()
{
// ARRANGE
MockCustomerRepository.Setup(x => x.Find(It.IsAny<int>())).Returns(Customer);
MockEmployeeRepository.Setup(x => x.Get).Returns(new List<Employee> { Employee1, Employee2 }.AsQueryable());
// ACT
var actual = AppointmentService.IsCityAvailable(Appointment);
// ASSERT
Assert.IsTrue(actual);
}

Strange behaviour on IEqualityComparer

I'm trying to implement an IEqualityComparer for my object that basically detects if an object is older that another one. The following simpler example will synthesises what i'm trying to accomplish:
class Program
{
static void Main(string[] args)
{
var authorsList = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=20 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=14 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Trevor", Lastname = "Smith", Age=15 },
new Author{ Firstname = "Brian", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Billy", Lastname = "Smith", Age=11 },
};
var authorsListExcept = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
};
var authorsList2 = authorsList
.Except(authorsListExcept, new AuthorUpdatedComparer()).ToList();
}
}
class Author
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public int Age { get; set; }
}
class AuthorUpdatedComparer : IEqualityComparer<Author>
{
public bool Equals(Author x, Author y)
{
return x.Age >= y.Age;
}
public int GetHashCode(Author obj)
{
//Check whether the object is null
if (Object.ReferenceEquals(obj, null)) return 0;
int FirstnameHash = (obj.Firstname ?? "").GetHashCode();
int LastnameHash = (obj.Lastname ?? "").GetHashCode();
int finalResult = FirstnameHash ^ LastnameHash;
return finalResult;
}
}
My authorsList2 result would be:
* Bob Smith with age 20
* Bom Smith with age 14
* Trevor Smith with age 15
* Brian Smith with age 11
* Billy Smith with age 11
But instead of this the Bob Smith with age 14 is not included.
When debugging I reached the conclusion that the Comparer after included Bob Smith with Age 20 start to using it has a comparer excluding then after all the Bob's younger than 20.
This is a strange behavior in my point of view it should only excluded the ones that are younger or with the same age to those included on the authorsListExcept.
I tried to read msdn documentation and what I want it should be supposed to happen:
font: http://msdn.microsoft.com/en-us/library/bb336390(v=vs.100).aspx
Anyone can help me?
Thanks,
Hugo Salgado
The following LINQ query provides the result you expect:
var result =
authorsList.GroupBy(x => Tuple.Create(x.Firstname, x.Lastname))
.SelectMany(g => g.Where(x => authorsListExcept.All(e => e.Firstname != x.Firstname || e.Lastname != x.Lastname || e.Age < x.Age)));
The following query also produces this result. It should perform better:
var result =
authorsList.GroupBy(x => Tuple.Create(x.Firstname, x.Lastname))
.GroupJoin(authorsListExcept, x => x.Key,
x => Tuple.Create(x.Firstname, x.Lastname),
(a, e) => a.Where(x => x.Age > e.Select(z => z.Age)
.DefaultIfEmpty(0)
.Max()))
.SelectMany(x => x)
And a third option (the same as the previous but in query syntax):
var result =
(from a in authorsList
group a by Tuple.Create(a.Firstname, a.Lastname) into g
join e in authorsListExcept on g.Key equals Tuple.Create(e.Firstname, e.Lastname) into er
from age in er.Select(x => x.Age).DefaultIfEmpty()
select g.Where(x => x.Age > age)).SelectMany(x => x);
The interface IEqualityComparer<T> is there to check for equality. It has nothing to do with any ordering. As such, you can't use it in the way you try.
In general: An implementation of this interface should always use the exact same set of properties in both the GetHashCode implementation and the Equals method.
If i have understood what you want try this.
class Program
{
static void Main(string[] args)
{
var authorsList = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=20 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=14 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Trevor", Lastname = "Smith", Age=15 },
new Author{ Firstname = "Brian", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Billy", Lastname = "Smith", Age=11 },
};
var authorsListExcept = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
};
var authorsList2 = authorsList.Where(x => !authorsListExcept.Any(y => y.Firstname == x.Firstname && y.Lastname == x.Lastname && x.Age <= y.Age));
}
}
public class Author
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public int Age { get; set; }
}

Categories

Resources