Can't define composite key in my EF Core database-first ASP.NET Core Web API - entity-framework-core

I have following model of a table:
public partial class FlightsOfTicket
{
public int TicketId { get; set; }
public int FlightId { get; set; }
public int PlaneId { get; set; }
public int FlightNumber { get; set; }
public bool? IsDirect { get; set; }
public virtual Flight Flight { get; set; }
public virtual Plane Plane { get; set; }
public virtual Ticket Ticket { get; set; }
}
And my DbContext has the following code:
modelBuilder.Entity<FlightsOfTicket>(entity =>
{
entity.HasKey(e => new { e.TicketId, e.FlightId, e.PlaneId });
entity.ToTable("FlightsOfTicket");
entity.Property(e => e.IsDirect)
.IsRequired()
.HasDefaultValueSql("((1))");
entity.HasOne(d => d.Flight)
.WithMany(p => p.FlightsOfTickets)
.HasForeignKey(d => d.FlightId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_FlightsOfTicket_Flight");
entity.HasOne(d => d.Plane)
.WithMany(p => p.FlightsOfTickets)
.HasForeignKey(d => d.PlaneId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_FlightsOfTicket_Plane");
entity.HasOne(d => d.Ticket)
.WithMany(p => p.FlightsOfTickets)
.HasForeignKey(d => d.TicketId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_FlightsOfTicket_Ticket");
});
I know that entity.HasKey(e => new { e.TicketId, e.FlightId, e.PlaneId }) creates a composite key, but at runtime I got this exception:
which means, as far as I know, that it's not creating the composite primary key.
I've tried to create key in different places, tried to re-scaffold model and nothing - it keeps not working for 6 hours now

Related

Entity Mapping in EF Core 6

I would like to convert the following EF 6 code into EF Core 6.
HasMany(company => company.AssociatedTo)
.WithMany(x => x.AssociatedFrom)
.Map(mc =>
{
mc.MapLeftKey("Company_1");
mc.MapRightKey("Company_2");
mc.ToTable("Companies_Companies");
});
I have tried this, but for some reason, it doesn't pick any values
builder.Entity<Compnay>
.HasMany(company => company.AssociatedTo)
.WithMany(x => x.AssociatedFrom)
.UsingEntity<Companies_Companies>(
j => j
.HasOne<Company>()
.WithMany()
.HasForeignKey(x => x.Company_1),
j => j
.HasOne<Company>()
.WithMany()
.HasForeignKey(x => x.Company_2)
);
These are my Entity Models:
public class Company
{
[Key]
public int Id { get; set; }
...
[NotMapped]
public IEnumerable<Company> AssociatedCompanies => (AssociatedFrom ?? new List<Company>())
.Union(AssociatedTo ?? new List<Company>()).Where(x => x.Id != Id).Distinct().ToArray();
public virtual ICollection<Company> AssociatedFrom { get; set; } = new List<Company>();
public virtual ICollection<Company> AssociatedTo { get; set; } = new List<Company>();
...
}
And the Companies_Companies Model, I added to support Many-to-Many relationship
public class Companies_Companies
{
public int Company_1 { get; set; }
public int Company_2 { get; set; }
}
You can establish many to many relationships like this, my score is not enough to comment, but if you share your entityModel, I can help more.
builder.Entity<Compnay>()
.HasMany(x => x.AssociatedFrom).WithMany(x => x.AssociatedTo)
.UsingEntity<Dictionary<string, object>>("TableNameHere",
x => x.HasOne<Compnay>().WithMany().HasForeignKey("CompnayId").HasConstraintName("CompnayFK"),
x => x.HasOne<AssociatedFromModels>().WithMany().HasForeignKey("AssociatedFromId").HasConstraintName("AssociatedFromFK"));

How to create relationship with temporal table in EF Core

I want to create a 1-0..1 relation between a table and a temporal table using EF Core code-first approach. But when I add the migration I get the error :
Navigation 'Ticket (Dictionary<string, object>).TicketAdministration' was not found. Please add the navigation to the entity type before configuring it.
I let you see step by step what I am doing:
I create the Ticket and TicketAdministration classes:
public partial class Ticket
{
public long Id { get; set; }
//... other unuseful props
public virtual TicketAdministration TicketAdministration { get; set; }
}
public class TicketAdministration
{
public long Id { get; set; }
//... other unuseful props
public long? TicketId { get; set; }
public virtual Ticket Ticket { get; set; }
}
Then I configured the two classes/tables:
modelBuilder.Entity<Ticket>(entity =>
{
entity.ToTable("tickets");
entity.Property(e => e.Id)
.HasColumnType("bigint")
.ValueGeneratedOnAdd();
});
modelBuilder.Entity<TicketAdministration>(entity =>
{
entity.ToTable("ticket_admininistration", "admin", t => t.IsTemporal());
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).UseIdentityColumn(1, 1);
entity.HasOne(d => d.Ticket)
.WithOne(p => p.TicketAdministration)
.HasForeignKey<TicketAdministration>(b => b.TicketId)
.OnDelete(DeleteBehavior.ClientCascade)
.HasConstraintName("FK_dbo.Tickets_admin.TicketAdministration_TicketId");
});
How do I have to configure the relationship? Why is it expecting a dictionary? What the dictionary represent?
Thank you

EF 6.1.3 Multiplicity conflicts

I have been getting error when I try to run add-migration script.
The error is:
Domain.DataAccessLayer.AllRoutines_Product: : Multiplicity conflicts with the referential constraint in Role 'AllRoutines_Product_Target' in relationship 'AllRoutines_Product'. Because all of the properties in the Dependent Role are non-nullable, multiplicity of the Principal Role must be '1'.
And I cannot figure what I am doing wrong. I have AllRoutines and Product entities. AllRoutines can have 0 or 1 Products. Here is my AllRoutines class (some code has been omitted for clarity):
public class AllRoutines
{
[Key]
public long Id { get; set; }
public string Name { get; set; }
public string Tags { get; set; }
public int? RoutineLevelId { get; set; }
public RoutineLevel RoutineLevel { get; set; }
public Guid? ProductId { get; set; }
public virtual Product Product { get; set; }
}
Here is FluetnApi mapping (again some code is omitted):
public void MapRoutine(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RoutineLevel>().HasKey(r => r.RoutineLevelId);
modelBuilder.Entity<AllRoutines>()
.HasKey(r => r.Id)
.Property(r => r.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<AllRoutines>()
.HasOptional(r => r.RoutineLevel)
.WithMany()
.HasForeignKey(r => r.RoutineLevelId);
modelBuilder.Entity<AllRoutines>().HasOptional(c => c.Product)
.WithMany()
.HasForeignKey(c => c.ProductId)
.WillCascadeOnDelete(false);
}
Also I am not sure if this is important or not, but there is also class CustomRoutine which inherits AllRoutines and looks like this:
public class CustomRoutine : AllRoutines
{
public DateTime DateCreated { get; set; }
public string UserId { get; set; }
public User UserWhoCreatedRoutine { get; set; }
}
The inheritance approach was Table per Hierarchy.
I've tried to add to mapping configuration this:
modelBuilder.Entity<CustomRoutine>().HasOptional(c => c.Product)
.WithMany()
.HasForeignKey(c => c.ProductId)
.WillCascadeOnDelete(false);
But the error was same. I am not sure why this is happening, because, as you can see in the code same mapping was already done (without any problems) for RoutineLevel, also I have same mapping for Product and the other class, again with no problems.
EDIT
Here is also Product class:
public class Product
{
public Guid Id { get; protected set; }
public string Code { get; set; }
public string Name { get; set; }
public bool IsFree { get; set; }
public ICollection<SubscriptionProduct> SubscriptionProducts { get; set; }
}
And FluentAPI mapping:
modelBuilder.Entity<Product>()
.HasKey(p => p.Id);
modelBuilder.Entity<Product>()
.Property(p => p.Code)
.IsRequired()
.HasMaxLength(10);
modelBuilder.Entity<Product>()
.Property(p => p.Name)
.IsRequired()
.HasMaxLength(100);
modelBuilder.Entity<Product>()
.HasMany(p => p.SubscriptionProducts)
.WithRequired()
.HasForeignKey(p => p.ProductId)
.WillCascadeOnDelete(true);

Entity Framework mysterious error

Please help. I don`t understand why from my entity context
var stagesExist = context.WfwDocumentWorkStages
.Any(it => it.Enabled && it.ExecutionId == execution.Id
&& it.Level == execution.Level && it.ResultId == null);
value stagesExist is false
But
var stages = context.WfwDocumentWorkStages.Where(it => it.Enabled
&& it.ExecutionId == execution.Id
&& it.Level == execution.Level).ToList();
bool stagesExist = stages.Any(it=>it.ResultId == null);
value stagesExist is true??
Model:
public partial class WfwDocumentWorkScheme : EnabledEntity
{
public WfwDocumentWorkScheme()
{
this.WfwExecutionEvents = new List<WfwExecutionEvent>();
}
public int ExecutionId { get; set; }
public int Level { get; set; }
public int? RoleId { get; set; }
public string CoordinatorSid { get; set; }
public DateTimeOffset? Date { get; set; }
public int? ResultId { get; set; }
public string Comment { get; set; }
public virtual Employee Coordinator { get; set; }
public virtual EmployeeRole EmployeeRole { get; set; }
public virtual WfwEventResult WfwEventResult { get; set; }
public virtual WfwDocumentExecution WfwDocumentExecution { get; set; }
public virtual ICollection<WfwExecutionEvent> WfwExecutionEvents { get; set; }
}
Mapping
public class WfwDocumentWorkSchemeMap : EntityTypeConfiguration<WfwDocumentWorkScheme>
{
public WfwDocumentWorkSchemeMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.CoordinatorSid)
.HasMaxLength(46);
// Table & Column Mappings
this.ToTable("WfwDocumentWorkSchemes");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.ExecutionId).HasColumnName("ExecutionId");
this.Property(t => t.Level).HasColumnName("Level");
this.Property(t => t.RoleId).HasColumnName("RoleId");
this.Property(t => t.CoordinatorSid).HasColumnName("CoordinatorSid");
this.Property(t => t.Date).HasColumnName("Date");
this.Property(t => t.ResultId).HasColumnName("ResultId");
this.Property(t => t.Comment).HasColumnName("Comment");
this.Property(t => t.Enabled).HasColumnName("Enabled");
// Relationships
this.HasRequired(t => t.Coordinator)
.WithMany(t => t.WfwDocumentWorkSchemes)
.HasForeignKey(d => d.CoordinatorSid);
this.HasRequired(t => t.WfwDocumentExecution)
.WithMany(t => t.WfwDocumentWorkSchemes)
.HasForeignKey(d => d.ExecutionId);
this.HasRequired(t => t.WfwEventResult)
.WithMany(t => t.WfwDocumentWorkSchemes)
.HasForeignKey(d => d.ResultId);
this.HasOptional(t => t.EmployeeRole)
.WithMany(t => t.WfwDocumentWorkSchemes)
.HasForeignKey(d => d.RoleId);
}
}
Result model contains virtual List
public class WfwEventResult : EnabledEntity
{
public WfwEventResult()
{
this.WfwExecutionEvents = new List<WfwExecutionEvent>();
this.WfwDocumentWorkSchemes = new List<WfwDocumentWorkScheme>();
}
public string Name { get; set; }
public string Description { get; set; }
public bool Success { get; set; }
public virtual ICollection<WfwExecutionEvent> WfwExecutionEvents { get; set; }
public virtual ICollection<WfwDocumentWorkScheme> WfwDocumentWorkSchemes { get; set; }
}
The problem is this line in your mapping:
this.HasRequired(t => t.WfwEventResult)
You are effectively telling the EF that the associated FK column will never be null (although you've made it int? and have records with null value). Remember that EF uses metadata information when building SQL queries, and in this case I guess the query optimizer decides that this query will never return records (similar to .Where(it => false)) and generates a fake SQL query you see.
Shortly - make sure you always provide the correct information to EF. In this case, change the above to
this.HasOptional(t => t.WfwEventResult)
and you'll see a different (real) query and get a correct results.

Code First - Two foreign keys as primary keys, unable to add migration

My User table:
public class User
{
[Key]
public int UserId { get; set; }
public virtual ICollection<PollVote> PollVotes { get; set; }
}
My Poll table:
public class Poll
{
[Key]
public int PollId { get; set; }
public virtual ICollection<PollVote> PollVotes { get; set; }
}
My PollVote table
public class PollVote
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int VoteId { get; set; }
[Key]
public int PollId { get; set; }
[Key]
public int UserId { get; set; }
public DateTime TimeVoted { get; set; }
public int Answer { get; set; }
public virtual Poll Poll { get; set; }
public virtual User User { get; set; }
}
My Configurations:
//User config:
this.HasMany(x => x.PollVotes)
.WithRequired()
.HasForeignKey(x => x.UserId)
.WillCascadeOnDelete(false);
//Poll Config
this.HasMany(x => x.PollVotes)
.WithRequired()
.HasForeignKey(x => x.PollId)
.WillCascadeOnDelete(false);
//PollVote Config
this.HasKey(x => x.UserId)
.HasRequired(x => x.User)
.WithMany()
.HasForeignKey(x => x.UserId);
this.HasKey(x => x.PollId)
.HasRequired(x => x.Poll)
.WithMany()
.HasForeignKey(x => x.PollId);
The relation is: One Poll can have many votes, but a User can only give one vote to every poll.
I get this error when i try to Add-Migration in PM-Console
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'PollVote_Poll_Source' in relationship 'PollVote_Poll'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'Poll_PollVotes_Target' in relationship 'Poll_PollVotes'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
Any suggestions?
You specify a composite key by either adding the [Column] attribute to the data annotations...
[Key, Column(Order = 1)]
public int PollId { get; set; }
[Key, Column(Order = 2)]
public int UserId { get; set; }
...or by using an anonymous object with Fluent API:
this.HasKey(x => new { x.UserId, x.PollId });
this.HasRequired(x => x.User)
.WithMany(u => u.PollVotes)
.HasForeignKey(x => x.UserId);
this.HasRequired(x => x.Poll)
.WithMany(p => p.PollVotes)
.HasForeignKey(x => x.PollId);
Don't forget the lambda expressions for the inverse navigation properties in WithMany, as shown above, and remove the redundant configurations in UserConfig and PollConfig.