Failed executing DbCommand ON DELETE NO ACTION - asp.net-core

I am working on an ASP.NET Core project using code first I want to alter database design.
I have a case were I need to add two foreign keys to the same table, even I used DeleteBehavior.Restrict, still the error occurred when I update database.
Models:
public class Languages
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Projects> PFromLanguages { get; set; }
public virtual ICollection<Projects> PToLanguages { get; set; }
}
public class Projects
{
public int Id { get; set; }
public int FromLanguageId { get; set; }
public int ToLanguageId { get; set; }
public virtual Languages FromLanguage { get; set; }
public virtual Languages ToLanguage { get; set; }
}
Db Context:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Projects>()
.HasOne(p => p.FromLanguage)
.WithMany(b => b.PFromLanguages)
.HasForeignKey(b => b.FromLanguageId)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<Projects>()
.HasOne(p => p.ToLanguage)
.WithMany(b => b.PToLanguages)
.HasForeignKey(b => b.ToLanguageId)
.OnDelete(DeleteBehavior.Restrict);
}
Migration:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddForeignKey(
name: "FK_Projects_Languages_FromLanguageId",
table: "Projects",
column: "FromLanguageId",
principalTable: "Languages",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_Projects_Languages_ToLanguageId",
table: "Projects",
column: "ToLanguageId",
principalTable: "Languages",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
Error:
Failed executing DbCommand (17ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
ALTER TABLE [Projects] ADD CONSTRAINT [FK_Projects_Languages_FromLanguageId] FOREIGN KEY ([FromLanguageId]) REFERENCES [Languages] ([Id]) ON DELETE NO ACTION;

I am working on an ASP.NET Core project using code first I want to
alter database design. I have a case were I need to add two foreign
keys to the same table, even I used DeleteBehavior.Restrict, still the
error occurred when I update database
This is officially known issue. Because Primary keys are mapped by default convention please have a look here. You can't have single collection referenced by two FKs. Projects is mapped without cascading delete because it doesn't work in these self referencing many-to-many. Therefore, We have two ways to handle this scenario thus the error:
1. Change one or more of the relationships to not cascade delete.
In this scenario, we could make the Projects relationship with Languages optional by giving it a nullable foreign key property: for instance we can do something like:
.WillCascadeOnDelete(false);// Or you can try
.IsRequired(false)
Note: Either of one .WillCascadeOnDelete(false); or .IsRequired(false) should resolve your issue. You can check our official document for more details.
2. Configure the database without one or more of these cascade deletes,
then ensure all dependent entities are loaded so that EF Core can
perform the cascading behavior.
Considering this appreach we can keep the Projects, and Languages relationship required and configured for cascade delete, but make this configuration only apply to tracked entities, not the database: So we can do somethng like below:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Projects>()
.HasOne(p => p.FromLanguage)
.WithMany(b => b.PFromLanguages)
.HasForeignKey(b => b.FromLanguageId)
.OnDelete(DeleteBehavior.Restrict);
.WillCascadeOnDelete(false);// Or .IsRequired(false)
builder.Entity<Projects>()
.HasOne(p => p.ToLanguage)
.WithMany(b => b.PToLanguages)
.HasForeignKey(b => b.ToLanguageId)
.OnDelete(DeleteBehavior.Restrict);
.WillCascadeOnDelete(false); // Or .IsRequired(false)
}
Note: As you may know OnDelete(DeleteBehavior.ClientCascade); or ClientCascade allows the DBContext to delete entities even if there is a cyclic ref or LOCK on it. Please read the official guideline for more details here . In addition, as you haven't share your detele operation code while, executing your project make sure you are also removing language from context like here.

The error occurred because I am trying to modify the relations between tables while there are some records already inserted into the targeted tables which violating new foreign key.
The solution was simply to clear existing records in those tables and then apply the migration..
Thank you all for your kind support.

Related

RawSQL and auto-generated keys in EF Core 3.1

I have a Model with a Guid primary key. I want the Database to generate a key on insert so I added the following annotations:
public class Employee
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid employee_id { get; set; }
[Required]
public int employee_number { get; set; }
//more props...
}
Now I expected that inserts with RawSQL wouldn't expect a primary key, however the folllowing statement doesn't work when executred through ExecuteSqlRaw:
INSERT INTO employees (employee_number/**, more props*/)
VALUES (123 /**,more props*/)
An error is caused by the DB about a non-nullable primary key. Explicitly inserting some random Guid works, but i figured the DB would take care of this.
Is the Identity annotation wrong?
You could miss one step.
When I add migration, ef core will generate a migration file. Then defaultValueSql: "newsequentialid()" need to be added here.
After excuting Update-Database, I can insert the record with RawSQL.
Edit:
Another method to use HasDefaultValueSql in DbContext.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.Property(b => b.employee_id)
.HasDefaultValueSql("newsequentialid()");
}

How to have a primary key with a different name than "Id"

I have the following situation in my ASP.NET Core application with Entity Framework Core 1.1
Database-Table named "Content"
Content_ID (int not null, primary key)
Title (varchar max)
Description (varchar max)
Model ("ContentEntry.cs"):
public class ContentEntry
{
public int Id {get; set;}
public string Title {get; set;}
public string Description {get; set;}
}
Configuration File (ContentEntryConfiguration.cs)
public class ContentEntryConfiguration : IEntityMappingConfiguration<ContentEntry>
{
public void Map(EntityTypeBuilder<ContentEntry> modelBuilder)
{
modelBuilder.HasKey(m => m.Id);
modelBuilder.Property(m => m.Id).HasColumnName("Content_ID");
modelBuilder.Property(m => m.Title ).HasColumnName("Title");
modelBuilder.Property(m => m.Description).HasColumnName("Description");
modelBuilder.ToTable("Content");
}
}
As mentioned above, the primary key of my table is named "Content_ID".
When I execute a LINQ query, I receive an error saying that the column "ID" hasn't been found on the database. After inspecting the generated query with the SQL Profiler, I noticed that the query contains "ID" instead of "Content_ID".
I expect entity framework to generate a query containing the column "Column_ID" instead and map it to my model-property named "Id".
Do you have an idea why this is happening and how I could fix this issue?
For people with simpler needs, this will suffice
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ContentEntry>().Property(c => c.Id).HasColumnName("Content_ID");
}
I solved it, I just forgot to register the entity mapping in my DB Context Class:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.RegisterEntityMapping<ContentEntry, ContentEntryConfiguration>();
}

Orchard CMS ISessionConfigurationEvents and 1:N, N:N Relationships?

I have spent many days trying to implement relationships within OrchardCMS 1.9.1 between my custom contentParts to no avail.
Strewn across the internet are many others trying to achieve the same thing, who have also failed; giving me the impression that it's impossible?
Though recently I read an article at: http://www.ideliverable.com/blog/isessionconfigurationevents that gave the impression that all things possible with Fluent Nhibernate should be possible within Orchard.
So I implemented:
public class DbMapping : ISessionConfigurationEvents
{
public void Created(FluentConfiguration cfg, AutoPersistenceModel defaultModel)
{
defaultModel.UseOverridesFromAssemblyOf<ProfilePartRecord>().Alterations(x => x.AddFromAssemblyOf<ProfileOverride>());
defaultModel.UseOverridesFromAssemblyOf<LocationPartRecord>().Alterations(x => x.AddFromAssemblyOf<LocationOverride>());
}
public void Prepared(FluentConfiguration cfg) { }
public void Building(Configuration cfg) { }
public void Finished(Configuration cfg) { }
public void ComputingHash(Hash hash) { }
}
public class LocationOverride : IAutoMappingOverride<LocationPartRecord>
{
public void Override(AutoMapping<LocationPartRecord> mapping)
{
//[ Profile ] <--> [ Location ]
//mapping.Id(x => x.Id, "LocationPartRecord_id"); //As it's not in the model due to being a contentPart, NH will throw an error because of such.
mapping.Map(x => x.Type);
mapping.Map(x => x.Name);
mapping.References(x => x.ProfilePartRecord, "ProfilePartRecord_id");
}
}
public class ProfileOverride : IAutoMappingOverride<ProfilePartRecord>
{
public void Override(AutoMapping<ProfilePartRecord> mapping)
{
//[ Profile ] 0.1 <---> N [ Location ]
//NEW
mapping.HasMany(x => x.Locations)
.Inverse()
//.KeyColumn("ProfilePartRecord_id")
.Cascade.All()
.ForeignKeyCascadeOnDelete()
.ForeignKeyConstraintName("FK_Location__Profile");
}
}
MODELS:
public class ProfilePartRecord : ContentPartRecord
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
[CascadeAllDeleteOrphan]
public virtual IList<LocationPartRecord> Locations { get; set; }
public ProfilePartRecord()
{
Locations = new List<LocationPartRecord>();
}
}
public class LocationPartRecord : ContentPartRecord
{
public virtual string Type { get; set; }
public virtual string Name { get; set; }
//For HasMany
[CascadeAllDeleteOrphan]
public virtual ProfilePartRecord ProfilePartRecord { get; set; }
}
MIGRATION:
SchemaBuilder.CreateTable("ProfilePartRecord",
table => table
.ContentPartRecord()
//PK: ProfilePartRecord_id
.Column<string>("FirstName")
.Column<string>("LastName")
//System
.Column<DateTime>("CreatedAt")
);
ContentDefinitionManager.AlterPartDefinition("ProfilePart",
builder => builder.Attachable());
ContentDefinitionManager.AlterTypeDefinition("Profile", t => t
.WithPart(typeof(ProfilePart).Name)
.WithPart("UserPart")
);
ContentDefinitionManager.AlterTypeDefinition("User", t => t
.WithPart("ProfilePart")
);
SchemaBuilder.CreateTable("LocationPartRecord",
table => table
.ContentPartRecord()
//PK: LocationPartRecord_id
//FK:
.Column<int>("ProfilePartRecord_id")
.Column<string>("Type")
.Column<string>("Name")
//System
.Column<DateTime>("CreatedAt")
);
ContentDefinitionManager.AlterPartDefinition("LocationPart",
builder => builder.Attachable());
ContentDefinitionManager.AlterTypeDefinition("Location", type => type
.WithPart("CommonPart")
.WithPart("LocationPart")
.Creatable()
.Listable());
But alas, I still can't create a relationship between these two entities. I can do such via Migration, but this is very limited - as in - I can't set the relationship to Cascade.
Can anyone shed some light on whether this is possible, and if so, how? Thanks
This may not get you the whole way, but I believe it will help. One thing I have done for performance reasons as well as to establish relationships at the database level between my parts is to use the "CreateForeignKey" and "CreateIndex" in the Migration. Here is an example that should work for you
// Add foreign key
SchemaBuilder.CreateForeignKey(
"FK_LocationProfile",
"LocationPartRecord", new[] { "ProfilePartRecord_id" },
"ProfilePartRecord", new[] { "Id" });
// Add index
SchemaBuilder.AlterTable("LocationPartRecord",
table => table
.CreateIndex("IDX_ProfilePartRecord_Id", "ProfilePartRecord_Id")
);
With these relationships defined, I wonder if that will in any way impact the NHibernate work you are doing.
As for how we have done the overall goal I believe you are trying to achieve, you can monitor the "ProfilePart" "Delete" event in the "LocationPart" handler and apply your own cascading delete logic there to ensure that there are no "LocationPart" left around.

Fluent NHibernate - IndexOutOfRange

I've read all the posts and know that IndexOutOfRange usually happens because a column is being referenced twice. But I don't see how that's happening based on my mappings. With SHOW_SQL true in the config, I see an Insert into the Events table and then an IndexOutOfRangeException that refers to the RadioButtonQuestions table. I can't see the SQL it's trying to use that generates the exception. I tried using AutoMapping and have now switched to full ClassMap for these two classes to try to narrow down the problem.
public class RadioButtonQuestion : Entity
{
[Required]
public virtual Event Event { get; protected internal set; }
[Required]
public virtual string GroupIntroText { get; set; }
}
public class Event : Entity
{
[Required]
public virtual string Title { get; set; }
[Required]
public virtual DateTime EventDate { get; set; }
public virtual IList<RadioButtonQuestions> RadioButtonQuestions { get; protected internal set; }
}
public class RadioButtonQuestionMap : ClassMap<RadioButtonQuestion>
{
public RadioButtonQuestionMap()
{
Table("RadioButtonQuestions");
Id(x => x.Id).Column("RadioButtonQuestionId").GeneratedBy.Identity();
Map(x => x.GroupIntroText);
References(x => x.Event).Not.Nullable();
}
}
public class EventMap : ClassMap<Event>
{
public EventMap()
{
Id(x => x.Id).Column("EventId").GeneratedBy.Identity();
Map(x => x.EventDate);
Map(x => x.Title);
HasMany(x => x.RadioButtonQuestions).AsList(x => x.Column("ListIndex")).KeyColumn("EventId").Not.Inverse().Cascade.AllDeleteOrphan().Not.KeyNullable();
}
}
The generated SQL looks correct:
create table Events (
EventId INT IDENTITY NOT NULL,
EventDate DATETIME not null,
Title NVARCHAR(255) not null,
primary key (EventId)
)
create table RadioButtonQuestions (
RadioButtonQuestionId INT IDENTITY NOT NULL,
GroupIntroText NVARCHAR(255) not null,
EventId INT not null,
ListIndex INT null,
primary key (RadioButtonQuestionId)
)
This is using NH 3.3.0.4000 and FNH 1.3.0.727. When I try to save a new Event (with a RadioButtonQuestion attached) I see
NHibernate: INSERT INTO Events (EventDate, Title) VALUES (#p0, #p1);#p0 = 5/21/2012 12:32:11 PM [Type: DateTime (0)], #p1 = 'My Test Event' [Type: String (0)]
NHibernate: select ##IDENTITY
Events.Tests.Events.Tasks.EventTasksTests.CanCreateEvent:
NHibernate.PropertyValueException : Error dehydrating property value for Events.Domain.RadioButtonQuestion._Events.Domain.Event.RadioButtonQuestionsIndexBackref
----> System.IndexOutOfRangeException : An SqlCeParameter with ParameterIndex '3' is not contained by this SqlCeParameterCollection.
So if a column really is being referenced twice, what's the problem with my FNH config that's causing that behavior? I'm trying for a bidirection relationship (One Event Has Many Radio Button Questions) with ordering (I'll maintain it since NH won't in a bidir relationship, from what I've read). FWIW I also tried this as a unidirectional relationship by removing the Event from RadioButtonQuestion and it still caused the same exception.
I am using mapping in code (NH 3.3.1) and I have noticed that adding Update(false) and Insert(false) cures the problem:
ManyToOne(x => x.DictionaryEntity, map =>
{
map.Column("Dictionary");
map.Update(false);
map.Insert(false);
map.Cascade(Cascade.None);
map.Fetch(FetchKind.Select);
map.NotFound(NotFoundMode.Exception);
map.Lazy(LazyRelation.Proxy);
});
You have a bidirectional association, so one side should be marked as Inverse() and that can only be the RadioButtonQuestions collection. If you want the collection to be the owner, you have to remove the reference to the event in your RadioButtonQuestion class.
Additionally, the EventId column in the table RadioButtonQuestions is not nullable, which can cause problems, if the collection mapping is not inverse. See the note in the documentation.
I just spent a morning rooting this error out. The IndexOutOfRangeException sent me down the wrong path initially, but I've found the cause.
My problem concerned a FluentNHibernate class map that uses several components; the issue was that two properties were inadvertedly and incorrectly mapped to one and the same column:
before:
// example is stripped for simplicity, note the column names
Component(mappedClass => mappedClass.MappedComponent1,
map =>
{
map.Map(c => c.SomeProperty, "samecolumn");
});
Component(mappedClass => mappedClass.MappedComponent2,
map =>
{
map.Map(c => c.OtherProperty, "samecolumn");
});
after:
Component(mappedClass => mappedClass.MappedComponent1,
map =>
{
map.Map(c => c.SomeProperty, "firstcolumn");
});
Component(mappedClass => mappedClass.MappedComponent2,
map =>
{
map.Map(c => c.OtherProperty, "secondcolumn");
});
How this results in an IndexOutOfRangeException isn't obvious to me; I'm guessing that there's an array of mapped (source) properties and an array of destination columns, and in this case the destination array is too short for the number of items in the source properties array, because some of the destination columns are identical.
I think but it's worth writing a pull request for FluentNHibernate to check for this and throw a more explicit exception.

Setting CustomSqlType on References

I have a situation where my primary key is a char(2) in SqlServer 2008, and I want to reference it in a one-to-many relationship, but the ManyToOneBuilder (which is returned by ClassMap<>.References()) doesn't have a CustomSqlType() method. Specifically:
public class State
{
// state FIPS code is 2 characters
public virtual string StateCode { get; set; }
public virtual ICollection<County> { get; set; }
}
public class County
{
// state-county FIPS code is 5 characters
public virtual string StateCountyCode { get; set; }
public virtual State State { get; set; }
}
public class StateMap : ClassMap<State>
{
public StateMap()
{
Id(e => e.StateCode).CustomSqlType("char(2)").GeneratedBy.Assigned();
}
}
public class CountyMap : ClassMap<County>
{
public CountyMap()
{
Id(e => e.StateCountyCode).CustomSqlType("char(5)").GeneratedBy.Assigned();
References(e => e.State, "StateCode")
// Here's what I want to do, but can't because the method is not
// implemented on the class ManyToOneBuilder:
.CustomSqlType("char(2)");
}
}
Is there any way to accomplish this without modifying the ManyToOneBuilder? Is there a way to automatically map the FK (i.e. County.StateCode) to the correct type? It's trivial to add CustomSqlType to ManyToOneBuilder, but is that the right thing to do?
Keep your mapping definition as is, add your "State" column definition with
.CustomSqlType("char(2)")
and set for this column Insert=false and update=false.
I've the same problem and in AutoMapping I use this code:
mapping.Map(x => x.IdUniArticolo)
.CustomSqlType("varchar(50)")
.Not.Insert().Not.Update();
mapping.References(x => x.Articolo)
.Column("IdUniArticolo").PropertyRef("IdUniArticolo");
Keep in mind that if NHibernate itself doesn't support it, then Fluent NHibernate can't, and I don't NHibernate supports the scenario you have. I had a similar problem in that I had a 2 column composite key on a table and on one of the fields, I wanted to use an enumerated type which had a custom IUserType to translate it to its appropriate code value in the DB. Couldn't do it, so I was stuck keeping the property of the string type rather than the enumerated type.