I want to create a database in PostgreSQL and API for my app. I've created Model and API in EF Core 1.0.0. Model exists in separate library.
public class Architect : IEntity
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public ICollection<Project> Projects { get; set; }
}
public class Project : IEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTimeOffset CreatedAt { get; set; }
public ICollection<Company> Companies { get; set; }
public string City { get; set; }
public ICollection<ProjectState> ProjectStates { get; set; }
public Guid ArchitectId { get; set; }
public Architect Architect { get; set; }
}
public class Company : IEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid ProjectId { get; set; }
public Project Project { get; set; }
}
public class ProjectState : IEntity
{
public Guid Id { get; set; }
public ProjectPhase phase { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public Guid ProjectId { get; set; }
public Project Project { get; set; }
}
I have created also DemoContext that inherits DbContext. I defined relashionships in OnModelCreating(ModelBuilder modelBuilder) method.
public class DemoContext : DbContext
{
public DbSet<Architect> Architects { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<ProjectState> ProjectStates { get; set; }
public DemoContext(DbContextOptions<DemoContext> options) : base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql("PORT = 5432; HOST = localhost; TIMEOUT = 15; POOLING = True; MINPOOLSIZE = 1; MAXPOOLSIZE = 20; COMMANDTIMEOUT = 20; DATABASE = ProgressTest; PASSWORD = 'postgres'; USER ID = postgres", b => b.MigrationsAssembly("WebAPI.API"));
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasPostgresExtension("uuid-ossp");
modelBuilder.Entity<Project>()
.HasOne(p => p.Architect)
.WithMany(a => a.Projects)
.HasForeignKey(p=>p.ArchitectId);
modelBuilder.Entity<ProjectState>()
.HasOne(p => p.Project)
.WithMany(p => p.ProjectStates)
.HasForeignKey(p => p.ProjectId);
modelBuilder.Entity<Company>()
.HasOne(c => c.Project)
.WithMany(p => p.Companies)
.HasForeignKey(c => c.ProjectId);
}
}
I want to migrate all classes and create new database. I've used
dotnet ef migrations add InitializeMigration to add new migration and after it I wanted to update my database, so I've used dotnet ef database update but Postgres throw an Exception:
Npgsql.PostgresException: 42P01: relationship "ProjectState.IX_ProjectState_ProjectId" doesn't exist
Can you help me with it?
In my case i just decorate my model class with "Table" attribute with actual table name (mind case sensativity) and it resolved my problem
[Table("city")]
public class CityModel
Related
How do I define the relationships here with EF Core?
I have an Employee table which has multiple Jobs
public class Employee
{
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<HourlyRate> DefaultRate { get; set; }
public string Note { get; set; }
public bool Active { get; set; }
public DateTime DateHired { get; set; }
public List<PhoneNumber> PhoneNumbers { get; set; }
public List<Address> Addresses { get; set; }
public List<Job> Jobs { get; set; }
public bool Deleted { get; set; }
}
And the Job class has an Employee object to navigate back to the employee and the Job has multiple Directors which are also Employees
public class Job
{
public int JobId { get; set; }
public Employee Employee { get; set; }
public JobType Type { get; set; }
public Department Department { get; set; }
public List<Employee> Directors { get; set; }
public bool Active { get; set; }
public decimal HourlyRate { get; set; }
public string Note { get; set; }
public bool Deduction { get; set; }
public int? DeductionPercent { get; set; }
}
This is my DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasMany(employee => employee.Jobs)
.WithOne(i => i.Employee);
}
Initially the Job only had a single Director and everything was good but the requirement has changed to have multiple directors and ef removed the Director column from the Job table and added a JobId column to the Employee table but the problem is that if i add that director to a second job by job.Directors.Add(director) EF overrides the job id of the of the director and the director is being removed from the previous job
I am using EF Core 2.2
if a Job has only 1 Employee but multiple Directors (also Employee)
add public int EmployeeId {get; set;} to your Job class and add this
modelBuilder
.Entity<Job>()
.HasMany(p => p.Directors)
.WithMany(p => p.Jobs));
also, change List<> to ICollection<>
You should tell EF through fluent API that there's a 1-to-many relationship from Employee to Job. Otherwise, EF may get confused.
The many-to-many relationship needs a junction table and matching entity in the model which you'll also need to configure through fluent API. You'll define two 1-to-many relationships from Employee and Job to that new entity. EF core does not directly support such relationships before 5.0.
If you are targeting SQL, then you need to mark at least one of the relationships as OnDelete(CascadeBehavior.NoAction). Otherwise, your model will generate invalid table defintions which will raise errors at creation time.
Update:
The junction table would be defined something like this.
public class Employee
{
// ... other stuff
public List<EmployeeJob> EmployeeJobs { get; set; }
}
public class Job
{
// ... other stuff
public List<EmployeeJob> EmployeeJobs { get; set; }
}
public class EmployeeJob
{
public int EmployeeId { get; set; }
public int JobId { get; set; }
public Employee Employee { get; set; }
public Job Job { get; set; }
}
// goes in DbContext
modelBuilder.Entity<EmployeeJob>.HasKey(x => new { x.EmployeeId, x.JobId });
Try to use this code. Since your employee can have one or many jobs I added the table EmployeeJob and many-to-many relations. I think you just need to add IsDirector flag to Employee or maybe better something like an EmployeeType:
public class Employee
{
public Employee()
{
EmployeeJobs = new HashSet<EmployeeJob>();
}
[Key]
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Note { get; set; }
public bool Active { get; set; }
public DateTime DateHired { get; set; }
public bool Deleted { get; set; }
[InverseProperty(nameof(EmployeeJob.Employee))]
public virtual ICollection<EmployeeJob> EmployeeJobs { get; set; }
}
public class Job
{
public Job()
{
EmployeeJobs = new HashSet<EmployeeJob>();
}
[Required]
public int JobId { get; set; }
public bool Active { get; set; }
public decimal HourlyRate { get; set; }
public string Note { get; set; }
public bool Deduction { get; set; }
public int? DeductionPercent { get; set; }
[InverseProperty(nameof(EmployeeJob.Job))]
public virtual ICollection<EmployeeJob> EmployeeJobs { get; set; }
}
public class EmployeeJob
{
[Key]
public int Id { get; set; }
public int EmployeeId { get; set; }
[ForeignKey(nameof(EmployeeId))]
[InverseProperty(nameof(EmployeeJob.Employee.EmployeeJobs))]
public virtual Employee Employee { get; set; }
public int JobId { get; set; }
[ForeignKey(nameof(JobId))]
[InverseProperty(nameof(EmployeeJob.Employee.EmployeeJobs))]
public virtual Job Job { get; set; }
}
public class EmployeeDbContext : DbContext
{
public EmployeeDbContext()
{
}
public EmployeeDbContext(DbContextOptions<EmployeeDbContext> options)
: base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<EmployeeJob> EmployeeJobs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=localhost;Database=Employee;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<EmployeeJob>(entity =>
{
entity.HasOne(d => d.Employee)
.WithMany(p => p.EmployeeJobs)
.HasForeignKey(d => d.EmployeeId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_EmployeeJob_Employee");
entity.HasOne(d => d.Job)
.WithMany(p => p.EmployeeJobs)
.HasForeignKey(d => d.JobId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_EmployeeJob_Job");
});
}
I am trying to add a migration using the code-first workflow, but it seems that the EF core does not recognize my many-to-many relationship between "TeamMember" and "Step" classes.
My "step" class:
public class Step {
public int Id { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
[Required]
[StringLength(30, MinimumLength = 3, ErrorMessage = "The step name must have between 3 and 30 characters!")]
public string Name { get; set; }
[Required]
[StringLength(500, MinimumLength = 10, ErrorMessage = "The step description must have between 10 and 500 characters!")]
public string Description { get; set; }
public ICollection<TeamMember> TeamMembers{ get; set; }
public Step() {
}
}
My "TeamMember" class:
public class TeamMember {
public int Id { get; set; }
public int UserId{ get; set; }
public User User{ get; set; }
public string Role { get; set; }
public int TeamId { get; set; }
public Team Team{ get; set; }
public ICollection<Step> Steps{ get; set; }
public TeamMember() {
}
}
My Context class:
public class ProjectContext : IdentityDbContext<User> {
public ProjectContext(DbContextOptions<ProjectContext> options)
: base(options) {
}
public DbSet<User> User { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<Team> Teams { get; set; }
public DbSet<Step> Steps { get; set; }
public DbSet<TeamMember> TeamMembers{ get; set; }
protected override void OnModelCreating(ModelBuilder builder) {
builder.Entity<User>().HasKey(m => m.Id);
builder.Entity<Project>().HasKey(m => m.Id);
builder.Entity<Team>().HasKey(m => m.Id);
builder.Entity<Step>().HasKey(m => m.Id);
builder.Entity<TeamMember>().HasKey(m => m.Id);
base.OnModelCreating(builder);
}
}
Error string: Unable to determine the relationship represented by
navigation property 'Step.TeamMembers' of type
'ICollection'. Either manually configure the relationship,
or ignore this property using the '[NotMapped]' attribute or by using
'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
As you can see I have an ICollection in each one of the classes and I can't figure out the error.
EF Core doesn't support many to many relationships by default.
You have to create a join table in order to achieve the relationship:
You can easily configure it like this:
Step:
public class Step {
public ICollection<TeamMemberStep> TeamMemberStep { get; set; }
}
Team Member:
public class TeamMember {
public ICollection<TeamMemberStep> TeamMemberStep { get; set; }
}
Join table:
public class TeamMemberStep
{
public string TeamMemberId { get; set; }
public virtual TeamMember TeamMember { get; set; }
public int StepId { get; set; }
public virtual Step Step { get; set; }
}
DbContext:
modelBuilder.Entity<TeamMemberStep>()
.HasKey(aup => new { aup.TeamMemberId, aup.StepId });
public DbSet<TeamMemberStep> TeamMemberSteps { get; set; }
Now you query the TeamMemberStep table to get your data from either sides.
I'm trying to learn how to properly utilize DbContext in EF Core.
I have a Team class:
public class Team
{
public int ID { get; set; }
public string Name { get; set; }
public bool CanSelfManage { get; set; } = false;
public virtual List<Mileage> Mileages { get; set; }
public IdentityUser Member { get; set; }
public string State { get; set; }
public List<Horse> Horses { get; set; }
}
And a Mileage class:
public class Mileage
{
public int ID { get; set; }
public virtual Team Team { get; set; }
public virtual int TeamID { get; set; }
public DateTime Date { get; set; }
public LogType Type { get; set; }
public decimal Miles { get; set; }
public IdentityUser User { get; set; }
public List<Horse> Horses { get; set; }
}
And my DbContext class contains
public DbSet<Team> Teams { get; set; }
public DbSet<Mileage> Mileages { get; set; }
public DbSet<Horse> Horses { get; set; }
public DbSet<SecurityEntry> SecurityEntries { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Mileage>()
.HasOne(t => t.Team)
.WithMany(b => b.Mileages)
.HasForeignKey(t => t.TeamID)
.IsRequired();
modelBuilder.Entity<Horse>()
.HasOne(t => t.Team)
.WithMany(h => h.Horses);
modelBuilder.Entity<Horse>()
.HasMany(m => m.Mileages);
modelBuilder.Entity<Mileage>()
.HasMany(h => h.Horses);
}
The problem that I'm having is that, no matter what I do, Team.Mileages returns null and is never populated.
If I set the List to not be mapped, inject the DbContext and try to run anything off of the context, it throws the following error:
A second operation started on this context before a previous operation
completed
Is there something glaring that I'm missing? I am using MySQL, if that makes any difference.
Entity framework by default uses Lazy loading, you either set to eager loading and load your references always or you ask for your collections on a database request.
Example:
_dbcontext.Team.Include(team => team.Mileages).ToList();
I am trying to accomplish many-to-many relationship using code-first EF with ASP.NET Identity table at one side. The join table is not generated in the DB, though. What am I missing? Here are my model classes:
AppUser.cs:
public class AppUser : IdentityUser
{
public AppUser()
{
Notes = new HashSet<Note>();
}
public DateTime? LastSuccessfulLoginDate { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
and
Note.cs:
public class Note
{
public Note() {
NoteAssignedToUsers = new HashSet<AppUser>();
}
[Key]
public int NoteID { get; set; }
[Required]
public int FileID { get; set; }
[Required]
public string Content { get; set; }
[Required]
public Importance? Importance { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy HH.mm}")]
public DateTime AddedOn { get; set; }
[Required]
public string CreatorID { get; set; }
[ForeignKey("FileID")]
public virtual OAFile OAFile { get; set; }
[ForeignKey("CreatorID")]
public virtual AppUser CreatedBy { get; set; }
public virtual ICollection<AppUser> NoteAssignedToUsers { get; set; }
}
In your dbcontext you can configure:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Note>()
.HasMany<AppUser>(s => s.NoteAssignedToUsers )
.WithMany(c => c.Notes)
.Map(cs =>
{
cs.MapLeftKey("AppUserId");
cs.MapRightKey("NoteId");
cs.ToTable("AppUsersNotes");
});
}
I have some classes:
public class Values : Entity
{
[Key]
public int Values_ID { get; set; }
[Required]
public string Values_Name { get; set; }
[Required]
public int ValuesNumeric { get; set; }
public virtual ICollection<ValuesMetrics> ValuesMetrics { get; set; }
}
public class GQMetric : Entity
{
[Key]
public int GQMetric_ID { get; set; }
[Required]
public string GQMetricName { get; set; }
[Required]
public int Importance_ID { get; set; }
public virtual List<GQMetricsQuestions> GQMetricsQuestions { get; set; }
public virtual ICollection<ValuesMetrics> ValuesMetrics { get; set; }
public virtual ImportanceScale ImportanceScale { get; set; }
}
I need to create many-to-many relationship to my own created class ValuesMetrics, not to automatically generated table by entity framework. I have tried a lot of solutions here, here and here but none of it did not work. Eventually, I did this:
public class ValuesMetrics : Entity
{
public int GQMetric_ID { get; set; }
public int Value_ID { get; set; }
public virtual GQMetric GQMetric { get; set; }
public virtual Values Values { get; set; }
}
FluentAPI:
modelBuilder.Entity<ValuesMetrics>()
.HasKey(c => new { c.GQMetric_ID, c.Value_ID });
modelBuilder.Entity<GQMetricsQuestions>()
.HasKey(c => new { c.GQMetric_ID, c.Question_ID });
but created table (ValuesMetrics) have an excessive relationship (GQMetrics_GQMetric_ID). I need only two primary keys from Values and GQMetrics tables
Can you advice me how to solve this problem? Thanks for any help!
Applied #Esteban 's solution from the link already referenced by you: Create code first, many to many, with additional fields in association table
Basically I did the following three changes:
Used POCO entities instead of inheriting from Entity class
Removed EF attributes, since we'll be using fluent API anyway
Changed fluent API configuration
Resulting code:
public class MyContext : DbContext
{
public DbSet<Values> Values { get; set; }
public DbSet<GQMetric> GqMetric { get; set; }
public DbSet<ValuesMetrics> ValuesMetrics { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Values>().HasKey(values => values.Values_ID);
modelBuilder.Entity<GQMetric>().HasKey(metric => metric.GQMetric_ID);
modelBuilder
.Entity<ValuesMetrics>()
.HasKey(valuesMetrics => new
{
valuesMetrics.Value_ID,
valuesMetrics.GQMetric_ID
});
modelBuilder
.Entity<ValuesMetrics>()
.HasRequired(valuesMetrics => valuesMetrics.Values)
.WithMany(valueMetrics => valueMetrics.ValuesMetrics)
.HasForeignKey(valueMetrics => valueMetrics.Value_ID);
modelBuilder
.Entity<ValuesMetrics>()
.HasRequired(valuesMetrics => valuesMetrics.GQMetric)
.WithMany(valueMetrics => valueMetrics.ValuesMetrics)
.HasForeignKey(valueMetrics => valueMetrics.GQMetric_ID);
base.OnModelCreating(modelBuilder);
}
}
public class Values
{
public int Values_ID { get; set; }
public string Values_Name { get; set; }
public int ValuesNumeric { get; set; }
public virtual ICollection<ValuesMetrics> ValuesMetrics { get; set; }
}
public class GQMetric
{
public int GQMetric_ID { get; set; }
public string GQMetricName { get; set; }
public virtual ICollection<ValuesMetrics> ValuesMetrics { get; set; }
}
public class ValuesMetrics
{
public int GQMetric_ID { get; set; }
public int Value_ID { get; set; }
public virtual GQMetric GQMetric { get; set; }
public virtual Values Values { get; set; }
}