Error Adding Controller because of Model Relationships - c#

I get an error message when trying to add a controller (presumably) because of incorrectly defined relationships. I cannot find what my issue is.
I have two simple models: Scenario and Condition. A Scenario can consist of multiple Conditions, but a Condition will only be associated with a single Scenario. (My DbContext is MySQL, not SQL Server).
Error: There was an error running the selected code generator: 'The relationship from 'Condition' to Scenario.Conditions with foreign key properties {'ScenarioId: int'} cannot target the primary key {'Id': int} because it is not compatible. Configure a principal key or a set of compatible foreign key properties for this relationship.'
(I had no problem creating the ScenariosController, and returning data from the API. Where I expected the array of Conditions to be, there were only null values. Also, the error occurred while trying to add the ConditionsController.)
public class Scenario
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<Condition> Conditions { get; set; }
}
public class Condition
{
public int Id { get; set; }
public int ScenarioId { get; set; }
public string Type { get; set; }
public string Preference { get; set; }
[ForeignKey("ScenarioId")]
public Scenario Scenario { get; set; }
}
public class EvaluatorContext : DbContext
{
public EvaluatorContext(DbContextOptions<EvaluatorContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Condition>()
.HasOne(s => s.Scenario)
.WithMany()
.HasPrincipalKey(s => s.Id);
modelBuilder.Entity<Scenario>()
.HasMany(s => s.Conditions)
.WithOne()
.HasForeignKey(c => c.ScenarioId);
}
public DbSet<Scenario> Scenario { get; set; }
public DbSet<Condition> Condition { get; set; }
}
I want to be able to add a ConditionsController using the Code Generator without encountering an error message and to be able to return a Scenario object with the list of associated Conditions, and vice versa.

I believe the issue with not being able to add a controller with the code generator was due to some issue with how I was defining relationships with Fluent API in the OnModelCreatingMethod(). I got rid of those and the error went away. As it turns out, they are not mandatory in order to make a controller actually run.
I also had another issue... the .Includes() statement did not work to actually load related entities. The answer for that issue, at least in may case, is here:
ASP.NET Core API only returning first result of list

Related

C# EF core: Child row gets removed when updating the Parent

Hi guys I have an issue with EF core version 6.4.4.
I am developing an .NET core (3.1) app that is multi-threaded (not the issue but just giving context) using MySQL (8.x) as database.
The issue that I am currently having is that my child row is getting removed when I update the parent row which doesn't cause a crash but it results in data loss as I am collecting data every second to create a chart.
I didn't have this issue initially but when I started adding foreign key constraints, indexes and refactored the code a bit for optimization I suddenly got this issue.
What I tried
So at first I thought that EF core set the parent as null when updating another entity or maybe that some required fields where null which resulted in a delete but this was not the case so I tried to use Z.EntityFramework.Extensions.EFCore for the ability to perform single updates (meaning that I will only update the given entity and not the referenced enitities aswell) but this didn't fix it.
I also checked all my indexes and foreignkeys to allow duplicates to make sure that I didn't delete a row because it had a duplicate key. All my indexes allowed duplicate rows (by setting unique false).
Classes
public class Parent
{
public int Id { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public ParentSatus Status { get; set; } //this is the property that I want to update
public decimal valueX{ get; set; }
public decimal valueY{ get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public decimal SomeValue{ get; set; }
public decimal SomeValue2{ get; set; }
public string IdForExternalAPI { get; set; }
public DateTime CreatedAt { get; set; }
public ChildStatus Status { get; set; }
public ChildType Type { get; set; }
public virtual Parent Parent { get; private set; }
public virtual int ParentId { get; set; } //This is for foreignkey constraint stuff see below
}
ModelBuilder
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Child>().HasOne(e => e.Parent).WithOne().HasForeignKey<Child>(e => e.ParentId).OnDelete(DeleteBehavior.Cascade).IsRequired();
modelBuilder.Entity<Parent>().HasIndex(e => e.SomeIndex).IsUnique(false);
modelBuilder.Entity<Child>().HasIndex(e => e.ParentId).IsUnique(false);
}
So obviously the relationship is a none-to-one which might be the cause but I don't want a one-to-one as the parent needs to get referenced by some other classes and the Child is also a parent for some other classes.
Can someone help with this?
I will add the answer if I find it some day.
Also some clarification: I did not add all the extra stuff as they have no part in this issue but will add it if it is necessary.
Edit:
Code where I update the parent
foreach (var parent in parentRepository.ReadParents(new Func<Parent, bool>(e => e.Status == Status.ONGOING)).ToList())
{
bool isStillOngoing = //Calculate the current status based on other entities
if (!isStillOngoing)
{
//Do some calculations here
}
parent.Status = isStillOngoing ? Status.ONGOING : Status.FINISHED;
//TODO: Bug here that deletes the child
parentRepository.UpdateParent(parent);
}
Ok I fixed it by turning the relationship around. Looks like I was too tired yesterday it should have been a none-to-many.
Fixed by changing the modelbuilder.
The fix
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>().HasMany<Child>().WithOne(e=> e.ParentId).HasForeignKey(e => e.ParentId).OnDelete(DeleteBehavior.Cascade).IsRequired();
modelBuilder.Entity<Parent>().HasIndex(e => e.SomeIndex).IsUnique(false);
modelBuilder.Entity<Child>().HasIndex(e => e.ParentId).IsUnique(false);
}
Thank you all for the help.

Entity Framework Core 3.1.6 Item With Same Key Has Already Been Added

I have a MVC website in .NET Core 3.1 using Entity Framework 3.1.6. My issue is that one of my entities now throws and error:
System.ArgumentException: An item with the same key has already been added. Key: Assessor
It was working fine up until recently and I'm not sure what changed.
Model
public class FacilityQCResult
{
[Key]
public int ID { get; set; }
public Guid? QCID { get; set; }
public string QCSeverity { get; set; }
public string QCType { get; set; }
public string QCShortName { get; set; }
public string Resolution { get; set; }
public string Base { get; set; }
public string FacilityNumber { get; set; }
public string FacilityName { get; set; }
public DateTime? DataEntryComplete { get; set; }
public string TeamAssignment { get; set; }
public string Assessor { get; set; }
public string TripWeek { get; set; }
public string Details { get; set; }
public Guid? FacilityID { get; set; }
}
Context
public partial class SQLDBContext : DbContext
{
public SQLDBContext()
{
}
public SQLDBContext(DbContextOptions<SQLDBContext> options)
: base(options)
{
}
public virtual DbSet<FacilityQCResult> FacilityQCResults { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<FacilityQCResult>(entity =>
{
entity.ToTable("FacilityQCResult");
entity.HasKey(e => e.ID);
entity.Property(e => e.DataEntryComplete)
.HasColumnName("Data_Entry_Complete")
.HasColumnType("datetime");
entity.Property(e => e.TeamAssignment)
.HasColumnName("Team_Assignment")
.HasMaxLength(100)
.IsUnicode(false);
entity.Property(e => e.TripWeek)
.HasColumnName("Trip_Week")
.HasMaxLength(10)
.IsUnicode(false);
});
}
}
Table Structure
It throws an error when it gets to the following line:
var result = await context.FacilityQCResults.FromSqlRaw(" exec [dbo].[pr_ExecuteFacilityQCRules]").ToListAsync();
The stored procedure does some other stuff and spits out rows of data that models the table structure. Funny thing is I have another object that is set up similar that runs with no issues. This one seemed to work until recently, but I'm not sure exactly when this starting happening or why as I have not changed the object or table structure. Basically, EF Core thinks the Assessor column is a key despite me not specifying so. How is this happening and how do I tell it that it is not a key?
In EF Core 6.0 I encountered this error when executing add-migration. I renamed a class, which should cause a migration that renames the table.
It did not work when I renamed the class and all the properties at once, but it did work when I renamed all properties to their new names and then create the migration, and rename the class after creating the migration.
That isn't likely a SQL Server related exception (i.e. I get the impression that you're thinking the exception is being thrown due to a SQL duplicate key exception, akin to what you would see if you tried to create multiple rows with a duplicate PK and/or unique key/index key). Were that the case, you'd be seeing a different exception (likely a SqlException or related).
That is most likely a C# exception bubbling up from somewhere in EF or related, and if I had to take a wild guess, my first hunch would be that you are possibly getting the "Assessor" column returned multiple times from the result set returned by the pr_ExecuteFacilityQCRules stored procedure. That's just a bit of a guess however - a simple test would be to execute the procedure in a SQL client and see the columns returned. You'd typically see this type of exception in C# by trying to add multiple duplicate key names to a dictionary or similar (i.e. myDictionary.Add(key)).
If that is not the case, I think there'd need to be a bit more info added to help diagnose beyond making a guess.
I had the same issue while creating a migration and solved it by upgrading from 6.0.1 to 6.0.3.
I received the same error in EF Core 6.0.
Updating EF Core to 6.0.2 fixes the issue, and runs the same migration with no issues.

How to map two entities to one database table by using Entity Framework (code first to an existing database)?

I got stuck with creating mapping for two entities that have to be mapped to the same database table. The reason why I need it is to create lite entity and full entity that will be used in different cases. I have already had existing database and I have my own database context that is derived from DbContext class. All mappings were made by using EntityTypeConfiguration<T> classes.
The table.
[PROCESS]
ProcessId <int> PK
Name <nvarchar>
StartDate <datetime>
ProcessSequency <int>
Limit <int>
OwnerName <nvarchar>
Now this table is mapped to the single domain model, but I want to change it.
I want to split this entity to the next two.
public class ProcessEntity
{
public int ProcessId { get; set; }
public int Name { get; set; }
public virtual ProcessDetailsEntity ProcessDetails { get; set; }
}
public class ProcessDetailsEntity
{
public int ProcessId { get; set; }
public DateTime StartDate { get; set; }
public int ProcessSequency { get; set; }
public int Limit { get; set; }
public string OwnerName { get; set; }
public virtual ProcessEntity ProcessBase { get;set; }
}
I created two configuration classes for them.
public class ProcessEntityConfiguration : EntityTypeConfiguration<ProcessEntity>
{
public ProcessEntityConfiguration()
{
// ToTable("PROCESS");
Map(m => m.ToTable("PROCESS"))
HasKey(t => t.ProcessId);
HasRequired(s => s.ProcessDetails).WithRequiredPrincipal(t => t.ProcessBase);
}
}
public class ProcessDetailsEntityConfiguration : EntityTypeConfiguration<ProcessDetailsEntity>
{
public ProcessDetailsEntityConfiguration()
{
// ToTable("PROCESS");
HasKey(t => t.ProcessId);
Map(m => m.ToTable("PROCESS"));
}
}
Everything looks fine and should work, but I see the error:
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Invalid column name 'Discriminator'.\r\nInvalid column name 'Discriminator'.",
"ExceptionType": "System.Data.SqlClient.SqlException",
"StackTrace": " at System.Data.SqlClient.SqlConnection.OnError(SqlException exception,
Boolean breakConnection, Action`1 wrapCloseInAction)
I googled it and found out that the Discriminator is used by Entity Framework in order to apply TPH - Table per hierarchy behavior. I also found one useful article that could be a solution for me: Entity Framework - Advanced mapping scenarios. Unfortunately the solution that is provided under Mapping a single table to multiple entities header doesn't work for me and I don't know why. Could you help me with this issue?
I'm a little bit familiar with NHibernate, so I tried to do it in NHibernate way. The idea is to create two independent entities. It was my first approach. Here is the code of this attempt.
public abstract class ProcessBaseEntity
{
public int ProcessId { get; set; }
public int Name { get; set; }
}
public class ProcessEntity : ProcessBaseEntity
{
}
public class ProcessDetailsEntity : ProcessBaseEntity
{
public DateTime StartDate { get; set; }
public int ProcessSequency { get; set; }
public int Limit { get; set; }
public string OwnerName { get; set; }
}
public class ProcessEntityConfiguration : EntityTypeConfiguration<ProcessEntity>
{
public ProcessEntityConfiguration()
{
ToTable("PROCESS");
HasKey(t => t.ProcessId);
}
}
public class ProcessDetailsEntityConfiguration : EntityTypeConfiguration<ProcessDetailsEntity>
{
public ProcessDetailsEntityConfiguration()
{
ToTable("PROCESS");
HasKey(t => t.ProcessId);
}
}
Unfortunately this attempt wasn't successful. The following exception was thrown:
The entity types 'ProcessEntity' and 'ProcessDetailsEntity' cannot
share table 'PROCESS' because they are not in the same type hierarchy
or do not have a valid one to one foreign key relationship with
matching primary keys between them.
I started splitting my first entity into two small as I want to make my SQL query as lighter as possible. Sometimes I wonder if it essential or not? Maybe my request won't be lighter even I spitted my entity. Maybe it worth trying to put all my properties in a single entity and forget about it.

EF Core - navigational property in index

I have the following two classes
public class Tip
{
public string Home { get; set; }
public string Away { get; set; }
public string Prediction { get; set; }
public Tipster Tipster { get; set; }
... other properties
}
public class Tipster
{
public int Id { get; set; }
public string Username { get; set; }
public string Platform { get; set; }
}
Now, I want to make unique index in theTip table. According to the EF Core documentation, there is no Data Annotations syntax, so I am using the fluent one:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Tip>()
.HasIndex(entity => new { entity.Tipster, entity.Home, entity.Away, entity.Prediction })
.HasName("IX_UniqueTip")
.IsUnique();
}
Now, when I update the database I get the following error
C:..>dotnet ef database update System.InvalidOperationException:
Cannot call Property for the property 'Tipster' on entity type 'Tip'
because it is configured as a navigation property. Property can only
be used to configure scalar properties.
It seems that EF didn't liked the fact that I am using referential property in the index. How can I fix that ?
You can't use navigation property in index defining expression. Instead, you should use the corresponding FK property.
The problem in your case is that you don't have explicit FK property in your model Tip. By convention EF Core will create int? TipsterId shadow property. So theoretically you should be able to use EF.Property method to access it:
.HasIndex(e => new { TipsterId = EF.Property<int>(e, "TipsterId"), e.Home, e.Away, e.Prediction })
Unfortunately this doesn't work currently (EF Core 2.0.1). So you have to resort to HasIndex overload with params string[] propertyNames:
.HasIndex("TipsterId", nameof(Tip.Home), nameof(Tip.Away), nameof(Tip.Prediction))
You must define the property TipsterId explicitly cause the Navigation property define it as shadow, so you cannot use it on custom index or alternate key
public class Tip
{
public string Home { get; set; }
public string Away { get; set; }
public string Prediction { get; set; }
public int TipsterId { get; set; }
public Tipster Tipster { get; set; }
... other properties
}
Now you can
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Tip>()
.HasIndex(entity => new { entity.TipsterId, entity.Home, entity.Away, entity.Prediction })
.HasName("IX_UniqueTip")
.IsUnique();
}
They way you defined your entities EF will put the referential column into the tipster table, since it looks like a 1-n relationship. Meaning a tipster can place several tips, but each tip is only placed by a single tipster.
That means on the database level there is nothing to index. No column, no key - nothing.
To fix this you might ask yourself what you really want to achieve with an index in the first place. An index is supposed to make queries using the columns of the index faster and avoid a full table scan.

Entity Framework Core still picks up old column

I recently delete a column ConversationId from my tables. When I start to debug my service and try to save I am getting an error:
Invalid column name 'ConversationId'.
Code:
public class AstootContext : DbContext
{
public AstootContext(DbContextOptions<AstootContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
public DbSet<ServiceRequest> ServiceRequests { get; set; }
}
And my entity looks like this:
public class ServiceRequest
{
public int Id { get; set; }
public int SenderUserId { get; set; }
public int PriceTypeId { get; set; }
public decimal Price { get; set; }
public bool IsAccepted { get; set; }
public DateTime Created { get; set; }
public int MessageId { get; set; }
}
All references to ConversationId were removed from the code, I've rebuilt, yet I'm still getting this error and I don't understand why.
This is my SQL Server table as you can see there is no ConversationId:
Is there a secret cache that I need to delete or something I have to run to update this?
EF Core is code based ORM, with the most important here being the M - Mapper. It doesn't matter what the actual database structure is, the important is what EF *thinks** it is based on your code model (entity classes and their properties, combined with data annotations, fluent configuration and set of conventions).
So the problem should originate from code. Since you've removed the explicit property, it should be caused by shadow property. And as explained in the documentation link, shadow properties are usually introduced by convention from relationships:
Shadow properties can be created by convention when a relationship is discovered but no foreign key property is found in the dependent entity class. In this case, a shadow foreign key property will be introduced.
The documentation also explains the naming rules applied in different scenarios.
A shadow property called ConversationId can be introduced in a several ways, but according to the provided information, the most likely cause is to have an entity class called Conversation defining one-to-many relationship with ServiceRequest by having a collection type navigation property:
public class Conversation
{
public int Id { get; set; }
// ...
public ICollection<ServiceRequest> ServiceRequests { get; set; }
}
Which according to your comment was indeed the case.
For completeness, here are some other possible scenarios generating such property:
(1) No collection navigation property in Conversation, reference navigation property in ServiceRequest:
public class Conversation
{
public int Id { get; set; }
// ...
}
public class ServiceRequest
{
// ...
public Conversation Conversation { get; set; }
}
(2) No navigation properties in Conversation and ServiceRequest, fluent configuration:
modelBuilder.Entity<Conversation>()
.HasMany<ServiceRequest>();
or
modelBuilder.Entity<ServiceRequest>()
.HasOne<Conversation>();
or variations of the above.
(3) No relationship involved, shadow property created through fluent configuration:
modelBuilder.Entity<ServiceRequest>()
.Property<int>("ConversationId");

Categories

Resources