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

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

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).

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

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.

EF CodeFirst Mapping Two Cascade Delete Relationships

I have some issues getting cascading to work on my optional one-to-one and one-to-many relation. Enabling 1 of them works fine but enabling both results in a 'possible circular cascading exception'.
I have a 'Customer' which has multiple 'DeliverAddresses' and one 'VisitAddress'. So for address I have an optional DeliverAddressForCustomer and an optional VisitAddressForCustomer.
this results in the following tables:
CREATE TABLE [dbo].[Customer]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY
[Name] NVARCHAR(50) NOT NULL,
)
CREATE TABLE [dbo].[Address]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY,
[Street] NVARCHAR(50) NOT NULL,
[DeliverAddressForCustomerId] INT NULL,
[VisitAddressForCustomer] INT NULL,
CONSTRAINT [FK_Address_Customer_DeliverAddressForCustomerId] FOREIGN KEY ([DeliverAddressForCustomerId]) REFERENCES [Customer]([Id]),
CONSTRAINT [FK_Address_Customer_VisitAddressForCustomerId] FOREIGN KEY ([VisitAddressForCustomerId]) REFERENCES [Customer]([Id])
)
This works with the following mapping:
this.ToTable("Address");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Postcode).HasColumnName("Postcode");
this.Property(t => t.DeliverAddressForCustomerId).HasColumnName("DeliverAddressForCustomerId");
// Relationships
this.HasOptional(t => t.DeliverAddressForCustomer)
.WithMany(t => t.DeliverAddresses)
.HasForeignKey(d => d.DeliverAddressForCustomerId)
.WillCascadeOnDelete(true);
this.HasOptional(a => a.VisitAddressForCustomer)
.WithOptionalDependent(k => k.VisitAddress)
.Map(x => x.MapKey("VisitAddressForCustomerId"))
.WillCascadeOnDelete(true);
Now if I have both 'WillCascadeOnDelete' set to true, it will work with an existing DB but not if I want to create the DB... I get the following exception message:
Introducing FOREIGN KEY constraint
'FK_dbo.Address_dbo.Address_Customer_VisitAddressForCustomerId' on table 'Address' may cause
cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON
UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
But this doesn't make sense to me since if I delete a deliveryAddress or a visitAddress, customer should stay untouched in this setting. So there should be no circular cascading.
The message says "may cause cycles or multiple cascade paths". It's the last part that matters. SQL server won't allow more than one FK constraint with cascaded deletes in one table.
By the way, note that cascaded delete does not prescribe what should happen if you delete an Address, but if you delete a Customer.
Also see this: SQL Server 2008 - Multiple Cascading FK's - Do i need a trigger?

How to specify relationship name in code-first many to many relationship

When I create two entities with many to many relationship, it will generate a relationship table in the database, is it possible to specify the table's name?
Yes but you have to use fluent API:
mb.Entity<FirstEntity>()
.HasMany(a => a.SecondEntities)
.WithMany(b => b.FirstEntities)
.Map(mc =>
{
mc.ToTable("YourTableName", "YourDbSchema");
mc.MapLeftKey("FirstEntityKeyColumnName");
mc.MapRightKey("SecondEntityKeyColumnName");
});

Entity Framework CTP5 Code First - Possible to do entity splitting on a non-primary key?

Using EF CTP5, I am trying to do some entity splitting where the entity is constructed from two separate tables. Is it possible to do this splitting if the key on the two tables is not the primary key?
E.g. Id is my primary key on the Note entity. I want to get my CreatedUser details from a separate table but the primary key on this second table corresponds to CreatedUserId in the Note entity.
modelBuilder.Entity<Note>()
.Map(mc =>
{
mc.Properties(n => new
{
n.Id,
n.Title,
n.Detail,
n.CreatedUserId,
n.CreatedDateTime,
n.UpdatedUserId,
n.UpdatedDateTime,
n.Deleted,
n.SourceSystemId,
n.SourceSubSystemId
});
mc.ToTable("Notes");
})
.Map(mc =>
{
mc.Properties(n => new
{
n.CreatedUserId,
n.CreatedUser
});
mc.ToTable("vwUsers");
});
I've seen comments that entity splitting is only possible if the entity primary key exists in both tables?
Thanks in advance.
Yes, all the tables that are being generated in an entity splitting scenario must have the object identifier (e.g. Note.Id) as their primary key. You should consider creating a 1:* association between User and Note entities in this case.