I am making a project where (from the perspective of school) you can calculate each student average.
You can register a student (first entity) on a screen and subjects (second entity) on another screen.
Student has name, email, grade and average as atributes, and Subjects has name. They relate many-to-many with each other.
I am trying to create a copy of subjects list to each student, then on each student i can register a grade to each subject. Like this:
Model concept
Model:
!https://imgur.com/gmXyR5j
I've created a singleton of subjects since it is used more than one location:
import Foundation
import CoreData
class SubjectsManager {
static let shared = SubjectsManager()
var subjects: [Subject] = []
func loadSubject(with context: NSManagedObjectContext) {
let fetchRequest: NSFetchRequest<Subject> = Subject.fetchRequest()
let sortDescritor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescritor]
do {
subjects = try context.fetch(fetchRequest)
} catch {
print(error.localizedDescription)
}
}
func deleteSubject(index: Int, context: NSManagedObjectContext) {
let subject = subjects[index]
context.delete(subject)
do {
try context.save()
subjects.remove(at: index)
} catch {
print(error.localizedDescription)
}
}
private init() {
}
}
And, on my student screen, i've tried many thing but nothing is working.
The to-many relation of student with subject is called registeredSubjects
I've created a NSSET called subjectsManagerSet to get values from the singleton, but it not working. Here what i've tried so far:
subjectManagerSet.addingObjects(from: subjectsManager.subjects)
Also tried to create a for loop of subjectManager.subjects to add on subjectManagerSet but it's not working too.
About errors, when i get samples from xcode output, it keep showing that subjectManagerSet did not get values from subjectManager.subject
Error message:
2019-09-26 20:38:16.983725-0300 MyAverage[1734:62290] Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file /Users/vitorgomes/Desktop/Mentorizacao/MyAverage/MyAverage/Controllers/StudentsViewController.swift, line 119
(lldb) po subjectManagerSet
0 elements
The expected result is that i want a copy of subjects for each student instance, then i can add grades for each subjects for each student.
The expected result is that i want a copy of subjects for each student
instance, then i can add grades for each subjects for each student.
I am not addressing your problem as stated at the beginning of your question, but the one stated at the end, per the quote above.
I would encourage you to reconsider the structure of your model.
Perhaps something like this?
In this proposed model, you're able to assign to an object of the entity Enrolment:
a grade (and date) via these attribute properties;
a student via the one-to-many relationship property with a Student entity;
a subject via the one-to-many relationship property with a Subject entity.
In the following examples, I assume Core Data generated NSManagedObject subclasses - that is - in the Data Model Inspector, set the value for Codegen = Class Definition (default).
(Personally and as an aside, I prefer to manually write NSManagedObject subclasses for each of my entities and use Set rather than the NSSet, as I find it subsequently a lot easier to maintain type integrity in my code. But I have not done that here as most people who are new to Core Data will use the default for Codegen noted above.)
You're able to access these values in the following manner...
let student = Student()
print("Student name is: \(String(describing: student.name))")
if let studentEnrolments: NSSet = student.studentEnrolments {
for item in studentEnrolments {
if
let enrolment = item as? Enrolment,
let subjectName: String = enrolment.subject?.name {
print("Subject name for this student is: \(subjectName)")
}
}
}
Its easy to assign a subject enrolment to a student...
let enrolment = Enrolment()
let subject = Subject()
enrolment.subject = subject
student.addToStudentEnrolments(enrolment)
Then now or later, a grade can be applied to the enrolled subject...
let grade: String = "A"
enrolment.grade = grade
Of course the average becomes a mathematical function based on the sum of all grades for each student, divided by the count. This is in my humble opinion, better constructed as it is needed, rather than saved as an attribute with each Student object.
Update
Im updating my answer to include a little database theory to explain my proposed object model.
According to Wikipedia, Database normalisation is...
the process of structuring a relational database
in accordance with a series of so-called normal forms in order to
reduce data redundancy and improve data integrity.
What does this practically mean to me? It means breaking my data down into its most discrete and unique parts, so that, in theory, I never need to enter any unique piece of data more than once.
Let me use a simple table example as a means of explaining this, as it might be set out in a spreadsheet (or your model concept):
Original Data
TABLE 1
A B C
1 STUDENT SUBJECT GRADE
2 Student1 Mathematics 8.8
3 Student1 Physics 7.0
4 Student1 Biology 6.0
5 Student2 Mathematics 5.0
6 Student2 Physics 9.0
7 Student2 Biology 7.0
Normalised Data
TABLE 1 TABLE 2 TABLE 3
A B C A B A B
1 STUDENT SUBJECT GRADE ID STUDENT ID SUBJECT
2 1 1 8.8 1 Student1 1 Mathematics
3 1 2 7.0 2 Student2 2 Physics
4 1 3 6.0 3 Biology
5 2 1 5.0
6 2 2 9.0
7 2 3 7.0
The normalised data uses relationships between the three tables. It stores the ID (as a primary key) of each STUDENT and each SUBJECT, instead of the actual words. This is obviously far more efficient in many different ways, including but not limited to: bytes of stored data, ability to index, speed of data retrieval.
When you set a relationship property in your Core Data object model graph, you are doing the same thing...
So for your example, the Core Data object model graph Entity replaces TABLE. The Core Data framework automagically inserts a primary key column into the SQLite database for us when it constructs the tables and later a primary key unique integer when we programmatically add rows (records, a.k.a instances of an entity). While we don't have direct access to that as a developer (using Core Data), the Core Data framework allows us to build one-to-one, one-to-many and many-to-many relationships between two entities, that achieves the same outcome.
Enrolment Student Subject
A B C A B A B
Rel. Rel. Att. Rel. Att. Rel. Att.
∞ ∞ 1 1
Z_PK Student Subject grade Z_PK name Z_PK name
1 1 1 8.8 1 Student1 1 Mathematics
2 1 2 7.0 2 Student2 2 Physics
3 1 3 6.0 3 Biology
4 2 1 5.0
5 2 2 9.0
6 2 3 7.0
Att. = Entity Attribute;
Rel. = Entity Relationship;
∞ = many side of one-to-many Relationship (<<-);
1 = one side of one-to-many Relationship (-->)
Any questions, let me know?
Related
I have the following database in PostgreSQL
EDIT: there is an Unique Key in PizzaTopping built with the fields Id_Pizza, Id_Topping
As you can see it's a Many-To-Many relationship.
When I ask Linqpad 6 to scaffold from my Database I have the following result:
The same result I have it when I use the EFCore Power Tools when I ask them to reverse engineer my database.
Reading from various sources, I've found, to ask EFCore to get the list of the toppings of the pizzas I should do something like that:
Pizzas.Where(p=>p.Description=="Margherita")
.Include(p=>p.PizzaToppings)
.ThenInclude(p=>p.IdToppingNavigation)
The query EFCore 5 returns this query:
SELECT P."Id_Pizza",
P."Description",
T0."Id_PizzaTopping",
T0."Id_Pizza",
T0."Id_Topping",
T0."Id_Topping0",
T0."Description"
FROM "Pizza" AS P
LEFT JOIN
(SELECT P0."Id_PizzaTopping",
P0."Id_Pizza",
P0."Id_Topping",
T."Id_Topping" AS "Id_Topping0",
T."Description"
FROM "PizzaTopping" AS P0
INNER JOIN "Topping" AS T ON P0."Id_Topping" = T."Id_Topping") AS T0 ON P."Id_Pizza" = T0."Id_Pizza"
WHERE P."Description" = 'Margherita'
ORDER BY P."Id_Pizza",
T0."Id_PizzaTopping",
T0."Id_Topping0"
Since I want to return a list with Pizza, topping like:
margherita, mozzarella
margherita, tomato sauce
marinara, garlic
marinara, tomato sauce
I tried with add .Select(topping=>topping.description) but it gets the pizza description.
So how can I take the toppings descriptions who they are in the topping table?
I tried to put a .Select() after the .ThenInclude() but I still see the Pizza entity and the p.PizzaToppings does not contain the property description of the topping table.
In this case you do not need Include but SelectMany with custom projection:
var query =
from p in Pizzas
from pt in p.PizzaToppings
select new
{
Pizza = p.Description,
Topping = pt.IdToppingNavigation.Description
}
I am using Entity Framework 6, just release, and need to:
1 - Map a table column to an Enum;
2 - Map a lookup table (has two columns: Id and Name) to an Enum.
Is this possible in Entity Framework 6?
Thank you,
Miguel
You typically don't map table to an enum type. You just define an enum type based on what you have in your lookup table and use it without including these tables in the model. For instance for the Northwind.Categories table:
ID Name Description
1 Beverages Soft drinks, coffees, teas, beers, and ales
2 Condiments Sweet and savory sauces, relishes, spreads, and seasonings
3 Confections Desserts, candies, and sweet breads
4 Dairy Products Cheeses
5 Grains/Cereals Breads, crackers, pasta, and cereal
6 Meat/Poultry Prepared meats
7 Produce Dried fruit and bean curd
8 Seafood Seaweed and fish
You would create the following enum type
public enum Categories
{
Beverages = 1,
Condiments = 2,
Confections = 3,
Dairy_Products = 4,
Grains_Cereals = 5,
Meat_Poultry = 6,
Produce = 7,
Seafood = 8,
}
(make sure that enum values correspond to the values in your database) and you would use it in your app without including the Categories table - i.e. you would use this enum type as the type of the properties that are foreign keys to the Categories table in the database. Alternatively - e.g. if you need descriptions - you would create an entity corresponding to the Categories table and use the enum (as defined above) as the key property type. Then again you would use the enum type for all properties that in the database are foreign keys to the Categories table.
I’m developing an application where I’m using Entity Framework. I have a table A and an autogen entity from this table class A
Public Class A
ID As Integer
Sum As Integer
TotalSum As Integer
LastPayment As Integer
NewPayment As Integer
.
.
.
End Class
In addition to my table I have a view that calculates and returns all the rows from table A where totalSum and LastPayment meets some conditions (table has 50 rows, view returns 35 rows).
Can I use this view together with my entity class A? When I use my entity class A I can say
unitOfWork.ARepository.Filter(Function(p) p.ID = Me._id, , )
but this will get the rows from the table without the calculations/filtering done by the view, let say it returns 50 row. I want to say
unitOfWork.ARepository.Filter(Function(p) p.ID = Me._id, , )
but I want to get the filtered rows from the view instead, this will return 35 rows instead of the 50. But I do not want the view to be an entity in my model, because I then will have two classes A (from table) and B (from view) that looks exactly the same. How can I solve this?
you can write a code corresponding view with entity framework in VB or C#. it's better than use 2 model that are equal.
I have a model in EF4. There are two hierarchical entities in this model that have a 0..1->many relationship with themselves (they have a ParentId field), so each has a Parent (same type) and a Children (collection of same type) property. their definitions are almost the same.
one of them works fine but in the other one, Children property always contains the first child event if it has multiple children.
Update:
I did a test with Sql Server Profiler:
var p = from item in context.Entity1.Include("Children")
where item.Id == 15
select item;
var q = from unit in context.Entity2.Include("Children")
where unit.Id == 239
select unit;
both of these Linq statements generate sql queries that return 3 records because Entity1 with id 15 and Entity2 with Id 239 both have 3 children. q.First().Count returns 1 but p.First().Count returns 3.
Consider two tables Bill and Product with a many to many relationship. How do you get all the bills for a particular product using Entity Sql?
Something like this
SELECT B FROM [Container].Products as P
OUTER APPLY P.Bills AS B
WHERE P.ProductID == 1
will produce a row for each Bill
Another option is something like this:
SELECT P, (SELECT B FROM P.Bills)
FROM [Container].Products AS P
WHERE P.ProductID == 1
Which will produce a row for each matching Product (in this case just one)
and the second column in the row will include a nested result set containing the bills for that product.
Hope this helps
Alex
You need to use some linq like this;
...
using (YourEntities ye = new YourEntities())
{
Product myProduct = ye.Product.First(p => p.ProductId = idParameter);
var bills = myProduct.Bill.Load();
}
...
This assumes that you have used the entitiy framework to build a model for you data.
The bills variable will hold a collection of Bill objects that are related to your product object.
Hope it helps.