Disable cascade delete on EF Core 2 globally - entity-framework

I need to know about ways of disabling cascade delete in EF Core 2 globally. Any help is appricated.
In EF 6.x we used following code to disable cascade delete on both OneToMany and ManyToMany realtions:
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

Unfortunately EF Core currently (latest at this time v2.0) does not expose a good way to control the conventions globally.
The default EF Core 2.0 convention is to use DeleteBehavior.Cascade for required and DeleteBehavior.ClientSetNull for optional relationships. What I can suggest as workaround is a typical metadata model loop at the end of the OnModelCreating override. In this case, locating all the already discovered relationships and modifying them accordingly:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// ...
var cascadeFKs = modelBuilder.Model.GetEntityTypes()
.SelectMany(t => t.GetForeignKeys())
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);
foreach (var fk in cascadeFKs)
fk.DeleteBehavior = DeleteBehavior.Restrict;
base.OnModelCreating(modelBuilder);
}

constraints: table =>
{
table.PrimaryKey("PK_ComandaPlato", x => x.ComandaPlatoId);
table.ForeignKey(
name: "FK_ComandaPlato_Comanda_ComandaId",
column: x => x.ComandaId,
principalTable: "Comanda",
principalColumn: "ComandaId",
onDelete: ReferentialAction.Cascade);//default: cascade on
table.ForeignKey(
name: "FK_ComandaPlato_Plato_PlatoId",
column: x => x.PlatoId,
principalTable: "Plato",
principalColumn: "PlatoId",
onDelete: ReferentialAction.NoAction);//turn off cascade,"by hand"
});
//comments: modifing the auto-generated migration, "harcode by hand" this attibute, as view bellow: change "Cascade" to "NoAction" (as view 1st and 2st examples)

May be It helps:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
ChangeTracker.CascadeDeleteTiming = ChangeTracking.CascadeTiming.Never;
ChangeTracker.DeleteOrphansTiming = ChangeTracking.CascadeTiming.Never;
}

Related

Setting IsUnicode to false for all types in Entity Framework Core 6

In normal Entity Framework you could use the code
modelBuilder.Properties<string>().Configure(c => c.IsUnicode(false));
to set all created column times to a non-unicode version. I am working in EF Core 6 and trying to do this but modelBuilder does not have a Properties attribute and I am not finding any solutions that parting to EFCore 6 when searching around Has doing this universially been droped and I must specify it for every column now or is there a hidden method some where I am not seeing?
You can set all string properties to be non-unicode by default via pre-convention model configuration:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.DefaultTypeMapping<string>(
b => b.HasColumnType("varchar(max)").IsUnicode(false));
}
Alternative:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var property in modelBuilder.Model.GetEntityTypes()
.SelectMany(e => e.GetProperties()
.Where(p => p.ClrType == typeof(string))))
{
property.SetIsUnicode(false);
}
}

How to user entity framework Map() method in Entity Framework Core 6

How to user Map method in Entity Framework Core 6. After upgrading to Entity Framework Core 6 the Map() method no longer works. Is there something similar I can use to Map the columns to a table?
Example of my code below.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
modelBuilder.Entity<RoleGroup>()
.HasMany(c => c.Roles).WithMany(i => i.RoleGroups).Map(t => t.MapLeftKey("RoleGroupId")
.MapRightKey("RoleId")
.ToTable("RoleGroupRole"));
}
Most examples for EF Core have a RoleGroupRole entity defined, but they do support using a Dictionary<string, object> for a shadow entity placeholder for basic joining tables:
modelBuilder.Entity<RoleGroup>
.HasMany(u => u.Roles)
.WithMany(g => g.RoleGroups)
.UsingEntity<Dictionary<string, object>>(
right => right
.HasOne<Role>()
.WithMany(),
left => left
.HasOne<RoleGroup>()
.WithMany(),
join => join
.ToTable("RoleGroupRoles"));
The gotcha with this configuration is that the expressions for the two sides goes "Right" then "Left", it's easy to get them backwards. :)

cascading foreign key 'X' cannot be created where the referencing column 'Y' is an identity column

I'm trying to upgrade the OwnedTypes sample to EF Core 3.0 Preview 8 but when I run the project it can't create the database.
When Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureCreated() is called the following exception is thrown:
Microsoft.Data.SqlClient.SqlException: 'Cascading foreign key 'FK_OrderDetails_DetailedOrders_OrderId' cannot be created where the referencing column 'OrderDetails.OrderId' is an identity column.
Could not create constraint or index. See previous errors.'
What previous errors? 🤔
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
#region OwnsOneNested
modelBuilder.Entity<DetailedOrder>().OwnsOne(p => p.OrderDetails, od =>
{
od.OwnsOne(c => c.BillingAddress);
od.OwnsOne(c => c.ShippingAddress);
});
#endregion
#region OwnsOneTable
modelBuilder.Entity<DetailedOrder>().OwnsOne(p => p.OrderDetails, od =>
{
od.OwnsOne(c => c.BillingAddress);
od.OwnsOne(c => c.ShippingAddress, sa =>
{
sa.Ignore(p => p.IgnoreMe);
});
od.ToTable("OrderDetails");
});
#endregion
}
Full project
I assume there is something in the 3.0 breaking changes list I need to follow?
You can remove od.ToTable("OrderDetails"); to map OrderDetails to the same table as DetailedOrder
You can add od.Property("OrderId").ValueGeneratedNever(); to disable marking the OrderId column as identity

Can I add a primary key and a unique constraint in EFCore

I'm manually moving from .NET Framework to .NET Core and working on the EF and DTOs.
I'm not enjoying this - I have read indexes are not supported via annotation and as such, am currently mixing fluent api and annotations which is a code smell. However, it appears as if I must proceed with this combination.
My question is if I can achieve this only with fluent api. My table has both a primary key and a unique constraints.
My object looks like
class Person
{
public int Id {get;set;}
public string UniqueToken {get;set;}
}
However, I am unable to add the following
modelBuilder.Entity<Person>()
.HasKey(a => a.Id)
.HasIndex(a => a.UniqueToken).IsUnique(); //this is what I would like to add but I can't.
I've attempted something which feels like a hacky work around
modelBuilder.Entity<Person>()
.HasKey(a => a.Id);
modelBuilder.Entity<Person>()
.HasIndex(a => a.UniqueToken).IsUnique();
Again, adding this entry twice seems a little bleugh… Fluent appears to want to simply chain the methods.
I have read on this, but I'm getting very lost. Is it possible to add both the primary key and unique constraint ?
Better you separate your entity configurations from OnModelCreating method with IEntityTypeConfiguration interface as follows:
public class PersonConfiguration : IEntityTypeConfiguration<Person>
{
public void Configure(EntityTypeBuilder<Person> builder)
{
builder.HasKey(a => a.Id);
builder.HasIndex(a => a.UniqueToken).IsUnique();
}
}
Now you can add configuration for all of your Db entities like PersonConfiguration and register all of them at once as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(PersonConfiguration).Assembly);
}
This will give you more separation of concern and readability!

Entity Framework WebAPI Model UpSerts

I'm having problems with duplicate data during migration with Code First.
A new foreign key record is duplicated each time the migration creates the master record.
The schemas in the database are being created correctly. Namely the Primary Keys and Foreign Key values (the latter being automatically generated)
Can someone please advise thanks about how I detach the foreign key record during migration to prevent it recreating the record or any configuration I need to implement? I've tried updating the state of the foreign key obects before inserting master data. to both modified and detached.
For example I see multi records for the same priority where there should only be 3.
I'm using Entity Framework 6.0.
public class VeloPointDbConfiguration : DbMigrationsConfiguration<VeloPointDbContext>
{
public VeloPointDbConfiguration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(VeloPointDbContext context)
{
context.TaskPriorities.AddOrUpdate(EventTaskPriority.Migrations.All());
context.TaskStatuses.AddOrUpdate(TaskStatus.Migrations.All());
EventOrganiserTask.Migrations.All().Select(x => context.Entry(x.Priority).State == EntityState.Modified);
EventOrganiserTask.Migrations.All().Select(x => context.Entry(x.TaskStatus).State == EntityState.Modified);
context.Tasks.AddOrUpdate(EventOrganiserTask.Migrations.All());
}
}
The following examples of the instances i'm using for the data.
I create the following methods for the foreign key objects
public static EventTaskType[] All()
{
return new[]
{
GetDeadline(),
GetEmail(),
GetTelephone(),
GetAppointment(),
GetSnailMail(),
};
}
internal static EventTaskType GetDeadline()
{
return new EventTaskType("09974722-D03E-4CA3-BF3A-0AF7F6CA1B67", 1, "Deadline")
{
Icon = ""
}
}
I call the following methods the create the master data.
public static EventOrganiserTask[] All()
{
return new EventOrganiserTask[]
{
GetBookHQ(1, new DateTime(Event.Migrations.EventDate.Year - 1, 10, 1)),
GetFindSponsor(2, new DateTime(Event.Migrations.EventDate.Year - 1, 10, 1)),
GetRegisterEvent(3, new DateTime(Event.Migrations.EventDate.Year - 1, 10, 1)),
GetBookFirstAid(4, Event.Migrations.EventDate.AddMonths(-6))
};
}
NOTE: When creating the master record, I call the method in the foreign key classes each time - which is the crux of the problem where I need to instruct the migration to detach this item.
public static EventOrganiserTask GetRegisterEvent(int id, DateTime date)
{
return new EventOrganiserTask
{
id = id,
Title = "Register event",
Summary = "Register the road race with the region",
DueDate = date,
Priority = EventTaskPriority.Migrations.GetHighPriority(),
Person = Person.Migrations.GetRaceOrganiser(1),
TaskType = EventTaskType.Migrations.GetDefault(),
TaskStatus = TaskStatus.Migrations.GetDefault(),
};
}
NOTE: When I do make changes to the data from the application, the foreign keys are not being updated. This must be related and indicates my entities are not configured correctly.
LATEST:
I'm still tearing my hair out. I've investigated this further and read about the migrations being multi threaded (It was another thread on stackoverflow but I can't find it again). Indeed running the Seed method I supposed is what it says on the tin and is purley for seeding data, so the data is only being added (regardless of AddOrUpdate - what's that all about then) So I've looked at the behaviour regarding the records being created. First of all I called context.SaveChanges() after creating the look up tables. At this point it doesn't created any duplicates as the items are only referenced once. I then let the seed method run the master data - but argggh - I see duplicates (when the instances are called on the master data). But this did flag something up with regard to the order in which it creates the records.
My next step was to create two migrations, but without any success.
I'm hoping somebody picks up this thread soon. I'm tearing my hair out.
Ok so i've finally found my answer. It was clever enough to create the foreign key relationships from the model, but i needed to be explicitly set the foreign key id field. I chose the Fluent API to explicitly set the relationships and I set the value of the id field in the mapping of the object.
modelBuilder.Entity<Task>()
.HasRequired(x => x.Priority)
.WithMany(x => x.Tasks)
.HasForeignKey(x => x.Priority_id);
Here it is in the seed method
public class VeloPointDbConfiguration : DbMigrationsConfiguration<VeloPointDbContext>
{
public VeloPointDbConfiguration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(VeloPointDbContext context)
{
context.TaskPriorities.AddOrUpdate(EventTaskPriority.Migrations.All());
context.TaskStatuses.AddOrUpdate(TaskStatus.Migrations.All());
EventOrganiserTask.Migrations.All().Select(x => context.Entry(x.Priority).State == EntityState.Modified);
EventOrganiserTask.Migrations.All().Select(x => context.Entry(x.TaskStatus).State == EntityState.Modified);
context.Tasks.AddOrUpdate(EventOrganiserTask.Migrations.All());
// Foreign Key relationships
modelBuilder.Entity<EventOrganiserTask>()
.HasRequired(x => x.TaskStatus)
.WithMany(x => x.Tasks)
.HasForeignKey(x => x.TaskStatus_id);
modelBuilder.Entity<Task>()
.HasRequired(x => x.TaskType)
.WithMany(x => x.Tasks)
.HasForeignKey(x => x.TaskType_id);
modelBuilder.Entity<Task>()
.HasRequired(x => x.Priority)
.WithMany(x => x.Tasks)
.HasForeignKey(x => x.Priority_id);
}
}