Entity Framework Core : one-to-many relationships - c#

Currently working on some learning in Entity Framework Core and would appreciate some help as I've been stuck for a decent while.
I've got a set of Models:
Users (Usernames, emails, names, etc)
Students (Each student has a one-one relationship with a user, this is just for readability)
This is the part that is stumping me:
School - Each school has ONE admin (user), a name and a collection/list of classes.
School Class - Has ONE teacher (user) and a collection/list of students.
The issue I have is how do I set these models up and create a new one without having to pass a whole user object into the class/school during creation? E.g. my model might look like
class School
{
[Required]
public string id {get; set;}
[Required]
public User Admin {get; set;}
[Required]
public List<Student> Students {get; set;}
[Required]
public List<SchoolClass> Classes {get; set;}
}
(Note this is just quickly thrown together for the sake of the question)
I can provide more detail if asked but I feel my whole current approach is slightly wrong.
Essentially whenever I add a user I should be able to add a student (optional), then I should be able to create schools but ideally I don't want to have to fetch/pass in a whole user object / list of classes when initially creating a school -> I'd just like to pass in the ID of a user to be the admin for example.
Then when I create a SchoolClass, it shouldn't need to create new users, I should just be able to add current users. Not entirely sure where to go with this.

You can make it nullable with the null operator (?)
public User? Admin {get; set;}
When you do your database calls, you'll add the Admin User object to the School object. However, it will not add the entire object to the database. It will only update the foreign key in the School table, and the Admin will not be marked for modification unless you explicitly do so.
Also be aware that attributes are gradually being phased out in favor of Fluent API.

Related

Extract one specific entry from a table?

I am currently looking for a way I can pass a foreing key to a table entry that is listed in one table,
and should be extracted in another table.
for example purposes I created this ?
public class Parent
{
public string Name {get; set;}
public virtual ICollection<child> Children
Public virtual ICollection<School> Schools {get; set;}
}
public class Child
{
public string Name {get; set;}
Public School Schoola{get; set;} // Which should be a school Name that the Parent Should know?
}
public class School
{
//ParentID
//ChildID
public string SchoolName {get; set;}
}
How do i give my Child instance a SchoolName that the Parent contains within the SchoolNames?
Children and SchoolNames are seperate tables - but child only need to know a specific entry..
Caveat
Your code does not work, since EF does not serialize collections of primitive types. EF Core does have value conversions but it is unclear what you're exactly looking for. I'm going to assume you meant to store these as actual School entities, since your question asks how to "extract one entry from a table".
For the sake of answering your question, I assume that your child should have a reference to the school entity, not a string property that's technically unrelated to the school entity itself, which would make it a question not related to Entity Framework and thus the question tags would be wrong.
I'll address both my assumption and your literal question, just to be sure.
If you need a relationship between a child and a school
From a purely database standpoint, there is no way to specify that an entity's (Child) foreign key should refer to an entity (School) which in and of itself has a foreign key to another entity (Parent). It simply doesn't exist in SQL and therefore EF cannot generate this behavior for you.
What you can do, is implement business validation on your code and refuse to store any child with a school that doesn't belong to its parent. Keep in mind, this requires you to load the parent and their schools every time you want to save a child to the database (because otherwise you can't check if the selected school is allowed for this child), so it will become a somewhat expensive operation.
However, that doesn't prevent the possibility for someone to introduce data into the database (circumventing your business logic, e.g. by a DBA) where this rule is violated but the FK constraint itself is upheld.
How you handle these bad data states is up to you. Do you remove those entries when you stumble upon them? Do you proactively scan the database once in a while? Do you allow it to exist but restrict your application's users to only choosing schools from the parent's scope? These are all business decisions that we cannot make for you.
If a child needs a school name without a relation to the school itself
At first sight, this seems to me to be a bad solution. What happens when the school's name changes? Wouldn't you expect the child's schoolname to also change? Because that's not going to happen in your current setup.
In either case, if you are looking to set a string property, that's trivial, you simply set the property. Presumably, your question is how to restrict the user's options to the child's parent's schools.
This restrictive list can be fetched from the database using the child's identifier:
var childID = 123;
var schoolsFromParent = db
.Children
.Where(c => c.Id == childId)
.Select(c => c.Parent.Schools)
.FirstOrDefault();
Note that this code works regardless of whether you have a School entity or a list of strings - though the type of schoolsFromParent will be different.
And then restrict your end user to only being able to pick from the presented options. Note that to prevent bad data, you should doublecheck the chosen name after the user has selected it.

C# MVC - Dynamic database architecture

I have an idea for a web app where I will want the user to create their own database through a web application, with their own table names and field types.
I thought about creating a database structure using Object Oriented Programming so that a pre-made database will support all kinds of Entities with custom properties. Something like this:
CustomType
{
public long TypeId {get;set;}
public string ActiveType {get;set;}
}
CustomProperty
{
public int wholeNumber {get;set;}
public string text {get;set;}
public bool boolean {get;set;}
public decimal dec {get;set;}
//Choosen Id of the type to work with
public long TypeId {get;set;}
public bool wholeNumber_ACTIVE {get;set;}
public bool text_ACTIVE {get;set;}
public bool boolean_ACTIVE {get;set;}
public bool dec_ACTIVE {get;set;}
}
CustomEntity
{
public string TableName {get;set;}
public CustomProperty Prop01 {get;set;}
public CustomProperty Prop02 {get;set;}
public CustomProperty Prop03 {get;set;}
public CustomProperty Prop04 {get;set;}
public CustomProperty Prop05 {get;set;}
}
The idea behind this is to let the user decide what they want their database to store, on a pre-made database for them to work with, without having to create it during runtime since this is a web app.
I believe I can manage it like this for them to store whatever they need, but I'm also thinking about the following issues:
How will I manage relationships when the user needs to link tables with Ids and foreign keys.
(I though about managing a public long ForeignId {get;set;} and just store the Id they need to associate).
How will I manage queries since tables will have CodeNames and each will have a different meaning for each person that sets it up.
(I though about, renaming the table during Runtime, but I'm afraid of errors and DB corruption).
Also thought about sending direct queries to create the database according to user's need, but then again non-experienced users can really mess up here or find it hard to manage.
How can I manage migration or DB changes with code instead of the use of PowerShell console.
If we have multiple users each with a unique database, but the same web app how can we manage webconfigs to work with this idea.
I know there's a lot of questions here, I'm looking for the best way to achieve this, having multiple users own their small web app through the internet using MVC pattern and lots of options through a browser.
I would recommend an Entity Attribute Value (EAV) pattern as a solution. With the EAV pattern, rather than creating new tables with new columns for every custom property you wish to store, you store those properties in rows. For example. Instead of every custom table being defined like this:
You define them like this instead:
This allows for flexible creation of multiple entities with multiple properties. The classes in your business logic will then be Entity classes with a collection of Property objects.
In case you haven’t spotted the trade-offs already, the limitation of using the EAV model is the inability to specify field types (int, varchar, decimal etc.), infact, all your property values will be stored as a single type (usually strings).
There are a number of ways to address this. Some handle all the validation etc. in the business logic, other create Field tables per type, so based on my example, rather than having just one EntityFields table, you’ll have multiple, separated by type.

Issue with ASP.NET MVC 5 Web Application Entity Framework

I am working on MIT open source license example ASP.NET MVC web applications, and adding them as github public repos, I am also planning to have private github repos for my applications I plan to make money with in the future. I have a developer account with github.com.
I created a BOOKS MVC 5 web application using a TSQL script I was provided during a previous job interview some years ago, and am using GUID for the primary key ID fields with a default value of NEWID(), instead of an INT with IDENTITY, the solution is an ASP.NET MVC 5 web application with database first Entity Framework. I am using LocalDB for my SQL Server with this project, the script to create and populate the database is in my SQL-Server repo and is called BOOKS_Create.sql
I published the solution to my GitHub at the following URL:
https://github.com/ABION-Technology/Books
The TSQL scripts are available in the following repo:
https://github.com/ABION-Technology/SQL-Server
I added links the the shared layout view to show the index view for all Authors in the database, and also links to Index views for the TITLE and SALE EF models.
THe AUTHORS link works just fine, and lists all the authors in the database. But when I click the TITLES link, I get a SQL Exception of 'Author_ID' invalid column name, I did a search through my entire solution and did not find any variable named 'Author_ID' and did not find a C# class with a property called 'Author_ID". So this issue has me very confused there does not appear to be a good way to debug this issue. Thanks.
EF will follow some default conventions to work out FK relationships. The error you are seeing is due to Author having a Titles collection and EF is attempting to automatically set up the 1-to-many between the two. It's expecting to find an "Author_ID" on the Title, which doesn't exist because your schema is set up with a joining table called TitleAuthor.
To resolve this, you will need to map the TitleAuthor entity, in which the Author will contain a collection of TitleAuthors which refer themselves to an Author and Title entity. EF can automatically map joining tables given those tables consist of just the two FKs. As soon as you want to introduce additional fields, then you need to define the joining entity.
public class TitleAuthor
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; internal set;}
public virtual Title Title { get; internal set;}
public virtual Author Author { get; internal set;}
// add other properties as needed..
}
So from your Author entity:
public virtual ICollection<TitleAuthor> Authors {get; internal set;} = new List<TitleAuthor>();
To access the titles for the author:
author.Titles.Select(x => x.Title);
I would recommend reading up on many-to-many mapping with EF. I invariably use deliberate mapping with EF rather than relying on it's conventions. It just helps make it more predictable.
If you are using defaults for PKs then you need to tell EF via the DatabaseGenerated attribute. This isn't needed for read operations, but it will be needed when you go to insert records.
Also, with SQL Server, consider using NewSequentialId() as the default for your UUID PKs. These are more index-friendly than NewId().
The above example using internal (private works too) setters to promote DDD style use of entities. Public setters can lead to misuse/abuse of entities in the sense that the context will diligently attempt to save whatever you set. It's generally a good idea to restrict functionality that would alter an entity's state to a method in the entity with required arguments to be validated, or a repository. I use internal scoping to allow unit tests to still initialize entities. (leveraging InternalsVisibleTo between domain and unit test assemblies)
Reason is you are getting that Author ID error is, you have list of Titles in Author Class. Then there should be relationship between Author and Title entities, which is not exists in your data context. Comment public virtual ICollection<Title> Titles { get; set; } . And it should work.
Reason for you cant search this attribute is, its automatically generated by entity framework. (TableName_PrimaryKey)
If you want to keep this, create relationship in database using foreign keys and add that to your data context also. You may refer this

Solution layers / Folder/project hierarchy .NET project

I'm creating project which will parse .html to database (kind of sqlite or other, it's not important yet). Database will has many tables, relationship and to understanding the schema will be some difficult, well I'll show you more simple Schema.
For example:
Models:
Subject: SubjectId, Name
Teacher: TeacherId, SubjectFk, Name, Surname
ClassRoom: ClassRoomId, year
Student: StudentId, ClassRoomFk, Name, Surname
Relations (it's not important!):
One subject is leading by multiple teacher, one teacher leads only one subject
One ClassRoom contains many students, one students belongs to only one classRoom
Is unique pair: TeacherId, ClassRoomId (In one classRoom can be only one object carried by a particular teacher, many teachers can not teach in a classRoom of the same subject, iam not sure...but it's not important).
Now I build a project hierarchy:
ParseData - solution name
ParseData.Repository - it contains App.Config which contains app settings to root folder where exist data.
ParseData.Domain - Classes for data model which will be parse, for example:
public class Student
{
public int StudentId {get;set;}
public string Name {get;set;}
public string Surname {get;set;}
public int ClassRoomFk {get;set;}
}
public class ClassRoom
{
public int ClasRoomId {get; set;}
public List<Student> Students {get;set;}
}
ParseData.Core - contains all algorithm and Classes which will read file from path and convert data to class model, for example:
public class StudentParse : IEntity<Student>
{
public Student Student {get; set;}
public StudentParse(string filePathWithDataForStudentsFromParticularClass, int ClassRoomFk) { (...) }
/* All methods, which will parse data to StudentModel */
}
public class ClassRoomParse : IEntity<ClassRoom>
{
public ClassRoom ClassRoom {get;set;}
public ClassRoomParse(string filePathWithClassRoomsData) { (...) }
/* All methods, which will parse data to ClassRoomModel */
}
public interface IParser
{
string filePathToMainFile {get;set;}
List<ClassRoom> Start();
}
ParseData.UI - Console application. Here i can write some code which will present me results from featching data. For example:
IParser parser = new Parser(Repository.MainFilePath);
List<ClassRoom> parser.Start();
/* LINQ or other actions..save to file or something else */
I'm searching a knowledge how could I organize my solution to best practices. I am open to criticism about my approach and inexperience.
Another option besides a ReadRepository and WriteRepository I mentioned earlier is to apply the Extract, Transform, Load (ETL) method. This is a clearer approach, mainly because the data is one-way only.
The solution would have a ParseData.Extract project (the ReadRepository) that loads the data from the HTML files into DTOs which match the data structure of the HTML files. A ParseData.Transform project would transform the DTOs to the database models (the entities if you're using Entity Framework for example). A ParseData.Load project would then serve as the WriteRepository mentioned earlier to save the entities to the database.
The ParseData.UI project can still be used to orchestrate the ETL process.
What you have are two data sources: One to read from (the HTML files) and one to write to (the database). Ideally you would hide how the files are read from disk and how they are persisted to the database. That is what the Repository pattern is for.
Your application has a clear purpose: It should import the data from the files to the database. Creating one repository would make the architecture of your application less clear. It would first 'read' from the repository only to 'write' it to the same repository again.
Because of that I suggest creating two Repository projects: One ReadRepository and one WriteRepository. That would make the console application project really simple: Instantiate the repositories, query the ReadRepository and save to the WriteRepository. The Core project would in effect become the ReadRepository. Both repositories would use the Domain objects.
I would also suggest to let the UI console application decide where the files are stored. So store the location in the App.config of the console application. That way you can perhaps overwrite the file location with a command-line parameter.

Change display name of entity property when accessed through relationship

I have a Fare entity and a Ticket entity. Fares can belong to many tickets, tickets can only have one fair. In the fair entity I'm defining the name as so:
[Required, MaxLength(50), MinLength(3)]
public string Name { get; set; }
The framework uses Name as the field name for the fare name, which is perfectly fine when I'm performing CRUD on fares. However, when I want to show the fare that's linked to a ticket, the framework still calls this property Name, and now it doesn't make sense. Take this screenshot as an example:
This page is showing a list of tickets, and the second column (called name) should be called "Fare".
I know I can change this by adding a Display(Name = "Fare") attribute to my Fare entity, but then the CRUD stuff for fares wouldn't make sense.
The only other way I can think of solving this is using a view model for this page, is there a better way?

Categories