Entity Framework Class / Database Schema advice needed - c#

I asked a question recently, and quite frankly, from the answer that was given, I am second guessing my entire strategy/how I design the classes and database.
I have not yet used the virtual keyword, nor Icollection at all in any of my Entity Framework projects, and quite frankly, after reading about it in some of the examples I do not fully understand why it is needed, or how it works.
In a sample application, I have a simple design where there are three lists - people, notes and pictures. The relationships are such that a person can own multiple notes and pictures, as well as people having a logo which is a picture.
public class Person
{
public int ID { get; set; }
public string name { get; set; }
public Picture logo { get; set; }
}
public class Note
{
public int ID { get; set; }
public string Text { get; set; }
public Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
public string Path { get; set; }
public Person Owner { get; set; }
}
When I want to select a list of notes that a person owns, I simply perform db.Notes.Where(x=>x.owner=="y") on the notes object. I think I understand that if I were to use Icollection on the person class, I could instead perform something along the lines of db.person.select(x=> x.notes) to retrieve all the notes. Am I correct in this thinking?
If you were in my position with the relatively simple example above, how would you build the classes (involving ICollection, virtual or anything else)?
In addition and most importantly, the above is just an example, however in my actual application, I have used a very similar structure where I use my custom type as the "connector"/Foreign Key.
In many examples I have been reading, (in the above example) they would be using public int OwnerID instead of public person Owner. This has really thrown me and I am questioning my entire EF strategy. What are the differences?
Any advice would be greatly appreciated.

I think you are making this more difficult that is needed. If you were laying out regular classes you would relate them to each other rather than finding related id's and loading them separately which you are doing in your example.
public class Person
{
public int ID { get; set; }
public string name { get; set; }
public ICollection<Note> Notes { get; set; }
public ICollection<Picture> Pictures { get; set; }
public Picture logo { get; set; }
}
public class Note
{
public int ID { get; set; }
public string Text { get; set; }
public Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
public string Path { get; set; }
public Person Owner { get; set; }
}
So now say you have gotten your person object using the query
var person = _context.People.Where(m=>m.ID=randomIntWeWant).First();
We can get all related items as properties.
For Notes
person.Notes
For Photos
person.Photos
ICollection is related to lazy loading. By declaring a property as ICollection on one side, your are saying you have a many-to-one relationship between the objects. If you declare a property as ICollection on both sides, you are saying it is a many-to-many relationship. EF takes care of creating the tables that track that relationship.

Related

Lazy loading not working with 1 to 0 or 1 relationship

I have the following table design.
As can be seen here, there is a one to many relationship, with the many on the EpisodePatient side.
Then, I have the following classes.
public class EpisodeModel
{
public int ID { get; set; }
public virtual EpisodePatientModel EpisodePatient { get; set; }
}
public class EpisodePatientModel
{
public int EpisodePatientID { get; set; }
public virtual EpisodeModel Episode { get; set; }
}
I am setting up the relationship, in Entity Framework, to be a one to 0 or many. The reason for this is, I am selecting all EpisodePatients from a View, and I want the Episode to be Lazy loaded when accessed.
This is how I am setting up my relationship.
modelBuilder.Entity<EpisodePatientModel>().HasRequired(r => r.Episode).WithOptional(o => o.EpisodePatient);
I want this to act as a One to zero or many in my code, as an Episode will always have an EpisodePatient, and vice versa.
The problem I am facing is, when I load the EpisodePatient, and try to access the Episode linked item, it is always null, and Lazy loading does not occur.
What am I doing wrong here?
UPDATE
This is how I load the original EpisodePatient items.
this.DbContext.EpisodePatients.AsNoTracking();
I re-created your model but with data annotations like below and it workes fine:
public class EpisodeModel
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public virtual EpisodePatientModel EpisodePatient { get; set; }
}
public class EpisodePatientModel
{
public string Name { get; set; }
[Key, ForeignKey("Episode")]
public int EpisodeId { get; set; }
public virtual EpisodeModel Episode { get; set; }
}
Try without AsNoTracking(), because if you use it your context is not tracking and you can't include more data if you need.
And try change to relation one to many.
modelBuilder.Entity<EpisodePatientModel>().HasRequired<Episode>(s => s.Episode).WithMany(s => s.EpisodePatient);

Implementation of navigational properties for described data model

INTRODUCTION
I am trying to learn how to use Entity framework on my own ( Code First approach ) by solving a small task that I have designed myself.
In order to understand my problem, you must be familiar with the content of the task I mentioned, so i will provide relevant information in the below section.
RELEVANT INFORMATION:
I have invented the following data model for a small quiz:
Each player answers 10 questions.
Each question has 3 possible answers, user chooses one (by clicking on the radio button, for example)
Only one answer is correct, other 2 are wrong.
PROBLEM:
I got stuck at implementing POCOs, so I need your advice on how to implement them properly.
I believe I did the basic stuff properly and that my main problem is in implementing navigational properties.
MY EFFORTS TO SOLVE THIS:
I do not have much to show. Still, my habit is to always show everything I have, in order to ease the task of the community.
Therefore, these are my unfinished POCOs:
public class Answer
{
public int AnswerId { get; set; }
public string TextOfTheAnswer { get; set; }
}
public class Question
{
public int QuestionId { get; set; }
public string TextOfTheQuestion { get; set; }
}
public class Player
{
public int PlayerId { get; set; }
public string Name { get; set; }
}
During writing of this post, I am using Google to learn as much as possible to solve my problem. If I make any headway I will update this post accordingly.
QUESTIONS:
How should I implement navigational properties to mirror the relationships from my data model?
Additionally, is there a way for me to enforce some of the imposed restrictions ( each question has 3 options; player answers on 10 different questions; only one answer is correct answer to the question; and so on...)?
I apologize if these questions may sound trivial to someone experienced. I am just beginning with C# and Entity framework, and can not wait to write anything that works. I hope you can all relate. Thank you for your understanding.
As for the navigational properties, here's something to get you started (let me know if there's something I have missed):
public class Answer
{
[Key]
public int AnswerId { get; set; }
public string TextOfTheAnswer { get; set; }
public int QuestionId{get;set;}
[ForeignKey(nameof(QuestionId))]
public virtual Question Question{get;set;}
}
public class Question
{
[Key]
public int QuestionId { get; set; }
public string TextOfTheQuestion { get; set; }
public virtual ICollection<Answer> Answers{get;set;}
public int CorrectAnswerId{get;set;}
[ForeignKey(nameof(CorrectAnswerId))]
public virtual Answer CorrectAnswer{get;set;}
}
public class SessionQuestion
{
[Key]
public int SessionQuestionId { get; set; }
public int QuestionId{get;set;}
[ForeignKey(nameof(QuestionId))]
public virtual Question Question{get;set;}
public int PlayerAnswerId{get;set;}
[ForeignKey(nameof(PlayerAnswerId))]
public virtual Answer PlayerAnswer{get;set;}
public int TriviaSessionId { get; set; }
[ForeignKey(nameof(TriviaSessionId))]
public virtual TriviaSession TriviaSession{ get; set; }
}
public class TriviaSession
{
[Key]
public int SessionId { get; set; }
public int PlayerId { get; set; }
[ForeignKey(nameof(PlayerId))]
public virtual Player Player{ get; set; }
public virtual ICollection<SessionQuestion> SessionQuestions{get;set;}
}
public class Player
{
[Key]
public int PlayerId { get; set; }
public string Name { get; set; }
public virtual ICollection<TriviaSession> TriviaSessions{get;set;}
}
Basically, EF creates subclasses of your classes at runtime, so leaving the navigation properties virtual lets the EF classes override them and obtain the reference according to the key which resides in the property whose name is the string passed to the ForeignKey attribute's constructor (quite a mouthful, huh?).
One to many navigation is easily created via declaring a virtual generic ICollection property.
Note that this model enforces the fact that only one question is correct- by design. As for the other restrictions, it sounds like business logic rules, not something you should have your data layer enforce.

Should objects further down a hierarchy reference the root?

My stack is ASP.NET MVC 5, Entity Framework 6.1, code-first, SQL Server.
I'm working on an application which involves multiple schools, each of which have courses (each which have sections) and students. These form hierarchies of related objects, which are each rooted by a single school instance.
Basic layout so far:
One school has many courses and students
One course has many sections
Simplified versions of the models follow.
public class School
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
public class Student
{
public int Id { get; set; }
public int SchoolId { get; set; }
public virtual School School { get; set; }
public virtual ICollection<Enrolment> Enrolments { get; set; }
}
public class Course
{
public int Id { get; set; }
public string Title { get; set; }
public int SchoolId { get; set; }
public virtual School School { get; set; }
public virtual ICollection<CourseSection> CourseSections { get; set; }
}
public class CourseSection
{
public int Id { get; set; }
public int CourseId { get; set; }
public virtual Course Course { get; set; }
public virtual ICollection<Enrolment> Enrolments { get; set; }
}
There are other models and relationships involved, but this should be enough to form the basis for my question.
A course section is related to a course, which in turn relates to a school. Given a course section, I can determine the school it belongs to, e.g. var school = givenSection.Course.School. Conversely, given a school, I can get the course sections belonging to the school. In code it's just a couple of references away, while in the database it's just a couple of table joins. But it gets more interesting still. Consider the next model:
public class Enrolment
{
public int Id { get; set; }
public int StudentId { get; set; }
public int CourseSectionId { get; set; }
public virtual Student Student { get; set; }
public virtual CourseSection CourseSection { get; set; }
}
An Enrolment instance is the many-to-many bridge between students and course sections. Getting a list of enrolments for a school is multiple steps requiring multiple table joins. In a system which might become quite large in number of records, I worry about the efficiency of this setup. Then again, the application is configured for lazy loading, so maybe that's OK, I don't yet know enough about EF to be certain.
In the interest of simplicity for data retrieval, might it be ideal to reference the school from either of, or both, the CourseSection and Enrolment models? Extending it further, should all models in the hierarchy be able to directly reference the School they belong to?
No, that would break normalization. Your performance concern is valid but the solution cannot be known and should not be implemented prematurely and without measuring actual timings. In my opinion the data is the most important thing because chances are it will outlive the code. Therefore all else equal the good shape of the data should be e priority.

EntityFramwork Many-To-Many Code First

I'm not sure the best way to create this kind of relation ship. I have these two entities for this example.
Person & Address
public class Person
{
public string Name { get; set; }
public virtual ICollection<PersonAddressLink> HomeAddresses { get; set; }
public virtual ICollection<PersonAddressLink> WorkAddresses { get; set; }
}
public class Address
{
public string AddressString {get; set; }
public virtual ICollection<Person> People { get; set; }
}
and a link table, needed because it contains other info.
public class PersonAddressLink
{
public Address HomeAddress { get; set; }
public Address WorkAddress { get; set; }
public int SomeOtherInt { get; set; }
public string SomeOtherString { get; set; }
}
The problem is EF doesn't know how to separate the entities on person.HomeAddresses / person.WorkAddresses. I have tried mergin HomeAddress & WorkAddresses into a single collection like this:
public virtual ICollection<PersonAddressLink> WorkAddresses { get; set; }
but it still won't work.
I'm just looking for advice on how to lay something like this out to get it working with EF Code first.
I hope that makes sense.
Thanks
Late reply but I got the mapping correct by creating the table in SQL Management studio and using the reverse engineer functionality which generated the Code First. I need to use two separate entities.

Many to Many self Join with Entity Framework Code First

Consider this Poco:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
}
Now i want to implement a follow technique where a user may follow other users so basically its self Many to Many relationship
problem is i don't know how exactly i can achieve this in Entity Framework Code-First ?
I thought of a linker Table :
public class UserFollow
{
public int Id { get; set; }
public int Follower { get; set; }
public int Following { get; set; }
public DateTime FollowDate { get; set; }
}
i want to be able to get All Followers and Following from every User Object?
This is quite simple using EF code-first as you only need the User POCO:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Fullname { get; set; }
public ICollection<User> FollowedUsers { get; set; }
}
The collection means that a User is related to other users.
PS: I noted you added a timestamp in your solution example. To achieve that you should still add the collection changing the generic type to whatever suits your needs.
Hope it helps.

Categories

Resources