Help mapping Domain model with EF4.1 fluent API? - entity-framework-4.1

I'm new to EF and the fluent API. My DBA decided he wanted to map the "primary" citizenship of a person with an Is_Primary flag in the citizenships table. So our DB looks something like this:
**Person Table**
Person_Id int identity (PK)
First_Name nvarchar(30)
...
**Citizenship_Table**
Citizenship_Person_Id int identity (PK)
Person_Id int
Country_Code char(2)
Is_Primary byte
...
**Country_Table**
Country_Code char(2) (PK)
Country_Name varchar(30)
...
I really don't want the Is_Primary flag in my domain model. Instead, I want my domain to look something like this:
public class Person
{
public int Id {get; set;}
...
public virtual ICollection<Country> Citizenships {get; set;}
public Country PrimaryCitizenship {get; set;}
}
public class Country
{
public int Code {get; set;}
public string Name {get; set;}
}
Is there a way to map this scenario?

It's not possible to map your domain model proposal to the given table structure. In my opinion it's also wrong - from the domain viewpoint - to introduce an Is_Primary flag into the Citizenship table (which is basically a join table between Person and Country).
The domain says that a person can have one primary citizenship (or perhaps none) but never many. This is not correctly expressed in the table structure: The Is_Primary column could be set for more than one country for a given person. It's also difficult to change the primary citizenship for a person because you would have to search for all entries in the citizenship table belonging to that person if there is another country marked as primary, then reset this flag before you set the flag for the new country.
This is correctly expressed in your model which means that the Person should have a foreign key to the Country table (either required or optional). Changing the primary citizenship would only require to set this FK to another value. Duplicate primary flags are impossible in this model.
If you change the table structure accordingly to the following ...
**Person Table**
Person_Id int identity (PK)
First_Name nvarchar(30)
PrimaryCountry_Code char(2) (FK to Country table)
...
**Citizenship_Table**
Person_Id int (PK)
Country_Code char(2) (PK)
**Country_Table**
Country_Code char(2) (PK)
Country_Name varchar(30)
...
... a mapping to your model would be possible:
modelBuilder.Entity<Person>()
.Property(p => p.Id)
.HasColumnName("Person_Id");
modelBuilder.Entity<Person>()
.Property(p => p.Name)
.HasColumnName("First_Name")
.HasMaxLength(30);
modelBuilder.Entity<Person>()
.HasMany(p => p.Citizenships)
.WithMany()
.Map(a => {
a.MapLeftKey("Person_Id");
a.MapRightKey("Country_Code");
a.ToTable("Citizenship");
});
modelBuilder.Entity<Person>()
.HasOptional(p => p.PrimaryCitizenship) // or .HasRequired(...)
.WithMany()
.Map(a => a.MapKey("PrimaryCountry_Code"))
.WillCascadeOnDelete(false);
modelBuilder.Entity<Country>()
.HasKey(c => c.Code)
.Property(c => c.Code)
.HasColumnName("Country_Code")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)
.HasMaxLength(2)
.IsFixedLength()
.IsUnicode(false);
modelBuilder.Entity<Country>()
.Property(c => c.Name)
.HasColumnName("Country_Name")
.HasMaxLength(30)
.IsUnicode(false);
Not a solution to your problem, but some food for discussion with your DBA.

Related

Building a Real-estate SQL schema: Explicit many-to-many relationship or alternatives

We have an instance of a "Listing" model, and we want to be able to assign it a "listing type," e.g., Sale / Rent / (or Both). If it is both, how can we have both in a list to query them by their "listing type". We've been thinking about doing a many-to-many relationship and creating a model for the "ListingType." That way, the "Listing" model can hold multiple instances of the "ListingType" model.
(We tried to do an implicit many-to-many relationship, but PlanetScale complained because they don't support foreign keys, so we're thinking about trying an explicit many-to-many relation, but we would like some guidance on alternatives. Thank you ahead of time.
(We are using Prisma & PlanetScale)
model Listing {
id Int #id #default(autoincrement())
name String
slug String #unique
bio String
price Int
userId Int
user User #relation(fields: [userId], references: [id])
status ListingStatus #default(ARCHIVED)
visibility ListingVisibility #default(PUBLIC)
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
listingTypes ListingOnListingType[]
}
model ListingType {
id Int #id #default(autoincrement())
name ListingTypeEnum #unique
ListingOnListingType ListingOnListingType[]
}
model ListingOnListingType {
listing Listing #relation(fields: [listingId], references: [id])
listingId Int
listingType ListingType #relation(fields: [listingTypeId], references: [id])
listingTypeId Int
assignedAt DateTime #default(now())
assignedBy String
##id([listingId, listingTypeId])
}

How to make a foreign key within a model

After reading the prisma documentation, I came across the #relation func to create a relation to another model. My goal is to make organization_id in my project table a foreign key in relation to my organization table. However upon generating the database i receive the error:
Error validating field `organization_id` in model `project`: The relation field `organization` on Model `project` is missing an opposite relation field on the model `organization`. Either run `prisma format` or add it manually.
--> schema.prisma:34
|
33 | created DateTime
34 | organization_id organization #relation(fields: id, references: org_id)
35 | content_title String
Here is the schema:
model organization {
org_id String #id
created DateTime
Name String?
credit_balance Decimal #db.Decimal(9,2)
}
model project {
id String #id
created DateTime
organization_id organization #relation(fields: id, references: org_id)
content_title String
content_id String?
client_key String
has_data Boolean
}
As the error message said, just simply format your schema, prisma will automatically fix the problem for you (Pressing Alt + Shift + F in VS code for example).
Or you can do it manually by adding project project[] to model organization
model organization {
org_id String #id
created DateTime
Name String?
credit_balance Decimal #db.Decimal(9, 2)
project project[] // Adding manually or using code formatter
}
model project {
id String #id
created DateTime
organization_id organization #relation(fields: id, references: org_id)
content_title String
content_id String?
client_key String
has_data Boolean
}

Prisma, prevent duplicated likes button?

I don't want duplicate rows to be added to the database from users who have already clicked the like. What should I do?
I've seen it said to use upsert, but isn't upsert create if it doesn't exist and update if it exists?
If you update, there will be no duplicates, but doesn't it waste database resources anyway?
Here's the schema which you would need to define a constraint in which a user can like a post only one time.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int #id #default(autoincrement())
name String
email String #unique
posts Post[]
Like Like[]
}
model Post {
id Int #id #default(autoincrement())
title String
published Boolean #default(true)
author User #relation(fields: [authorId], references: [id])
authorId Int
Like Like[]
}
model Like {
id Int #id #default(autoincrement())
post Post #relation(fields: [postId], references: [id])
postId Int
user User #relation(fields: [userId], references: [id])
userId Int
##unique([postId, userId])
}
Here a compound unique key is configured on the combination of postId and userId in Like Table.
If a user tries to like a post second time, the database would throw a unique constraint failed error.

How can I "filter" a ManyToMany child in Laravel Eloquent

First some context
I am working on a corporate application, in this application a user can have access to multiple companies.
The same user can be assigned to one employee in each company. To prevent the user from creating multiple user accounts, the user will be "connected" to a company and "company employee" via pivot tables.
The problem
I have a Company model in laravel with a users() attribute, this works as expected (because it returns all the users connected to the business via the pivot table).
In my User model I have an employee() attribute, this returns all the employees linked to the user via the pivot table, including the employees that are not linked to the requested business / do not have the same business_fk value as the requested Business ID.
I need to get the single employee that is connected to the requested business and user via the pivot tables.
So I think I need to filter the User -> Employee relation by the business_fk key in the users() attribute in the Company model, but I don't know how.
Model: Company
public function users()
{
return $this->belongsToMany(User::class, 'user_employee_pivot', 'employee_fk', 'user_fk')
->withPivot('ID');
}
Model: User
public function employee()
{
return $this->belongsToMany(Employee::class, 'user_employee_pivot', 'user_fk', 'employee_fk')
->withPivot('ID');
}
Simplified table structure
TABLE users
ID PK INT
username STRING
--------
TABLE company
ID PK INT
name STRING
--------
TABLE employee
ID PK INT
fullname STRING
company_fk FK INT (referencing to company table PK)
--------
TABLE user_business_pivot
ID PK INT
user_fk FK INT (referencing to user table PK)
company_fk FK INT (referencing to company table PK)
--------
TABLE user_employee_pivot
ID PK INT
user_fk FK INT (referencing to user table PK)
employee_fk FK INT (referencing to employee table PK)
If I'm not clear enough, please let me know. I will edit my question accordingly.
Employees you can fetch, by filtering the relationship, since it is belongsToMany employee should be employees.
public function getEmployeesByCompany(User $user, Company $company) {
return $user->employees()->where('company_fk', $company->id)->get();
}
This method could easily be done on the user model or similar, this is just an example.

Serialize object to DB and integrity

hello i want serialize my class to DB but i have a questions: how to do an easy referencial integrity?
this is an exsample: artist and his albums
one artist to do many albums:
public class Artist{
int id (primary key)
String name;
String surname;
getAlbums (get all albums from ID **select * from Albums where id = 2,3,4,5** and convert it to Albums class)
addAlbums (save only ID album into a list)
removeAlbum (remove an album from Album table)
public void serializeDB (serialize all this class to DB)
}
public class Album{
int id (primary key)
String name;
String type;
public void serializeDB (serialize all this class to DB)
}
this is table "Artist" (name, surname etc.. serialized to "Value")
PK ID, Sting Value
this is table "Album" (name, type etc.. serialized to "Value")
this tables are separated and not bound..
my question is:
is possible to find my database inconsistent? i have to do a foreingn key into Album table? is it a god way to serialize this data?