One-to-one entity framework - sql

I am doing the entity framework code first to set up my database.
I have two classes where their relationships are one-to-one, Lecturer & LoginInfo.
public class Lecturer
{
public int LecturerId { get; set; }
public string FullName { get; set; }
public int UserId { get; set; }
public LoginInfo LoginInfo { get; set; }
}
public class LoginInfo
{
public int UserId { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public bool ChangePassword { get; set; }
public Lecturer Lecturer { get; set; }
}
So for my entity framework, I have written this for the one-to-one relationship.
modelBuilder.Entity<Lecturer>()
.HasOne(input => input.LoginInfo)
.WithOne(input => input.Lecturer)
.HasForeignKey<Lecturer>(input => input.UserId);
From the code above, does it mean that Lecture has one LoginInfo, LoginInfo with one Lecturer and Lecturer has a UserId as a foreign key?
Another question would be, for this one to one relationship, do I have to write another set of code for LoginInfo like this:
modelBuilder.Entity<LoginInfo>()
.HasOne(input => input.Lecturer)
.WithOne(input => input.LoginInfo)
.HasForeignKey<LoginInfo>(input => input.LecturerId);
I am just a beginner trying to learn, thanks for helping :).

From the code above, does it mean that Lecture has one LoginInfo, LoginInfo with one Lecturer and Lecturer has a UserId as a foreign key?
Correct. It also means that LoginInfo is the principal and Lecturer is the dependent end of the relationship. Also since the FK property is not nullable, the relationship is required, i.e. in order to create Lecturer you have to create LoginInfo first, which is not associated with another Lecturer.
Also note that since the UserId is not following the default convention for PK name, you should explicitly configure it as PK of the LoginInfo:
modelBuilder.Entity<LoginInfo>()
.HasKey(e => e.UserId);
Another question would be, for this one to one relationship, do I have to write another set of code for LoginInfo like this
No. Single relationship requires single configuration and single FK. If you do so, you would be defining a second relationship, which also would create circular dependency between the entity, which should be avoided in general. The first fluent configuration fully defines the desired relationship and is enough to handle loading related data and other CRUD operations.
For more info about terms, relationship types and configuration, see Relationships section of the EF Core documentation.

Related

EF Core composite foreign key and constraint

In my project I have noticed that I will be have a lot of dictionaries with the same structure:
shortcut
full name for tooltip
which will be used on many different business forms.
I started to thing that there is no sense to keep all of them in separate tables.
It is better to keep all of them in one table and provide an additional column (DictionaryType) which will separate them in the case of asking the database for data?
So one repository with such method
public class DictionaryEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public string FullName { get; set; }
public DictionaryType Type { get; set; }
}
public async Task<IEnumerable<DictionaryEntity> GetDictionaries(DictionaryType type)
{
return await _dbContext.Dictionaries.Where(d => d.DictionaryType == type).ToArrayAsync();
}
If new dictionaries appear, I need to only extend DictionaryType and I don't need to worry about database changes or repo/service/controller changes.
For now it is nice and easy, but... I would like to configure foreign key in business entities in that way:
public class CarEntity
{
public Guid Id { get; set; }
public Guid ModelTypeId { get; set;}
public DictionaryEntity ModelType { get; set;}
public Guid PetrolTypeId { get; set;}
public DictionaryEntity PetrolType { get; set;}
}
How to configure in EF Core, foreign key in that way where:
CarEntity.ModelTypeId points to DictionaryEntity.Id and DictionaryEntity.Type = DctionaryType.ModelType ?
CarEntity.PetrolTypeId points to DictionaryEntity.Id and DictionaryEntity.Type = DctionaryType.PetrolType ?
I read, that there is something like a composite foreign key, so I could do FK on { dict.Name, dict.Type } but it demands from me to keep in CarEntity as many properties as composite foreign key have.
Is there a chance to do unique constraint across multiple tables ?
Something like this:
modelBuilder.Entity<CarEntity>()
.HasCheckConstraint("CK_ModelType", "[ModelTypeId] IS NOT NULL AND [Document].[Type] = 'ModelType'", c => c.HasName("CK_ModelType_Dictionary"));

Entity Framework Core Code First Model Navidation Properties

I have a Web API that uses entity framework. I have several tables there were created using the code first setup. My Competitions class is defined below.
Everything works great and I'm able to get my Competitions table data along with all the data in the navigation properties that are returning a collection. However, I'm not able to get any values for the CompetitionTypes and Users navigation properties. OwnerId references UserId in the Users table.
How would I get the linked data in my CompetitionTypes and Users table? I basically want the same thing as the three collection navigation properties, except that CompetitionTypes and Users would only return one row.
public partial class Competitions
{
[Key, Required]
public int CompetitionId { get; set; }
public int CompetitionTypeId { get; set; }
public int OwnerId { get; set; }
public string CompetitionName { get; set; }
public CompetitionTypes CompetitionTypeId { get; set; }
public Users UserId { get; set; }
public ICollection<Participants> Participants { get; set; }
public ICollection<ResultStats> ResultStats { get; set; }
public ICollection<Results> Results { get; set; }
}
}
EF auto-matches FK properties with navigation properties based on conventions. Namely, it expects FK properties to be named the same as navigation properties, just with Id at the end. In other words, for it to automatically match up OwnerId, you'd need a navigation property like:
public User Owner { get; set; }
Since your navigation property is UserId, it's actually looking for a property named UserIdId.
If you don't want to follow conventions, then you must either use the ForeignKey attribute or fluent config to tell EF which property belongs with which.
That said, there's some pretty major issues with your naming of things here. First, entities should always be singular User, not Users. Second, you should not have navigation properties that end with Id: e.g., User, not UserId. Only actual PK or FK properties should end with with Id. Finally, don't prefix properties on your entity with the entity name. This last one is mostly for readability. Which is more natural: competition.Id or competition.CompetitionId? Likewise with CompetitionName; it should just be Name. And, for what it's worth, you don't need Required for either a primary key or a non-nullable type (such as int). In either case, the property is required by default.

Fluent Nhibernate Cascade.None() results in HasOne foreign key relationship not being persisted

I'm having issues with Nhibernate persisting a HasOne Relationship for one of my entities with Cascade.None() in effect. My domain model involves 4 classes listed below.
public class Project
{
public virtual int Id {get;set;}
public virtual IList<ProjectRole> Team { get; protected set; }
}
public class ProjectRole
{
public virtual int Id { get; set; }
public virtual User User { get; set; }
public virtual Role Role { get; set; }
}
public class Role
{
public virtual int Id { get; set; }
public virtual string Value { get; set; }
}
public class User
{
public virtual int Id { get; protected set; }
public virtual string LoginName { get; set; }
}
So basically we have projects, which have a list of ProjectRoles available from the Team property. Each ProjectRole links a User to the specific Role they play on that project.
I'm trying to setup the following cascade relationships for these entities.
project.HasMany<ProjectRoles>(p=> p.Team).Cascade.All()
projectRole.HasOne<Role>(r => r.Role).Cascade.None()
projectRole.HasOne<User>(r => r.User).Cascade.SaveUpdate()
I've used fluent nhibernate overrides to setup the cascades as above, but I'm finding that the line
projectRole.HasOne<Role>(r => r.Role).Cascade.None()
is resulting in the ProjectRole.Role property not being saved to the database. I've diagnosed this be looking at the SQL Generated by Nhibernate and I can see that the "Role_id" column in the ProjectRoles table is never set on update or insert.
I've also tried using
projectRole.HasOne<Role>(r => r.Role).Cascade.SaveUpdate()
but that fails as well. Unfortunately leaving it Cascade.All() is not an option as that results in the system deleting the Role objects when I try to delete a project role.
Any idea how to setup Cascade.None() for the ProjectRole-> Role relationship with out breaking persistence.
HasOne is for a one-to-one relationship which are rare. You want to use References to declare the one side of a one-to-many relationship. Making some assumptions about your domain model, the mapping should look like:
project.HasMany<ProjectRoles>(p=> p.Team).Inverse().Cascade.AllDeleteOrphan()
projectRole.References<Role>(r => r.Role);
projectRole.References<User>(r => r.User);
See also this question about the difference between HasOne and References.

Fluent NHibernate one-to-many with intervening join table?

I'm having trouble getting the Fluent Nhibernate Automapper to create what I want. I have two entities, with a one-to-many relationship between them.
class Person
{
public string name;
IList<departments> worksIn;
}
class Department
{
public string name;
}
The above is obviously bare bones, but I would be expecting to generate the fleshed out schema of:
Person{id, name}
Department{id, name}
PersonDepartment{id(FK person), id(Fk Department)}
Unfortunately, I am instead getting:
Person{id, name}
Department{id, name, personid(FK)}
I don't want the FK for Person included on the department table, I want a separate join/lookup table (PersonDepartment above) which contains the primarykeys of both tables as a composite PK and also Fks.
I'm not sure if I am drawing up my initial classes wrong (perhaps should just be LIst workIn - representing ids, rather than List worksIn), or if I need to manually map this?
Can this be done?
The way the classes have been structured suggests a one-to-many relationship (and indeed that's how you describe it in your question), so it should not be a surprise that FNH opts to model the database relationship in that way.
It would be possible, as you suggest, to manually create a many-to-many table mapping. But, is this definitely what you want?
I tend to find that pure many-to-many relationships are quite rare, and there is usually a good case for introducing an intermediate entity and using two one-to-many relationships. This leaves open the possibility of adding extra information to the link (e.g. a person's "primary" department, or perhaps details of their office within each of their departments).
Some example "bare-bones" classes illustrating this kind of structure:
public class Person
{
public int Id { get; set;}
public string Name { get; set;}
public IList<PersonDepartment> Departments { get; set; }
}
public class PersonDepartment
{
public int Id { get; set; }
public Person Person { get; set; }
public Department Department { get; set; }
public bool IsPrimary { get; set; }
public string Office { get; set; }
}
public class Department
{
public int Id { get; set; }
public IList<PersonDepartment> Personnel { get; set; }
public string Name { get; set; }
}

Fluent Nhibernate many-to-many where table has multiple columns

I am trying to to map a many to many relationship using Fluent NHibernate.
I have a table User and a second table Organization. The association table is UserOrganization which contains the UserId and OrganizationId. The UserOrganization table also contains a few other fields (YearBegan, YearEnd).
How would I go ahead and map those using fluent mapping.
Thanks
You should probably make UserOrganization its own entity that contains those fields. That also gives you more flexibility in terms of cascading updates and deletes.
public class UserOrganization {
public virtual User User { get; set; }
public virtual Organization Organization { get; set; }
public virtual DateTime YearBegan { get; set; }
public virtual DateTime YearEnd { get; set; }
}