Inheritance and Linq-to-Entities - c#

I have the following models:
public class Person
{
long Id;
string name;
}
public class Student : Person
{
string studentId;
}
public class Bus
{
long Id;
public ICollection<Person> riders {set; get;}
}
public class SchoolBus : Bus
{
long schoolBusNumber;
}
I also have the following code:
SchoolBus schoolBus = new SchoolBus();
schoolBus.riders = new List<Person>
{
new Student { name = "Jim" },
new Student { name = "Jane }
}
var query = from rider in SchoolBus.riders
select new
{
(rider as Student).studentId;
}
Students and Person are set up as separate tables and I'm using DbContext.
I know why this would not work, but what are possible solutions for me to get this to return the right studentId by using a Person collection?

try this:
var studentIds = rider.OfType<Student>().Select(x => x.studentId);

If your code is exactly what you shown, this will work:
SchoolBus schoolBus = new SchoolBus();
schoolBus.riders = new List<Person>
{
new Student { name = "Jim" },
new Student { name = "Jane }
}
var query = from rider in SchoolBus.riders
select new
{
riderID = (rider as Student).studentId;
}
But if your query runs on linq2entity, you should show your exact code, and your problem.

Related

From a one to many situation how do I get common items in Entity Framework

I just started with Entity Framework and I was having difficulty generating a query for the following situation.
I currently have two model classes Student and Sport. A student can play multiple sports. This is what my models look like
public class DbContext : DbContext
{
public DbContext(): base("name=DbContext")
{
}
public DbSet<Student> MyStudents { get; set; }
public DbSet<Sport> MySports { get; set; }
}
public class Student
{
public List<Sport> Actions { get; set; }
public string Name { get; set; }
}
public class Sport
{
public string SportName { get; set; }
}
My question is how do I get a list of all sports played by all the students? In short I am looking for common sports. So basically in the following case
Student A played Sports : Soccer , Tennis , Bowling
Student B played Sports : Soccer , Tennis ,
Student C played Sport : Tennis
Then only Tennis should be returned
Using the DB schema you've provided you can get the common sports checking sports of each student:
var sports = new[]
{
new Sport { SportName = "Tennis" },
new Sport { SportName = "Soccer" },
new Sport { SportName = "Bowling" }
};
var students = new[]
{
new Student
{
Name = "Student 1",
Actions = sports
},
new Student
{
Name = "Student 2",
Actions = new[] { sports[0], sports[1] }
},
new Student
{
Name = "Student 3",
Actions = new[] { sports[0] }
}
};
// Or
var sports = context.Sports;
var students = context.Students;
// In case students' sports are objects (as in this sample) you can use such a query:
var commonSports = sports.Where(sport =>
students.All(student => student.Actions.Contains(sport)));
// In case you're going to check the sports by name, this:
var commonSports = sports.Where(sport =>
students.All(student => student.Actions.Any(studSport =>
studSport.SportName == sport.SportName)));
Console.WriteLine($"Comon sports: {string.Join(",", commonSports.Select(i => i.SportName))}");
// To get only names of common sports:
var sportNames = commonSports.Select(i => i.SportName);
Console.Read();
If you use a relational database it would be easier and (as for me) more logical to implement many-to-many relationship as described here:
var context = new DbContext()
var unique = context.MyStudents.SelectMany(student => student.Actions.Select(sport => sport.SportName)).Distinct();
you just do this :
var commonSports = Context.Students.SelectMany(x=>x.Actions).GroupBy(x => x.SportName).Where(x=>x.Count()==items.Count(c=>c.Actions!=null)).Select(x=>x.Key).ToList();
I hope it been helpful .
To achieve this you might want to first set up some kind of model class, this isn't strictly necessary but might make things clearer for you:
public class StudentWithSports()
{
public string Name {get;set;}
public List<string> Sports {get;set;}
}
You can then populate your model from your context:
using(var context = new DbContext())
{
List<StudentWithSports> list = context
.Students
.Include(stu => stu.Actions)
.Select(stu => new StudenWithSports
{
Name = stu.Name,
Sports = stu.Actions.Select(act => act.SportName).ToList()
}).ToList();
}
If you don't want to create a model you could just do:
var list = context
.Students
.Include(stu => stu.Actions)
.Select(stu => new {
Name = stu.Name,
Sports = stu.Actions.Select(act => act.SportName).ToList()
}).ToList();
Which will give you a list of anonymous objects with the same properties.
The essence of my answer is the linq query, but I created a couple of classes to model your EF classes to show it works.
Student student1 = new Student
{
Name = "John",
Actions = new List<Sport>
{
new Sport { SportName = "Tennis" },
new Sport { SportName = "Soccer" },
new Sport { SportName = "Bowling" }
}
};
Student student2 = new Student
{
Name = "Mary",
Actions = new List<Sport>
{
new Sport { SportName = "Tennis" },
new Sport { SportName = "Soccer" }
}
};
Student student3 = new Student
{
Name = "Jane",
Actions = new List<Sport>
{
new Sport { SportName = "Tennis" }
}
};
IEnumerable<Student> students = new List<Student>
{
student1,
student2,
student3
};
var query = from s in students
select new
{
s.Name,
Sports = from sp in s.Actions
select sp.SportName
};
var result = query.ToList();
for (int i = 0; i < result.Count(); i++)
{
Console.Write(result[i].Name + " played sports: ");
foreach (var sport in result[i].Sports)
Console.Write(" " + sport);
Console.WriteLine();
}
Well your Db design isn't right because you have many to many relation between MyStudents and MySports tables. You have to add joint table between Students and Sports. You can call it StudentsSports
public class DbContext : DbContext
{
public DbContext(): base("name=DbContext")
{
}
public DbSet<Student> MyStudents { get; set; }
public DbSet<StudentsSport> StudentsSports { get; set; }
public DbSet<Sport> MySports { get; set; }
}
public class Student
{
public int ID { get; set; }
public List<StudentsSport> Actions { get; set; }
public string Name { get; set; }
}
public class Sport
{
public int ID { get; set; }
public string SportName { get; set; }
}
public class StudentsSport
{
public int ID { get; set; }
[ForeignKey(Student)]
public int StudentID { get; set; }
[ForeignKey(Sport)]
public int SportID { get; set; }
}
Then you can just do
var listOfActions = MyStudents.Select(s => s.Actions.Select(a => a.SportID));
var intersection = listOfActions
.Skip(1)
.Aggregate(
new HashSet<T>(listOfActions.First()),
(h, e) => { h.IntersectWith(e); return h; }
);
EDIT:
If you have students without sports then you will always get empty intersection list. If you don't want that then you will have to filter them
var listOfActions = MyStudents.Select(s => s.Actions.Select(a => a.SportID)).Where(c => c.Any());

Build Dynamic query using Expression Tree

I am working on dynamic query building in LINQ using Expression Tree.
I have taken the reference to the following post
https://www.codeproject.com/Tips/582450/Build-Where-Clause-Dynamically-in-Linq
How can I build expression if I want to check all the element in the list contains in another collection or not?
I have a Person class
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
and I have a list
List<Person> personList = new List<Person>()
{
new Person{ Name = "Shekhar", Age = 31},
new Person{ Name = "Sandip", Age = 32},
new Person{ Name = "Pramod", Age = 32},
new Person{ Name = "Kunal", Age = 33}
};
I have another list
List<string> nameList = new List<string>() { "Sandip", "Prashant" };
How can I build expression tree to check all the element in the list "nameList" contains in "personList" and give result true or false?
try this:
public bool Find(List<string> nameList, List<Person> personList)
{
foreach (var name in nameList)
if (personList.FirstOrDefault(person => person.Name == name) != null)
{
// Find
return true;
}
return false;
}
try this:
bool contained = !personList.Select(l=>l.Name).Except(nameList).Any();

Is there a shorter, better and optimised way to fill this list from SQL database

I got this code and it works without problems. But i sense there is much better way to do this.
namespace Repositories
{
public class AuthorRepository : IAuthorRepository
{
public List<Author> GetAllFromRepo()
{
using (AppContext myDB = new AppContext())
{
List<Author> authorsFromRepo = new List<Author>();
foreach (var item in myDB.Authors)
{
authorsFromRepo.Add(new Author()
{
Books = new List<Book>(),
ID = item.ID,
FirstName = item.FirstName,
LastName = item.LastName
});
}
return authorsFromRepo.ToList();
}
}
}
}
When i try something along the lines of this:
public List<Author> GetAllFromRepo()
{
using (AppContext myDB = new AppContext())
{
List<Author> authorsFromRepo = new List<Author>();
authorsFromRepo = myDB.Authors.ToList();
return authorsFromRepo;
}
}
I always get this error:
Value cannot be null.
Parameter name: source
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: source
Source Error: Line 33: return authors.Select(x => new AuthorViewModel()
Any Help?
The model where the error takes me
namespace Services
{
public class AuthorService : IAuthorService
{
private readonly IAuthorRepository _AuthorRepository;
public AuthorService(IAuthorRepository authorRepository)
{
_AuthorRepository = authorRepository;
}
public List<AuthorViewModel> GetAll()
{
List<Author> authors = _AuthorRepository.GetAllFromRepo();
return authors.Select(x => new AuthorViewModel()
{
ID = x.ID,
FullName = $"{x.FirstName } {x.LastName} ",
Books = x.Books.Select(g => new BookViewModel()
{
ID = g.ID,
Name = g.Name
}).ToList()
}).ToList();
}
}
}
To add again, everything works fine if i use the first example of code.
When i try something shorter like
return myDB.Authors.ToList();
i get the error.
when i change to:
return authors.Select(x => new AuthorViewModel()
{
ID = x.ID,
FullName = $"{x.FirstName } {x.LastName} ",
Books = {}
}).ToList();
It works then... but this means it doesn't read the author books...
I had to change the model for Authors to
namespace Entities
{
public class Author
{
// this is what a had to add here
// From here
public Author()
{
Books = new List<Book>();
}
// to here
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Book> Books { get; set; }
}
}
So It wont give me any errors when x.Books is null.

Type casting in C# .net

I am learning a MOOC course on c#. we had to create an Arraylist of type students and then using the foreach loop had to iterate over it and print the names. i have tried all casting methods but could not get through it. please help
c.students.Add(student1);
c.students.Add(student2);
c.students.Add(student3);
foreach(object o in students)
{
Student s = (Student)o;
Console.WriteLine(s.FirstName);
}
c is the course object. course is a class. students is the arraylist. Student is a class.
Not sure where you face the error. Check out my .NET Fiddle here. Code shown below as well. Hope it helps.
using System;
using System.Collections;
public class Program
{
public static void Main()
{
var students = new ArrayList();
students.Add(new Student() { FirstName = "John", LastName = "Doe" });
students.Add(new Student() { FirstName = "Richard", LastName = "Roe" });
foreach(Student s in students)
{
Console.WriteLine(s.FirstName);
}
}
}
public class Student
{
public string FirstName {get;set;}
public string LastName {get;set;}
}
foreach(object o in c.students)
this should do it, its probably a silly mistake
namespace stackOverflow
{
class Program
{
static void Main(string[] args)
{
course c = new course();
student student1 = new student("a");
student student2 = new student("b");
student student3 = new student("c");
c.students.Add(student1);
c.students.Add(student2);
c.students.Add(student3);
foreach (object o in c.students)
{
student s = (student)o;
Console.WriteLine(s.name);
}
}
}
class course
{
public List<student> students = new List<student>();
}
class student
{
public string name { get; set; }
public student(string s)
{
name = s;
}
}
}
ArrayList students = new ArrayList();
This line should be: c.students = new ArrayList(); as mentioned by Channs previously.
As you have written it, it is trying to create a new variable in your main function called students, it never accesses the students array inside your course object.
Although your initialisation of internal object variables should be done within the object itself.
So inside your course object do something more like this:
class Course
{
public ArrayList students;
public Course()
{
students = new ArrayList();
}
}
This way, whenever you declare a new object of type Course ( ie: Course c = new Course() ) it will initialise the array automatically.
Another issue I noticed was in your Student constructor declaration, you are always trying to take a parameter of string fname.
public Student(string fname)
Then in your code you never pass that data ie:
Student student1 = new Student();
So either pass the firstname variable in when you are initialising or change your constructor in Student to allow it to accept nothing as well as a firstname as shown below:
class Student
{
private string firstName;
private string lastName;
public Student(string fname = null)
{
this.FirstName = fname;
}
this way you don't have to pass the data, but if you do it will be copied over to the firstname of the student object.
You could always change the null to something like "John" or "No Name" so that you have printable data in the object. just a suggestion though.
Regards,
Slipoch
Here is your code fixed up. There were 2 things:
Your student class did not have a default constructor.
Your student array list was not initialized in the course class.
Hope this helps.
using System;
using System.Collections;
class Program
{
public static void Main(string[] args)
{
Student student1 = new Student();
student1.FirstName = "a";
student1.LastName = "w";
Student student2 = new Student();
student2.FirstName = "e";
student2.LastName = "s";
Student student3 = new Student();
student3.FirstName = "i";
student3.LastName = "o";
Course c = new Course();
ArrayList students = new ArrayList();
c.students.Add(student1);
c.students.Add(student2);
c.students.Add(student3);
foreach (Student o in c.students)
{
Student s = (Student)o;
Console.WriteLine(s.FirstName);
}
Console.ReadLine();
}
}
internal class Course
{
public ArrayList students = new ArrayList();
}
internal class Student
{
private string firstName;
private string lastName;
public Student()
{
}
public Student(string fname)
{
this.FirstName = fname;
}
public string FirstName
{
get
{
return firstName;
}
set
{
firstName = value;
}
}
public string LastName
{
get
{
return lastName;
}
set
{
lastName = value;
}
}
}

Cleanest Way To Map Entity To DTO With Linq Select?

I've been trying to come up with a clean and reusable way to map entities to their DTOs. Here is an example of what I've come up with and where I'm stuck.
Entities
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
// Other properties not included in DTO
}
public class Address
{
public int ID { get; set; }
public string City { get; set; }
// Other properties not included in DTO
}
DTOs
public class PersonDTO
{
public int ID { get; set; }
public string Name { get; set; }
public AddressDTO Address { get; set; }
}
public class AddressDTO
{
public int ID { get; set; }
public string City { get; set; }
}
Expressions
This is how I began to handle the mapping. I wanted a solution that wouldn't execute the query before mapping. I've been told that if you pass a Func<in, out> instead of Expression<Func<in, out>> that it will execute the query before mapping.
public static Expressions
{
public static Expression<Func<Person, PersonDTO>> = (person) => new PersonDTO()
{
ID = person.ID,
Name = person.Name,
Address = new AddressDTO()
{
ID = person.Address.ID,
City = person.Address.City
}
}
}
One issue with this is that I already have an expression that maps an Address to an AddressDTO so I have duplicated code. This will also break if person.Address is null. This gets messy very quick especially if I want to display other entities related to person in this same DTO. It becomes a birds nest of nested mappings.
I've tried the following but Linq doesn't know how to handle it.
public static Expressions
{
public static Expression<Func<Person, PersonDTO>> = (person) => new PersonDTO()
{
ID = person.ID,
Name = person.Name,
Address = Convert(person.Address)
}
public static AddressDTO Convert(Address source)
{
if (source == null) return null;
return new AddressDTO()
{
ID = source.ID,
City = source.City
}
}
}
Are there any elegant solutions that I'm missing?
If you want to create mappings manually then you can use Select on the collection in the following way:
Some test data:
var persons = new List<Person>
{
new Person() {ID = 1, Name = "name1", Address = new Address() {ID = 1, City = "city1"}},
new Person() {ID = 2, Name = "name2", Address = new Address() {ID = 2, City = "city2"}},
new Person() {ID = 3, Name = "name3", Address = new Address() {ID = 1, City = "city1"}}
};
Mapping methods:
public static PersonDTO ToPersonDTOMap(Person person)
{
return new PersonDTO()
{
ID = person.ID,
Name = person.Name,
Address = ToAddressDTOMap(person.Address)
};
}
public static AddressDTO ToAddressDTOMap(Address address)
{
return new AddressDTO()
{
ID = address.ID,
City = address.City
};
}
Actual usage:
var personsDTO = persons.Select(x => ToPersonDTOMap(x)).ToList();
Keep in mind that if this was a real query is would not get executed as long as it was IQueryable, it would be executed once you materialize it (using ToList() for example).
However, I would consider using some framework which could do it (the mappings) for you automatically (if your mapping are as simple as provided example(.
Just use AutoMapper.
Example:
Mapper.CreateMap<Address, AddressDTO>();
Mapper.CreateMap<Person, PersonDTO>();
Your query will execute when the mapping is performed but if there are fields in the entity that you're not interested use Project().To<> which is available both for NHibernate and EntityFramework. It will effectively do a select on the fields specified in the mapping configurations.
Automapper is the best way .
For me, I use this for simple objects only, but I don't recommend it
public static class ObjectMapper
{
public static T Map<T>(object objfrom, T objto)
{
var ToProperties = objto.GetType().GetProperties();
var FromProperties = objfrom.GetType().GetProperties();
ToProperties.ToList().ForEach(o =>
{
var fromp = FromProperties.FirstOrDefault(x => x.Name == o.Name && x.PropertyType == o.PropertyType);
if (fromp != null)
{
o.SetValue(objto, fromp.GetValue(objfrom));
}
});
return objto;
}
}
And I call it like that wherever I want
var myDTO= ObjectMapper.Map(MyObject, new MyObjectDTO());
You could either use AutoMapper or write extension methods like these:
public static class PersonMapper
{
public static PersonDTO ConvertToDTO(this Person person)
{
return new PersonDTO { ID = person.ID, Name = person.Name, Address = person.Address.ConvertToDTO() };
}
public static IEnumerable<PersonDTO> ConvertToDTO(this IEnumerable<Person> people)
{
return people.Select(person => person.ConvertToDTO());
}
}
public static class AddressMapper
{
public static AddressDTO ConvertToDTO(this Address address)
{
return new AddressDTO { ID = address.ID, City = address.City };
}
public static IEnumerable<AddressDTO> ConvertToDTO(this IEnumerable<Address> addresses)
{
return addresses.Select(address => address.ConvertToDTO());
}
}
You could then map a Person object to a PersonDTO object like this:
public class Program
{
static void Main(string[] args)
{
Person person = new Person { ID = 1, Name = "John", Address = new Address { ID = 1, City = "New Jersey" } };
PersonDTO personDTO = person.ConvertToDTO();
Console.WriteLine(personDTO.Name);
}
}
I see the way you want to do it. I can propose you this solution:
public class PersonDTO
{
public int ID { get; set; }
public string Name { get; set; }
public AddressDTO Address { get; set; }
public static Expression<Func<Entities.Person, PersonDTO>> PersonSelector
{
get
{
return person => new PersonDTO()
{
ID = x.Id,
Name = x.Name,
Address = x.Address
.Select(AddressDTO.AddressSelector)
};
}
}
}
public async Task<PersonDTO> GetPerson(int id)
{
var person = await _personRepository.Get(id, PersonDTO.PersonSelector);
return person;
}
public async Task<TResult> Get<TResult>(int id, Expression<Func<Person, TResult>> selector)
{
var result = await _context.Persons
.Where(x => x.Id == id)
.Select(selector)
.SingleOrDefaultAsync();
return result;
}

Categories