The collection argument 'foreignKeyPropertyNames' must contain at least one element - sql-server

I have two models with certain properties:
public class Issue {
public int Id { get; set; }
}
public class Action {
public int Id { get; set; }
}
This was the last working code. Then as I forgot to add Issue as a foreign key property to Action I did it and it became:
public class Action {
public int Id { get; set; }
public Issue Issue { get; set; }
public int IssueId { get; set; }
}
The solution built, migration up method created and ef update database failed with the following error:
Applying migration '20180220102738_Action.issue'. Failed executing
DbCommand (20ms) [Parameters=[], CommandType='Text',
CommandTimeout='30'] ALTER TABLE [Actions] ADD CONSTRAINT
[FK_Actions_Issues_IssueId] FOREIGN KEY ([IssueId]) REFERENCES
[Issues] ([Id]) ON DELETE CASCADE; System.Data.SqlClient.SqlException
(0x80131904): Introducing FOREIGN KEY constraint
'FK_Actions_Issues_IssueId' on table 'Actions' may cause cycles or
multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO
ACTION, or modify other FOREIGN KEY constraints.
Then I tried to change the onDelete: ReferentialAction.Cascade to onDelete: ReferentialAction.SetNull or anything else but nothing worked and btw I don't understand what could be its problem, this would be the third foreign key on this table.
My next step was to remove last migration but it started to complain about the following:
System.ArgumentException: The collection argument
'foreignKeyPropertyNames' must contain at least one element. at
Microsoft.EntityFrameworkCore.Utilities.Check.NotEmpty[T](IReadOnlyList1
value, String parameterName) at
Microsoft.EntityFrameworkCore.Metadata.Builders.ReferenceCollectionBuilder2.HasForeignKey(String[]
foreignKeyPropertyNames) at
ISSupport.Models.ISSupportContext.<>c.b__69_7(EntityTypeBuilder`1
b) in
I could roll back to the last commit but I don't see what did I do and what should I avoid in the next round. Google didn't help with this error.
.NET Core 2.0
EF Core 2.0
Sql Server 13.0.1742
Update
I had to do a rollback and added the foreign key again. It complained again about multiple cascade paths and now I changed it in the migration Up() method to onDelete: ReferentialAction.NoAction and run update successfully. I don't know what I had f*#!d up before and still need an explanation why do I have to set ReferentialAction.NoAction

This is what you had to do:
public class Issue
{
public int Id { get; set; }
}
public class Action
{
public int Id { get; set; }
[ForeignKey("IssueId")]
public Issue Issue { get; set; }
public int IssueId { get; set; }
}
You have to define what is going to be your foreign key property.

In migration Up() method I had to switch the Foreign key ReferentialAction to onDelete: ReferentialAction.NoAction

Related

Another efcore violation of primary key constraint reference table problem

Assume I've read and googled, and I still don't know what I'm doing incorrectly. Whenever I try to execute
_dbContext.Set<T>().Add(aMediaObjectWithAssociatedProvider);
_dbContext.SaveChanges();
I get the dreaded efcore violation of primary key constraint reference table
I have a class as such:
public class Media : BaseModel
{
public virtual string Title { get; set; }
public virtual string? Description { get; set; }
public virtual string Source { get; set; }
public virtual Guid? MediaTypeId { get; set; }
public virtual Guid? ProviderId { get; set; }
public virtual DateTime? StartDate { get; set; }
public virtual DateTime? EndDate { get; set; }
public virtual Provider? Provider { get; set; }
}
The BaseModel class is
public abstract class BaseModel : IBaseModel
{
public virtual Guid Id { get; set; }
}
The Provider class is as such:
public class Provider : BaseModel
{
public virtual string Name { get; set; }
public virtual string? ApiUsername { get; set; }
public virtual string? ConfigurationSection{ get; set; }
}
My DBContext has the following:
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<Media>().HasKey(x => x.Id);
mb.Entity<Media>().HasOne(p => p.Provider).WithOne().HasForeignKey<Media>(x => x.ProviderId);
}
The code for inserting a new object is as follows:
public T Insert(T oneObject)
{
try
{
// Ensure the entity has an ID
if (oneObject.Id == Guid.Empty)
{
oneObject.Id = Guid.NewGuid();
}
_dbContext.Set<T>().Add(oneObject);
_dbContext.SaveChanges();
}
catch (Exception error)
{
_logger.LogError(error.Message, error);
}
return oneObject;
}
Assume that providers are static, in a sense that they already exist in their table, and I don't want to add new providers when I save media... Media just needs to have a provider.
I know exactly what is happening (the model, after travelling through json back through the api to the server is losing context, but I'm also trying to build a repository type of system where I don't have to build complex save logic for every object. (hence why i'm hand wringing over adding code that loads existing providers).
This problem specifically began rearing its head when I was saving new Media objects into the database with existing Providers. I am still mulling over how to look up children dynamically, but i'm not quite there yet.
I've been at this for so long, i'm about ready to give up on efcore relations and just rebuild the models as single objects, and handle all of the manipulation in javascript. And I don't like this idea.
I know for a fact that there will be questions for more code, but please let me know what. Again, I'm just stepping into .net core / ef core so this code-first is a little confusing for me. Thanks
You may have 2 options to try. Do backup your whole project and database beforehand. Clone your database to another database name. Try these either one option using new cloned database for testing.
No.1
Set "newid()" without quotes in your ID's default value in sql server. So you don't need to use Guid.NewGuid() in code every insert. newid() will auto generate GUID.
No. 2
How about removing primary key from ID (GUID) and then creating new column "UID" (running number) and set UID (running number) as primary key and enable its identity? You need to change all other tables too. And re-link UID each other if you use relationship. This way, your UID will not have existing number when insert.

Entity Framework fails to update SQL Server database

I have a problem with a constraint in my database. A StairWay class uses a strongly typed property called Tread. I receive a message that the constraint does not exist when I try to update-database but the code below tells me it is created. I am using Entity Framework, SQL Server with ASP.NET Core 3.1.
Screenshot of the error message:
//Classes I have created
public class StairWay
{
public StairTread Tread { get; set; }
public int StairTreadId { get; set; }
public int StairWayId { get; set; }
}
public class StairTread
{
public int StairTreadId { get; set; }
public int StairWayId { get; set; }
}
// SQL code generated
ALTER TABLE [dbo].[StairTread]
ADD CONSTRAINT [FK_StairTread_StairCases_StairWayId]
FOREIGN KEY ([StairWayId])
REFERENCES [dbo].[StairWays] ([StairWayId]) ON DELETE CASCADE;

How to debug Entity Framework Code First database deployment error?

How do I debug an error with my Entity Framework Code First database deployment?
I'm using EF 6.1.3. I'm trying to deploy a database to Azure that includes the following model:
public class DriverLog
{
public int ID { get; set; }
[Required]
public DateTime LogDate { get; set; }
public int RouteID { get; set; }
public virtual Route Route { get; set; }
public int DriverID { get; set; }
[ForeignKey("DriverID")]
public virtual Employee Driver { get; set; }
public int DispatcherID { get; set; }
[ForeignKey("DispatcherID")]
public virtual Employee Dispatcher { get; set; }
}
The deployment succeeds, but when I try to access the database, I get the following error:
{"Message":"An error has occurred."}
If I remove the last two fields -- DispatcherID and Dispatcher -- I do not get this error.
For some reason, EF does not like my including these two fields, but I don't know why, and I don't know how to go about debugging this.
I was able to resolve the problem like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<DriverLog>()
.HasRequired(o => o.Dispatcher)
.WithMany()
.WillCascadeOnDelete(false);
}
Credit for this solution goes to: Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?
Do check primary key constraint for 'DispatcherID', basicaly the order its creating the table, as you are using 'DropCreateDatabaseIfModelChanges'.
Also inject some custom error catch, to have detail error information.

Entity framework, code first generation relationship between tables

I'm learning EF with MVC and following
video
They saying that line :
public ICollection<Chirp> Chirps { get; set; }
adds the relationship between the two models, but I don't see any difference so far when looking at both tables columns and keys after regenerating tables WITHOUT this line. I'm missing something as this kind of relationship is mentioned in all the tutorials.
public class User
{
public int Id { get; set; }
public String Name { get; set; }
public ICollection<Chirp> Chirps { get; set; }
}
public class Chirp
{
public int Id { get; set; }
public string Message { get; set; }
public User User { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Chirp> Chirps { get; set; }
public DbSet<User> Users { get; set; }
}
In other words, does public ICollection Chirps { get; set; } tell EF to do anything with db structure ?
Thank you!
This should be generating an additional table that tracks the User to Chirps relationship. Look in your database for a User_Chirps or UserChirps table that should have two columns, User_Id and Chirp_Id. Additionally, I just noticed that your id property is using a lowercase i for id. I believe that the conventions in Entity Framework are expecting a property name of Id with an uppercase I. So you may want to try changing that as well.
Seems that difference is in constraint of the Chirps table. Without public ICollection Chirps { get; set; }
its:
ALTER TABLE [dbo].[Chirps] WITH CHECK ADD CONSTRAINT **[Chirp_User]** FOREIGN KEY([User_id])
REFERENCES [dbo].[Users] ([id])
while with it its :
ALTER TABLE [dbo].[Chirps] WITH CHECK ADD CONSTRAINT **[User_Chirps]** FOREIGN KEY([User_id])
REFERENCES [dbo].[Users] ([id]

Why can't SQL Server handle cascading deletes for two foreign keys pointing to the same table?

I'm using Entity Framework - Code First, and I've run into an issue where I have to disable cascading deletes for a particular foreign key.
Here is my entity class:
public class ChallengeMatch
{
public int Id { get; set; }
public int ChallengerClubMemberId { get; set; }
public int ChallengeeClubMemberId { get; set; }
public bool ChallengerWon { get; set; }
public string Score { get; set; }
public virtual ClubMember ChallengerClubMember { get; set; }
public virtual ClubMember ChallengeeClubMember { get; set; }
}
If I allow Code First to generate the database for this table with all default settings (which includes cascading deletes), it will throw an exception when it tries to generate the database objects.
If I add the following code to my DbContext class, I no longer get an exception, but now cascading deletes will only somewhat work:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ChallengeMatch>()
.HasRequired(cm => cm.ChallengeeClubMember)
.WithMany()
.HasForeignKey(cm => cm.ChallengeeClubMemberId)
.WillCascadeOnDelete(false);
}
Because of this, I have to write code by hand. Specifically, if I'm about to delete ClubMember 13, I have to delete any ChallengeMatch whose ChallengeeClubMemberId is 13.
What I don't understand is why any of this should be necessary. Why can't SQL Server handle a cascading delete even if there are two foreign keys pointing to the same table? I can't think of any reason why this would fail.
It should be a simple three-step process (psuedo code):
Delete all challenge matches where ChallengerClubMemberId == 13
Delete all challenge matches where ChallengeeClubMemberId == 13
Delete the club member where Id = 13.
Why can't SQL Server do this or why does it choose not to?
The problem is that your configuration allows multiple cascade delete paths. This can happen if both your properties will point to the same ClubMember. SQL Server doesn't allow that. It is more about internal implementation of SQL server and more details can be found in this answer. I think that this check is simple and safe solution to avoid some race conditions during deletes cascading in parallel.

Resources