EF Core: Multiple enum discriminators per TpH - entity-framework-core

AbstractA
/ \
AbstractB AbstractC
| |
ConcreteBA ConcreteCA
There is an enum discriminator which defines whether it is "B" or "C" and an additional enum which describes whether its "BA" or "CA"
If enum 1 has value 1 and enum 2 has value 1 then it's "BA"
If enum 1 has value 2 and enum 2 has value 1 then it's "CA"
The Concrete classes BA and CA have class which links to a separate table
Can I create multiple discriminators to help define this for EF core?

Related

Adding elements on many-to-many relation

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?

What column type is required when using ElementCollection with jpa?

I have a simple class with a column that is technically a List
#Entity
#Table(name='hat')
class Hat {
#Id
String id = UUID.randomUUID()
#ElementCollection
List<String> wat
}
Right now when I use either a varchar(500) or character varying(500) the PSQL code that pulls this entity out of the database explodes
org.postgresql.util.PSQLException: ERROR: relation
"hat_wat" does not exist
An element collection is not stored in a single column. It's stored in a separate table, just as if you have a OneToMany, except there is no identifier for the many side. Suppose the Hat foo has 2 wats "bar" and "baz":
hat table: hat_wat table:
id hat_id wat
----- ---------------
foo foo bar
foo baz
That allows querying the hats to find, for example, all the hats having "bar" in their list of wats. Trying to store multiple string in a single column is really not a good idea. It violates the first normal form rules.

Map Table Column to Enum and Lookup Table to Enum

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.

How to create a PostgreSQL column that is a set of enum values?

I like that I can create new enum types in PostgreSQL. But what if I want a column value that is a set of enum values. Do I need to implement that manually with an integer column type and bitwise operators, or is there a way to keep using the enums by name?
CREATE TYPE foo AS ENUM ('none', 'loud', 'bright', 'cheap')
CREATE TABLE t (
id serial,
properties [set of foo?]
)
...
SELECT * FROM t;
1 loud
2 loud, cheap
3 bright
4 none
...
You can use an array:
CREATE TYPE foo AS ENUM ('none', 'loud', 'bright', 'cheap');
CREATE TABLE t (
id serial,
properties foo[]
);

EF 4 - associations with keys that dont match

We're using POCOs and have 2 entities: Item and ItemContact. There are 1 or more contacts per item.
Item has as a primary key:
ItemID
LanguageCode
ItemContact has:
ItemID
ContactID
We cant add an association with a referrential constraint as they have differing keys. There isnt a strict primary / foreign key as languageCode isnt in ItemContact and ContactID isnt in Item.
How can we go about mapping this with an association for contacts for an item if there isnt a direct link but I still want to see the contacts for an item?
One of the entities originates in a database view so it is not possible to add foreign keys to the database
Thanks
Stephen Ward
In order to create any relationship (in EF or any ORM for that matter) you have to have something to Join on.
Because at the moment your don't, you need to fabricate something...
The only option I can think of is to create a Relationship - using some of the same techniques described in here to create an SSDL view to back the relationship using a <DefiningQuery> based on a cross product join.
So if you have data like this:
ItemID | LanguageCode
1 | a
and this:
ItemID | ContactID
1 | x
1 | y
1 | z
Then your <DefiningQuery> should have T-SQL that produces something like this:
Item_ItemID | Item_LanguageCode | ItemContact_ItemID | ItemContact_ContactID
1 | a | 1 | x
1 | a | 1 | y
1 | a | 1 | z
Now because this is technically an Independent Association - as opposed to an FK association - you should be able to claim in the CSDL that the cardinality is 1 - * even though there is nothing in the SSDL to constrain it - and stop it from being a * - *.
Hope this helps
Alex