EF Doesn't Delete Records For Fluent API - Many To Many Relationship - entity-framework

I have 2 entities,
News
FileAttachment
I wanted to configure using code-first fluent API so that Each News can have 0,1 or more than 1 attachments.
here is what i'm using right now
public NewsMap()
{
this.ToTable("News"); // Table Name
this.HasKey(m => m.Id); // Primary Key
// Field Definition
this.Property(m => m.Title).HasMaxLength(255).IsRequired();
this.Property(m => m.Body).HasColumnType("Text").IsRequired();
this.Property(m => m.Summary).HasMaxLength(1000).IsRequired();
this.Property(m => m.AuthorId).IsRequired();
this.Property(m => m.CreatedOn).IsRequired();
this.Property(m => m.UpdatedOn).IsRequired();
this.HasMany(m => m.Attachments).WithMany().Map(m => m.MapLeftKey("NewsId").MapRightKey("AttachmentId"));
}
public class FileAttachmentMap : EntityTypeConfiguration<FileAttachment>
{
public FileAttachmentMap()
{
this.ToTable("FileAttachments"); // Table Name
this.HasKey(m => m.Id); // Primary Key
// Field Definition
this.Property(m => m.DisplayName).HasMaxLength(256).IsRequired();
this.Property(m => m.PhysicalFileName).HasMaxLength(256).IsRequired();
this.Property(m => m.Extension).HasMaxLength(50).IsRequired();
this.Property(m => m.IsImage).IsRequired();
this.Property(m => m.ThumbTiny).HasMaxLength(275).IsOptional();
this.Property(m => m.ThumbSmall).HasMaxLength(275).IsOptional();
this.Property(m => m.ThumbMid).HasMaxLength(275).IsOptional();
this.Property(m => m.ByteSize).IsRequired();
this.Property(m => m.StorageType).IsRequired();
this.Property(m => m.CreatedOn).IsRequired();
this.Property(m => m.UpdatedOn).IsRequired();
}
}
This mapping correctly generates an intermediate table named NewsFileAttachment with two fields :
NewsId
AttachmentId
On News Entity when i call News.Attachments.Add(Attachment); it correctly adds records in both Attachment & NewsAttachment tables.
When i remove some list item from News.Attachments it correctly removes record from NewsAttachment table, but it doesn't delete record in FileAttachment table. I wanted to remove that too.
Can someone please suggest a better Fluent API configuration to achieve this?
Thanks,
Amit
EDIT
In my case FileAttachment stores files for various purpose. i've Blog entity that too have attachments. So, two intermediate tables BlogAttachments & FileAttachments. Now if i use WithOptional as (I can't use WithRequired as i need BlogId & NewsId both in FileAttachment table), i can get rid off intermediate table, but still delete doesn't delete record from FileAttachment table, it just make NewsId/BlogId NULL.
Any suggestion? Main thing is I do not wanted to create separate tables with all the fields i have in FileAttachment table.

That's expected - as it creates many-to-many and extra table - the cascade only applies to that table.
There is no direct 'FK' relationship in between your News and
Attachment, as it goes through a join table. And thus you cannot expect for e.g. attachment to be deleted, if the news does - as attachment could have other news relating to it.
See also this one - it's somewhat relevant.
One to Many Relationship with Join Table using EF Code First
i.e. if your structure permits don't explicitly create many-to-many (don't put collection on both sides, or similar in fluent config).
In your case providing your 'attachments' are not reusable in between News - then just put a collection navigation property in the News - and leave attachment w/o any - or make a 'FK', single instance navigation from Attachment (like a 'Parent') if you need it.
On the other side, if an attach... could be parented by different
news records - then you shouldn't have cascade delete anyways.
note: check your generated migration script - or SQL/Db - to see exactly what it creates - and make sure there is no intermediate table created - and only one 'FK' going from 'attachment' to 'news'.
edit:
modelBuilder.Entity<News>()
.HasMany(c => c.Attachments)
.WithOptional() // or WithRequired (test to see which is better for you)
.WillCascadeOnDelete(true);
...and make one public ICollection<FileAttachment> Attachments {get;set;} in the News.
(actually the collection property is all you need - but configuration is to be safe you get what you want)
That'd make you 1-to-many (or many-to-one), which is the nature of your data (as you said in comments) - and you can have cascade deletes.

Related

Entity Framework Core Nullable FK not getting records

I have an OrderHeader table and OrderLines table. I also have a Rate table. Both OrderHeader and OrderLines can have rates. So this Rate table is setup as:
RateId
RateValue
OrderHeaderId
OrderLineId
So if we're adding header rates the OrderHeaderId is filled in and OrderLineId is null. When adding line rates the opposite happens.
My OnModelCreating() has:
modelBuilder.Entity<Rate>(entity =>
{
entity.HasOne(d => d.OrderHeader)
.WithMany(p => p.Rates)
.HasForeignKey(d => d.OrderHeaderId)
.HasContraintName("FK1")
.IsRequired(false);
entity.HasOne(d => d.OrderLines)
.WithMany(p => p.Rates)
.HasForeignKey(d => d. OrderLineId)
.HasContraintName("FK2")
.IsRequired(false);
}
When I query for a OrderLine record the Rates has 0 records in it, but in the DB there are 2 records for this OrderLineId.
I'm not really sure what else I should be looking for. I thought the .IsRequired(false) would have fixed the issue since in Rates table those FK's can have null values but it didn't.
Related rows are only loaded if you request them, otherwise querying a single entity instance might load your whole database into memory. Read EF Core Loading Related Data
Also the FK between Rate and OrderLines should be on (OrderHeaderId,OrderLineId) to ensure that the Rate's OrderLines all belong to the Rate's OrderHeader. And for similar reasons the primary key of OrderLines should also be (OrderHeaderId,OrderLineId).

Entity Framework Core: Cannot update identity column 'Id'

I found related question but my issue seems to be different.
Running the following code:
var dbitem = context.MyDatabaseItems.Single(p => p.Id == someId);
context.Update(dbitem);
context.SaveChanges();
Results in "Cannot update identity column 'Id'". Table behind is a bit special. "Id" is NOT the primary key for different reasons. Primary key consists of combination of other fields. No matter what I do: detaching, reattaching etc etc the existing item I am unable to save the entity even if I do not change it (see the code).
However this Id is unique and auto generated.
The builder is the following:
builder.Property(p => p.Id)
.ValueGeneratedOnAdd();
builder.HasKey(p => new { p.BusinessDay, p.ClientId, p.Version });
BusinessDay is dateTime, CLientId and Version are integers.
What is going on here?
There are two metadata properties which control the update behavior called BeforeSaveBehavior and AfterSaveBehavior.
For auto generated keys the later is assumed to be Ignore, i.e. never update. For non key auto generated properties it must be configured explicitly (note that there is no fluent API for that so far, so you have to use the metadata API directly), e.g.
// First define the new key
builder.HasKey(p => new { p.BusinessDay, p.ClientId, p.Version });
// Then configure the auto generated column
// This (especially the `SetAfterUpdateBehavior` call) must be after
// unassociating the property as a PK, otherwise you'll get an exception
builder.Property(p => p.Id)
.ValueGeneratedOnAdd()
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); // <--
This does not change the database schema (model), hence no migration is needed. Just the EF Core update entity behavior.

Laravel Backpack: Show a Many to Many relationship in Show Operation

Couldn't find this in the docs.
Is there any standard way, without creating a custom widget, or overriding the view template, to show a Many to Many relationships in a CRUD's showOperation in Backpack for Laravel? If the answer is NO, what would be your approach to implement it?
Let's say I have a Course Model, and a User model, and there is a Many to Many between both
class Course extends Model
{
public function students()
{
return $this->belongsToMany(User::class, 'course_students');
}
}
class User extends Model
{
public function courses()
{
return $this->belongsToMany(Course::class, 'course_students');
}
}
In the Show Operation for the Course. How do I show a Table with all students?
Indeed, you can use the relationship column for this
Excerpt:
Output the related entries, no matter the relationship:
1-n relationships - outputs the name of its one connected entity;
n-n relationships - enumerates the names of all its connected entities;
Its name and definition is the same as for the relationship field
type:
[
// any type of relationship
'name' => 'tags', // name of relationship method in the model
'type' => 'relationship',
'label' => 'Tags', // Table column heading
// OPTIONAL
// 'entity' => 'tags', // the method that defines the relationship in your Model
// 'attribute' => 'name', // foreign key attribute that is shown to user
// 'model' => App\Models\Category::class, // foreign key model
],
Backpack tries to guess which attribute to show for the related item.
Something that the end-user will recognize as unique. If it's
something common like "name" or "title" it will guess it. If not, you
can manually specify the attribute inside the column definition, or
you can add public $identifiableAttribute = 'column_name'; to your
model, and Backpack will use that column as the one the user finds
identifiable. It will use it here, and it will use it everywhere you
haven't explicitly asked for a different attribute.

How to declare cascade delete in EF when child table isn't mapped as separate entity

I have got a database first approach with EF5 and here is a fragment of mappings:
internal class xxxMapping : EntityTypeConfiguration<Order>
{
public xxxMapping ()
{
ToTable("my_table");
//......
HasMany(it => it.Documents)
.WithMany()
.Map(
m =>
{
m.ToTable("dependent_table");
m.MapLeftKey("left_key_id");
m.MapRightKey("right_key_id");
});
}
What is the best way to declare, using fluent API, that when some row is deleted from my_table, then dependent rows from dependent_table will be deleted too (Cascade delete option in FK)
UPD It seems to be working without any additional code (Of course - if foreign key in table is configured properly). But i'm not sure it's a good practice to do so

Need proper syntax using EF6 Fluent API between 2 tables using PK and FK

If you all need more details I will send them for sure. Here is a screen shot containing 2 tables:
Basically this is what looks like in SQL today:
I am trying to use the fluent API for the relationship but not sure how to do it.
Table 1 has a PK and a FK.
Table 2 does not have a PK, only FK.
Below is an example of what I need but this code applies to a different set of tables. I am trying to get the "relationship" syntax correct for the scenario described here:
this.ToTable("Server");
//primary key
this.HasKey(t => t.serverId);
//properties
...
//relationships
this.HasMany(n => n.NetworkAdapters)
.WithRequired(s => s.Server)
.HasForeignKey(s => s.serverId)
.WillCascadeOnDelete(true);
Thank you
Without the primary key on table 2 you are most likely looking for a One-to–Zero-or-One relationship. You might possibly be requiring both ends which I will describe too.
One-to–Zero-or-One
// Configure the primary key for Table1
modelBuilder.Entity<Table1>()
.HasKey(t => t.networkAdapterId);
// Map one-to-zero or one relationship
modelBuilder.Entity<Table2>()
.HasRequired(t => t.Table1)
.WithOptional(t => t.Table2);
One-to-One
// Configure the primary key for the Table1
modelBuilder.Entity<Table1>()
.HasKey(t => t.networkAdapterId);
// Map one-to-one relationship
modelBuilder.Entity<Table2>()
.HasRequired(t => t.Table1)
.WithRequiredPrincipal(t => t.Table2);
See here for more details